diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor.ts b/apps/mobile/app/screens/editor/tiptap/use-editor.ts index 64380351b..2c0db0180 100644 --- a/apps/mobile/app/screens/editor/tiptap/use-editor.ts +++ b/apps/mobile/app/screens/editor/tiptap/use-editor.ts @@ -659,7 +659,7 @@ export const useEditor = ( } if (locked && isEncryptedContent(data)) { - const decryptedContent = await db.vault?.decryptContent(data, noteId); + const decryptedContent = await db.vault?.decryptContent(data); if (!decryptedContent) { useTabStore.getState().updateTab(tabId, { locked: true, diff --git a/apps/web/src/common/export.ts b/apps/web/src/common/export.ts index 3565bd13b..4ef8e3a94 100644 --- a/apps/web/src/common/export.ts +++ b/apps/web/src/common/export.ts @@ -31,6 +31,8 @@ import { } from "@notesnook/common"; import Vault from "./vault"; import { ExportStream } from "../utils/streams/export-stream"; +import { showToast } from "../utils/toast"; +import { confirm } from "./dialog-controller"; export async function exportToPDF( title: string, @@ -79,20 +81,36 @@ export async function exportNotes( format: "pdf" | "md" | "txt" | "html" | "md-frontmatter", notes: FilteredSelector ): Promise { - return await TaskManager.startTask({ + const result = await TaskManager.startTask({ type: "modal", title: "Exporting notes", subtitle: "Please wait while your notes are exported.", action: async (report) => { + const errors: Error[] = []; + const exportStream = new ExportStream(report, (e) => errors.push(e)); await fromAsyncIterator( _exportNotes(notes, { format, unlockVault: Vault.unlockVault }) ) - .pipeThrough(new ExportStream(report)) + .pipeThrough(exportStream) .pipeThrough(createZipStream()) .pipeTo(await createWriteStream("notes.zip")); - return true; + return { + errors, + count: exportStream.progress + }; } }); + confirm({ + title: `Exported ${result.count} notes`, + message: + result.errors.length > 0 + ? `Export completed with ${result.errors.length} errors: + +${result.errors.map((e, i) => `${i + 1}. ${e.message}`).join("\n")}` + : "Export completed with 0 errors.", + positiveButtonText: "Okay" + }); + return true; } const FORMAT_TO_EXT = { @@ -130,7 +148,9 @@ export async function exportNote( unlockVault: Vault.unlockVault }) ) - .pipeThrough(new ExportStream(report)) + .pipeThrough( + new ExportStream(report, (e) => showToast("error", e.message)) + ) .pipeThrough(createZipStream()) .pipeTo( await createWriteStream( diff --git a/apps/web/src/components/editor/index.tsx b/apps/web/src/components/editor/index.tsx index fa833699a..5e74ece2b 100644 --- a/apps/web/src/components/editor/index.tsx +++ b/apps/web/src/components/editor/index.tsx @@ -246,7 +246,7 @@ function EditorView({ if (!item.locked) return editor.updateContent(item.data); const result = await db.vault - .decryptContent(item, item.noteId) + .decryptContent(item) .catch(() => EV.publish(EVENTS.vaultLocked)); if (!result) return; editor.updateContent(result.data); diff --git a/apps/web/src/dialogs/progress-dialog.jsx b/apps/web/src/dialogs/progress-dialog.jsx index 008f98da8..97abb4c95 100644 --- a/apps/web/src/dialogs/progress-dialog.jsx +++ b/apps/web/src/dialogs/progress-dialog.jsx @@ -33,6 +33,7 @@ function ProgressDialog(props) { try { props.onDone(await props.action(setProgress)); } catch (e) { + console.error(e); props.onDone(e); } })(); diff --git a/apps/web/src/utils/streams/export-stream.ts b/apps/web/src/utils/streams/export-stream.ts index 6f7df5fe6..39cce00b6 100644 --- a/apps/web/src/utils/streams/export-stream.ts +++ b/apps/web/src/utils/streams/export-stream.ts @@ -19,7 +19,6 @@ along with this program. If not, see . import { ExportableItem } from "@notesnook/common"; import { db } from "../../common/db"; -import { showToast } from "../toast"; import { ZipFile } from "./zip-stream"; import { lazify } from "../lazify"; @@ -27,12 +26,15 @@ export class ExportStream extends TransformStream< ExportableItem | Error, ZipFile > { - constructor(report: (progress: { text: string; current?: number }) => void) { - let progress = 0; + progress = 0; + constructor( + report: (progress: { text: string; current?: number }) => void, + handleError: (error: Error) => void + ) { super({ - async transform(item, controller) { + transform: async (item, controller) => { if (item instanceof Error) { - showToast("error", item.message); + handleError(item); return; } if (item.type === "attachment") { @@ -56,13 +58,13 @@ export class ExportStream extends TransformStream< if (!stream) return; controller.enqueue({ ...item, data: stream }); report({ - current: progress++, + current: this.progress++, text: `Saving attachment: ${item.path}` }); } else { controller.enqueue(item); report({ - current: progress++, + current: this.progress++, text: `Exporting note: ${item.path}` }); } diff --git a/packages/common/src/utils/export-notes.ts b/packages/common/src/utils/export-notes.ts index dfff4c215..232a39383 100644 --- a/packages/common/src/utils/export-notes.ts +++ b/packages/common/src/utils/export-notes.ts @@ -104,31 +104,37 @@ export async function* exportNotes( continue; } - const content = await exportContent(note, { - unlockVault: options.unlockVault, - format, - attachmentsRoot, - pendingAttachments, - resolveInternalLink: (link) => { - const internalLink = parseInternalLink(link); - if (!internalLink) return link; - const paths = notePathMap.get(internalLink.id); - if (!paths) return link; - // if the internal link is linking within the same note - if (paths === notePaths) return `{{NOTE_PATH:}}`; - return `{{NOTE_PATH:${paths[0]}}}`; - } - }); - if (!content) continue; + try { + const content = await exportContent(note, { + unlockVault: options.unlockVault, + format, + attachmentsRoot, + pendingAttachments, + resolveInternalLink: (link) => { + const internalLink = parseInternalLink(link); + if (!internalLink) return link; + const paths = notePathMap.get(internalLink.id); + if (!paths) return link; + // if the internal link is linking within the same note + if (paths === notePaths) return `{{NOTE_PATH:}}`; + return `{{NOTE_PATH:${paths[0]}}}`; + } + }); + if (!content) continue; - for (const path of notePaths) { - yield { - type: "note", - path, - data: resolvePaths(content, path), - mtime: new Date(note.dateEdited), - ctime: new Date(note.dateCreated) - }; + for (const path of notePaths) { + yield { + type: "note", + path, + data: resolvePaths(content, path), + mtime: new Date(note.dateEdited), + ctime: new Date(note.dateCreated) + }; + } + } catch (e) { + yield new Error( + `Failed to export note "${note.title}": ${(e as Error).message}` + ); } } @@ -225,10 +231,15 @@ export async function exportContent( } const contentItem = rawContent?.locked - ? await database.vault - .decryptContent(rawContent, note.id) - .catch(() => undefined) - : rawContent; + ? await database.vault.decryptContent(rawContent) + : // .catch((e) => { + // console.error(e, note); + // return >{ + // type: "tiptap", + // data: `This note could not be decrypted: ${e}` + // }; + // }) + rawContent; const { data, type } = format === "pdf"