aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/Notes/Notes.module.scss16
-rw-r--r--apps/Notes/components/Actions.js9
-rw-r--r--apps/Notes/components/Export.js83
-rw-r--r--apps/Notes/components/Import.js2
-rw-r--r--apps/Notes/components/List.js9
-rw-r--r--apps/Notes/components/ListItem.js1
-rw-r--r--apps/Notes/helpers/fileName.js3
-rw-r--r--apps/Notes/helpers/noteActions.js10
-rw-r--r--apps/Notes/hooks/useNotes.js1
-rw-r--r--helpers/saveFile.js12
-rw-r--r--package-lock.json118
-rw-r--r--package.json1
12 files changed, 247 insertions, 18 deletions
diff --git a/apps/Notes/Notes.module.scss b/apps/Notes/Notes.module.scss
index babffa2..2e3c68f 100644
--- a/apps/Notes/Notes.module.scss
+++ b/apps/Notes/Notes.module.scss
@@ -232,6 +232,22 @@
}
}
+.export {
+ padding: 1em;
+
+ p {
+ padding-top: 2em;
+ padding-bottom: 1em;
+ }
+
+ &__select {
+ display: inline-block;
+ border-bottom: 1px dashed #666;
+ padding-bottom: .25em;
+ margin-bottom: .5em;
+ }
+}
+
.loader,
.connection {
display: flex;
diff --git a/apps/Notes/components/Actions.js b/apps/Notes/components/Actions.js
index 8f08103..1f5d680 100644
--- a/apps/Notes/components/Actions.js
+++ b/apps/Notes/components/Actions.js
@@ -1,6 +1,7 @@
import NoteView from './NoteView'
import NoteEdit from './NoteEdit'
import Import from './Import'
+import Export from './Export'
const Actions = ({
action, setAction, fetchedNote, setFetchedNote
@@ -26,9 +27,13 @@ const Actions = ({
fetchedNote={fetchedNote}
/>
)
- case 'importNote': return (
+ case 'importNotes': return (
<Import
- action={action}
+ setAction={setAction}
+ />
+ )
+ case 'exportNotes': return (
+ <Export
setAction={setAction}
/>
)
diff --git a/apps/Notes/components/Export.js b/apps/Notes/components/Export.js
new file mode 100644
index 0000000..f281d1a
--- /dev/null
+++ b/apps/Notes/components/Export.js
@@ -0,0 +1,83 @@
+import styles from '../Notes.module.scss'
+import React, {useState} from 'react'
+import useNotes from '../hooks/useNotes'
+import fetchJson from 'lib/fetchJson'
+import JSZip from 'jszip'
+import saveFile from 'helpers/saveFile'
+import filename from '../helpers/fileName'
+// import {state, color, handleImport, handleChange} from '../helpers/import'
+
+const Export = ({setAction}) => {
+ const {notes} = useNotes()
+ const [ids, setIds] = useState(notes.map(n => n.noteId))
+ // const [files, setFiles] = useState()
+ // const [done, setDone] = useState([])
+
+ const handleSelect = id => {
+ ids.includes(id)
+ ? setIds(ids.filter(i => i !== id))
+ : setIds([...ids, id])
+ }
+
+ const handleSelectAll = e => {
+ if (e.target.checked) {
+ setIds(notes.map(n => n.noteId))
+ } else {
+ setIds([])
+ }
+ }
+
+ const handleExport = e => {
+ e.preventDefault()
+ const zip = new JSZip()
+
+ Promise.all(ids.map(async id => {
+ const title = notes.find(n => n.noteId === id).title
+ const {content} = await fetchJson(`/api/note/${id}`)
+ zip.folder('notes').file(filename(title), content, {binary: true})
+ })).then(() => {
+ zip.generateAsync({type:"blob"})
+ .then(c => saveFile(c, 'notes.zip', 'application/zip'))
+ })
+ }
+
+ if (!notes) return null
+
+ return (
+ <section>
+ <div className='window__submenu'>
+ <div onClick={() => {setAction('')}}>Back</div>
+ </div>
+ <div className={`window__scroll ${styles.export}`}>
+ <h3>Click to export your notes:</h3>
+ <input
+ className="window__button"
+ type="submit"
+ value="Export"
+ onClick={handleExport}
+ />
+ <p>Notes to export:</p>
+ <div className={`${styles.export__select}`}>
+ <input type="checkbox" name='selectAll' onChange={handleSelectAll} checked={notes.length === ids.length} />
+ <label htmlFor='selectAll'>Select all</label>
+ </div>
+ {notes.map(note => (
+ <div key={note.noteId}>
+ <input
+ type="checkbox"
+ name={note.noteId}
+ value={note.noteId}
+ checked={ids.includes(note.noteId)}
+ onChange={() => handleSelect(note.noteId)}
+ />
+ <label htlmfor={note.noteId}>
+ {note.title}
+ </label><br/>
+ </div>
+ ))}
+ </div>
+ </section>
+ )
+}
+
+export default Export
diff --git a/apps/Notes/components/Import.js b/apps/Notes/components/Import.js
index 2e2e5c7..700acb4 100644
--- a/apps/Notes/components/Import.js
+++ b/apps/Notes/components/Import.js
@@ -4,7 +4,7 @@ import fetchJson from 'lib/fetchJson'
import useNotes from '../hooks/useNotes'
import {state, color, handleImport, handleChange} from '../helpers/import'
-const Import = ({action, setAction}) => {
+const Import = ({setAction}) => {
const [files, setFiles] = useState()
const [done, setDone] = useState([])
const {mutateNotes} = useNotes()
diff --git a/apps/Notes/components/List.js b/apps/Notes/components/List.js
index e4060d5..0f6221e 100644
--- a/apps/Notes/components/List.js
+++ b/apps/Notes/components/List.js
@@ -1,9 +1,8 @@
import styles from '../Notes.module.scss'
-import React, {useState, useEffect, useRef} from 'react'
+import React, {useState} from 'react'
import useUser from 'lib/useUser'
import useNotes from '../hooks/useNotes'
import useSort from '../hooks/useSort'
-import {Layout} from 'components'
import ListItem from './ListItem'
import Actions from './Actions'
import Splash from './Splash'
@@ -13,7 +12,7 @@ const List = () => {
const [action, setAction] = useState('')
const {notes, error} = useNotes()
const [sortedBy, sortBy, sortFn] = useSort(2)
- const {user, mutateUser} = useUser({
+ const {user} = useUser({
redirectToLogin: true,
redirectToVerify: true,
})
@@ -31,8 +30,8 @@ const List = () => {
<>
<div className='window__submenu'>
<div onClick={() => setAction('addNote')}>New note</div>
- <div onClick={() => setAction('importNote')}>Import</div>
- <div onClick={() => {}}>Export</div>
+ <div onClick={() => setAction('importNotes')}>Import</div>
+ <div onClick={() => setAction('exportNotes')}>Export</div>
</div>
<table className={styles.notesList}>
<thead>
diff --git a/apps/Notes/components/ListItem.js b/apps/Notes/components/ListItem.js
index 47dea3b..caa22e9 100644
--- a/apps/Notes/components/ListItem.js
+++ b/apps/Notes/components/ListItem.js
@@ -1,6 +1,5 @@
import styles from '../Notes.module.scss'
import React, { useContext } from 'react'
-import fetchJson from 'lib/fetchJson'
import {getNote, exportNote, removeNote} from '../helpers/noteActions.js'
import useNotes from '../hooks/useNotes'
import Context from 'context';
diff --git a/apps/Notes/helpers/fileName.js b/apps/Notes/helpers/fileName.js
new file mode 100644
index 0000000..f5d4a8e
--- /dev/null
+++ b/apps/Notes/helpers/fileName.js
@@ -0,0 +1,3 @@
+const filename = (t) => t.toLowerCase().replaceAll(' ', '-')+'.txt'
+
+export default filename
diff --git a/apps/Notes/helpers/noteActions.js b/apps/Notes/helpers/noteActions.js
index f90e1c7..9588150 100644
--- a/apps/Notes/helpers/noteActions.js
+++ b/apps/Notes/helpers/noteActions.js
@@ -1,4 +1,5 @@
import fetchJson from 'lib/fetchJson'
+import filename from '../helpers/fileName'
export const getNote = async (note, setFetchedNote, setPopup, callback) => {
try {
@@ -108,12 +109,5 @@ export const exportNote = async note => {
? 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);
+ saveFile(content, filename(title), 'text/plain')
}
diff --git a/apps/Notes/hooks/useNotes.js b/apps/Notes/hooks/useNotes.js
index 9d79034..586ceeb 100644
--- a/apps/Notes/hooks/useNotes.js
+++ b/apps/Notes/hooks/useNotes.js
@@ -1,5 +1,4 @@
import useSWR from 'swr'
-import fetchJson from 'lib/fetchJson'
export default function useNotes(){
const { data: notes, error, mutate: mutateNotes } = useSWR('/api/notes')
diff --git a/helpers/saveFile.js b/helpers/saveFile.js
new file mode 100644
index 0000000..83d46cb
--- /dev/null
+++ b/helpers/saveFile.js
@@ -0,0 +1,12 @@
+const saveFile = (content, filename, type) => {
+ const a = document.createElement('a')
+ const file = new Blob([content], {type})
+
+ a.href= URL.createObjectURL(file)
+ a.download = filename
+ a.click()
+
+ URL.revokeObjectURL(a.href)
+}
+
+export default saveFile
diff --git a/package-lock.json b/package-lock.json
index c2a0e1b..1c8cbc9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.15",
"bcryptjs": "^2.4.3",
+ "jszip": "^3.7.1",
"mongoose": "^5.12.13",
"next": "latest",
"next-iron-session": "4.1.7",
@@ -1315,6 +1316,11 @@
}
]
},
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+ },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -1587,11 +1593,57 @@
"json5": "lib/cli.js"
}
},
+ "node_modules/jszip": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+ "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/jszip/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
"node_modules/kareem": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz",
"integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ=="
},
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
"node_modules/loader-utils": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
@@ -2593,6 +2645,14 @@
"semver": "bin/semver.js"
}
},
+ "node_modules/set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -4104,6 +4164,11 @@
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+ },
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -4276,11 +4341,59 @@
"minimist": "^1.2.0"
}
},
+ "jszip": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+ "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
"kareem": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz",
"integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ=="
},
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
"loader-utils": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
@@ -5068,6 +5181,11 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
+ "set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+ },
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
diff --git a/package.json b/package.json
index dcb975e..dfdcbe5 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.15",
"bcryptjs": "^2.4.3",
+ "jszip": "^3.7.1",
"mongoose": "^5.12.13",
"next": "latest",
"next-iron-session": "4.1.7",