diff options
-rw-r--r-- | apps/Notes/Notes.module.scss | 47 | ||||
-rw-r--r-- | apps/Notes/components/Actions.js | 2 | ||||
-rw-r--r-- | apps/Notes/components/List.js | 7 | ||||
-rw-r--r-- | apps/Notes/components/ListItem.js | 11 | ||||
-rw-r--r-- | apps/Notes/components/NoteView.js | 9 | ||||
-rw-r--r-- | apps/Notes/components/Splash.js | 13 | ||||
-rw-r--r-- | apps/Notes/helpers/noteActions.js | 15 | ||||
-rw-r--r-- | components/App.js | 2 |
8 files changed, 89 insertions, 17 deletions
diff --git a/apps/Notes/Notes.module.scss b/apps/Notes/Notes.module.scss index e91dab9..babffa2 100644 --- a/apps/Notes/Notes.module.scss +++ b/apps/Notes/Notes.module.scss @@ -57,9 +57,8 @@ flex-grow: 1; } - & > span:nth-child(2), - & > span:nth-child(3) { - margin-left: .5em; + & > span:nth-child(n+2){ + margin-left: .25em; padding: .15em .5em; line-height: 1em; border-radius: 50%; @@ -76,8 +75,7 @@ border-radius: .5em; cursor: pointer; - & > td:first-of-type > span:nth-child(2), - & > td:first-of-type > span:nth-child(3) { + & > td:first-of-type > span:nth-child(n+2){ color: #666; visibility: visible; opacity: 1; @@ -91,6 +89,11 @@ } & > td:first-of-type > span:nth-child(3):hover { + background-color: #ffffde; + color: #333; + } + + & > td:first-of-type > span:nth-child(4):hover { background-color: #ffdede; color: #333; } @@ -229,3 +232,37 @@ } } +.loader, +.connection { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + flex-direction: column; + + @keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + svg { + font-size: 600%; + color: #ccc; + } + + p { + padding-top: 1em; + color: #ccc; + font-weight: 600; + } +} + +.loader { + svg { + animation: rotating 1s linear infinite; + } +} diff --git a/apps/Notes/components/Actions.js b/apps/Notes/components/Actions.js index 7f60ffa..8f08103 100644 --- a/apps/Notes/components/Actions.js +++ b/apps/Notes/components/Actions.js @@ -26,7 +26,7 @@ const Actions = ({ fetchedNote={fetchedNote} /> ) - case 'import': return ( + case 'importNote': return ( <Import action={action} setAction={setAction} diff --git a/apps/Notes/components/List.js b/apps/Notes/components/List.js index b9fde02..e4060d5 100644 --- a/apps/Notes/components/List.js +++ b/apps/Notes/components/List.js @@ -6,6 +6,7 @@ import useSort from '../hooks/useSort' import {Layout} from 'components' import ListItem from './ListItem' import Actions from './Actions' +import Splash from './Splash' const List = () => { const [fetchedNote, setFetchedNote] = useState() @@ -17,10 +18,10 @@ const List = () => { redirectToVerify: true, }) - if (error) return <p>Failed to fetch notes</p> + if (error) return <Splash type='connection' /> if (!user || !user.isLoggedIn || !user.isVerified || !notes || !sortFn) { - return <p>Loading...</p> + return <Splash /> } return ( @@ -30,7 +31,7 @@ const List = () => { <> <div className='window__submenu'> <div onClick={() => setAction('addNote')}>New note</div> - <div onClick={() => setAction('import')}>Import</div> + <div onClick={() => setAction('importNote')}>Import</div> <div onClick={() => {}}>Export</div> </div> <table className={styles.notesList}> diff --git a/apps/Notes/components/ListItem.js b/apps/Notes/components/ListItem.js index 5d3b91f..47dea3b 100644 --- a/apps/Notes/components/ListItem.js +++ b/apps/Notes/components/ListItem.js @@ -1,10 +1,10 @@ import styles from '../Notes.module.scss' import React, { useContext } from 'react' import fetchJson from 'lib/fetchJson' -import {getNote, removeNote} from '../helpers/noteActions.js' +import {getNote, exportNote, removeNote} from '../helpers/noteActions.js' import useNotes from '../hooks/useNotes' import Context from 'context'; -import { faEdit, faTrash } from '@fortawesome/free-solid-svg-icons' +import { faEdit, faDownload, faTrash } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' const datestring = date => { @@ -34,7 +34,12 @@ const ListItem = ({note, setAction, setFetchedNote}) => { <FontAwesomeIcon icon={faEdit} /> </span> <span - onClick={(e) => removeNote(e, note._id, mutateNotes, setPopup, setAction)} + onClick={e => {e.stopPropagation(); exportNote(note)}} + > + <FontAwesomeIcon icon={faDownload} /> + </span> + <span + onClick={e => removeNote(e, note._id, mutateNotes, setPopup, setAction)} > <FontAwesomeIcon icon={faTrash} /> </span> diff --git a/apps/Notes/components/NoteView.js b/apps/Notes/components/NoteView.js index 7d93b16..14ec045 100644 --- a/apps/Notes/components/NoteView.js +++ b/apps/Notes/components/NoteView.js @@ -2,7 +2,7 @@ import styles from '../Notes.module.scss' import React, {useContext} from 'react'; import Context from 'context'; import useNotes from '../hooks/useNotes' -import {removeNote} from '../helpers/noteActions.js' +import {removeNote, exportNote} from '../helpers/noteActions.js' import copyToClipboard from '../helpers/copyToClipboard.js' const NoteView = ({fetchedNote, setFetchedNote, setAction}) => { @@ -20,10 +20,11 @@ const NoteView = ({fetchedNote, setFetchedNote, setAction}) => { return ( <section className={styles.noteView}> <div className='window__submenu'> - <div onClick={() => { setFetchedNote(); setAction('') }}>Back</div> + <div onClick={() => {setFetchedNote(); setAction('')}}>Back</div> <div onClick={() => copyToClipboard(content, setPopup)}>Copy</div> - <div onClick={() => { setAction('editNote')}}>Edit</div> - <div onClick={e => { removeNote(e, _id, mutateNotes, setPopup, setAction) }}>Remove</div> + <div onClick={() => {setAction('editNote')}}>Edit</div> + <div onClick={() => exportNote(fetchedNote)}>Export</div> + <div onClick={e => {removeNote(e, _id, mutateNotes, setPopup, setAction)}}>Remove</div> </div> <div className='window__scroll'> diff --git a/apps/Notes/components/Splash.js b/apps/Notes/components/Splash.js new file mode 100644 index 0000000..bc52e1b --- /dev/null +++ b/apps/Notes/components/Splash.js @@ -0,0 +1,13 @@ +import styles from '../Notes.module.scss' +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faBan, faSpinner } from '@fortawesome/free-solid-svg-icons' + +const Splash = ({type}) => ( + <div className={type === 'connection' ? styles.connection : styles.loader}> + <FontAwesomeIcon icon={type === 'connection' ? faBan : faSpinner} /> + <p>{type === 'connection' ? 'No connection' : 'Loading...'}</p> + </div> +) + +export default Splash diff --git a/apps/Notes/helpers/noteActions.js b/apps/Notes/helpers/noteActions.js index c296c97..f90e1c7 100644 --- a/apps/Notes/helpers/noteActions.js +++ b/apps/Notes/helpers/noteActions.js @@ -102,3 +102,18 @@ export const removeNote = (e, _id, mutateNotes, setPopup, setAction) => { }) } +export const exportNote = async note => { + const {title} = note + const {content} = note.content + ? note + : await fetchJson(`/api/note/${note.noteId}`) + + const a = document.createElement('a'); + const file = new Blob([content], {type: 'text/plain'}); + + a.href= URL.createObjectURL(file); + a.download = title.toLowerCase().replaceAll(' ', '-')+'.txt'; + a.click(); + + URL.revokeObjectURL(a.href); +} diff --git a/components/App.js b/components/App.js index d79e8c6..f866211 100644 --- a/components/App.js +++ b/components/App.js @@ -24,7 +24,7 @@ const App = ({children, app, apps, setApps}) => { } style={app.pos.length ? {top: app.pos[1], left: app.pos[0]} : {}} > - <div className='window__title'>Notes</div> + <h2 className='window__title'>Notes</h2> <div className='window__title-buttons'> <span onClick={() => toggleMin('Notes', apps, setApps)}> <FontAwesomeIcon icon={faArrowUp} /> |