From 41eb94ee472312d835dfc5dc5d5f8298cd6a8529 Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Mon, 29 Jan 2024 11:01:49 +0500 Subject: [PATCH] mobile: fix exporting locked notes --- .../app/components/sheet-provider/index.js | 1 + .../components/sheets/export-notes/index.js | 6 +- apps/mobile/app/components/ui/sheet/index.js | 9 +- apps/mobile/app/hooks/use-actions.js | 9 -- apps/mobile/app/screens/editor/source.ts | 2 +- apps/mobile/app/services/exporter.js | 105 ++++++++++++++++-- 6 files changed, 105 insertions(+), 27 deletions(-) diff --git a/apps/mobile/app/components/sheet-provider/index.js b/apps/mobile/app/components/sheet-provider/index.js index 121d6e79e..a7f47e33c 100644 --- a/apps/mobile/app/components/sheet-provider/index.js +++ b/apps/mobile/app/components/sheet-provider/index.js @@ -101,6 +101,7 @@ const SheetProvider = ({ context = "global" }) => { setVisible(false); setData(null); }} + keyboardHandlerDisabled={data?.keyboardHandlerDisabled} bottomPadding={!data.noBottomPadding} enableGesturesInScrollView={ typeof data.enableGesturesInScrollView === "undefined" diff --git a/apps/mobile/app/components/sheets/export-notes/index.js b/apps/mobile/app/components/sheets/export-notes/index.js index 01cc9e9a8..90e0e455a 100644 --- a/apps/mobile/app/components/sheets/export-notes/index.js +++ b/apps/mobile/app/components/sheets/export-notes/index.js @@ -43,6 +43,7 @@ import Paragraph from "../../ui/typography/paragraph"; import { eSendEvent } from "../../../services/event-manager"; import { eCloseSheet } from "../../../utils/events"; import { requestInAppReview } from "../../../services/app-review"; +import { Dialog } from "../../dialog"; const ExportNotesSheet = ({ notes, update }) => { const { colors } = useThemeColors(); @@ -157,6 +158,8 @@ const ExportNotesSheet = ({ notes, update }) => { ) : null} + + {!exporting && !complete ? ( actions.map((item) => ( @@ -346,7 +349,8 @@ ExportNotesSheet.present = (notes, allNotes) => { notes={allNotes ? db.notes.all : notes} update={update} /> - ) + ), + keyboardHandlerDisabled: true }); }; diff --git a/apps/mobile/app/components/ui/sheet/index.js b/apps/mobile/app/components/ui/sheet/index.js index ae215405b..9b5f8797d 100644 --- a/apps/mobile/app/components/ui/sheet/index.js +++ b/apps/mobile/app/components/ui/sheet/index.js @@ -37,11 +37,11 @@ const SheetWrapper = ({ onOpen, closeOnTouchBackdrop = true, onHasReachedTop, - keyboardMode, overlay, overlayOpacity = 0.3, enableGesturesInScrollView = false, - bottomPadding = true + bottomPadding = true, + keyboardHandlerDisabled }) => { const localRef = useRef(null); const { colors } = useThemeColors("sheet"); @@ -121,8 +121,9 @@ const SheetWrapper = ({ initialOffsetFromBottom={1} onPositionChanged={onHasReachedTop} closeOnTouchBackdrop={closeOnTouchBackdrop} - keyboardMode={keyboardMode} - keyboardHandlerEnabled={sheetKeyboardHandler} + keyboardHandlerEnabled={ + keyboardHandlerDisabled ? false : sheetKeyboardHandler + } closeOnPressBack={closeOnTouchBackdrop} indicatorColor={colors.secondary.background} onOpen={_onOpen} diff --git a/apps/mobile/app/hooks/use-actions.js b/apps/mobile/app/hooks/use-actions.js index ad0ff8cb6..146f88805 100644 --- a/apps/mobile/app/hooks/use-actions.js +++ b/apps/mobile/app/hooks/use-actions.js @@ -550,15 +550,6 @@ export const useActions = ({ close = () => null, item }) => { } async function exportNote() { - if (item.locked) { - ToastEvent.show({ - heading: "Note is locked", - type: "error", - message: "Locked notes cannot be exported", - context: "local" - }); - return; - } ExportNotesSheet.present([item]); } diff --git a/apps/mobile/app/screens/editor/source.ts b/apps/mobile/app/screens/editor/source.ts index babf67dee..3f343edf2 100644 --- a/apps/mobile/app/screens/editor/source.ts +++ b/apps/mobile/app/screens/editor/source.ts @@ -28,5 +28,5 @@ const EditorMobileSourceUrl = * The url should be something like this: http://192.168.100.126:3000/index.html */ export const EDITOR_URI = __DEV__ - ? "http://192.168.8.107:3000/index.html" + ? EditorMobileSourceUrl : EditorMobileSourceUrl; diff --git a/apps/mobile/app/services/exporter.js b/apps/mobile/app/services/exporter.js index 4b8c67127..a2b0e53b8 100644 --- a/apps/mobile/app/services/exporter.js +++ b/apps/mobile/app/services/exporter.js @@ -25,9 +25,12 @@ import * as ScopedStorage from "react-native-scoped-storage"; import { zip } from "react-native-zip-archive"; import { DatabaseLogger, db } from "../common/database/index"; import Storage from "../common/database/storage"; -import { convertNoteToText } from "../utils/note-to-text"; import { sanitizeFilename } from "@notesnook/common"; +import { presentDialog } from "../components/dialog/functions"; +import { useSettingStore } from "../stores/use-setting-store"; +import BiometicService from "./biometrics"; +import { ToastEvent } from "./event-manager"; const MIMETypes = { txt: "text/plain", @@ -98,8 +101,8 @@ async function save(path, data, fileName, extension) { return uri || path; } -async function makeHtml(note) { - let html = await db.notes.note(note.id).export("html"); +async function makeHtml(note, content) { + let html = await db.notes.note(note.id).export("html", content); html = decode(html, { level: EntityLevel.HTML }); @@ -110,23 +113,25 @@ async function makeHtml(note) { * * @param {"txt" | "pdf" | "md" | "html" | "md-frontmatter"} type */ -async function exportAs(type, note, bulk) { +async function exportAs(type, note, bulk, content) { let data; switch (type) { case "html": { - data = await makeHtml(note); + data = await makeHtml(note, content); } break; case "md": - data = await db.notes.note(note.id).export("md"); + data = await db.notes.note(note.id).export("md", content); break; case "md-frontmatter": - data = await db.notes.note(note.id).export("md-frontmatter"); + data = await db.notes + .note(note.id) + .export("md-frontmatter", content?.data); break; case "pdf": { - let html = await makeHtml(note); + let html = await makeHtml(note, content); let fileName = sanitizeFilename(note.title + Date.now(), { replacement: "_" }); @@ -152,21 +157,84 @@ async function exportAs(type, note, bulk) { } break; case "txt": - data = await convertNoteToText(note); + { + data = await db.notes?.note(note.id).export("txt", content); + } break; } return data; } +async function unlockVault() { + let biometry = await BiometicService.isBiometryAvailable(); + let fingerprint = await BiometicService.hasInternetCredentials("nn_vault"); + if (biometry && fingerprint) { + let credentials = await BiometicService.getCredentials( + "Unlock vault", + "Unlock vault to export locked notes" + ); + if (credentials) { + return db.vault.unlock(credentials.password); + } + } + useSettingStore.getState().setSheetKeyboardHandler(false); + return new Promise((resolve) => { + setImmediate(() => { + presentDialog({ + context: "export-notes", + input: true, + secureTextEntry: true, + positiveText: "Unlock", + title: "Unlock vault", + paragraph: "Some exported notes are locked, Unlock to export them", + inputPlaceholder: "Enter password", + positivePress: async (value) => { + const unlocked = await db.vault.unlock(value); + if (!unlocked) { + ToastEvent.show({ + heading: "Invalid password", + message: "Please enter a valid password", + type: "error", + context: "local" + }); + return false; + } + resolve(unlocked); + useSettingStore.getState().setSheetKeyboardHandler(true); + return true; + }, + onClose: () => { + resolve(false); + useSettingStore.getState().setSheetKeyboardHandler(true); + } + }); + }); + }); +} + /** * * @param {"txt" | "pdf" | "md" | "html" | "md-frontmatter"} type */ async function exportNote(note, type) { + let content; + + if (note.locked) { + try { + let unlocked = await unlockVault(); + if (!unlocked) return null; + const unlockedNote = await db.vault.open(note.id); + content = unlockedNote.content; + } catch (e) { + DatabaseLogger.error(e); + } + } + let path = await getPath(FolderNames[type]); if (!path) return; - let result = await exportAs(type, note); + + let result = await exportAs(type, note, false, content); if (!result) return null; let fileName = sanitizeFilename(note.title + Date.now(), { replacement: "_" @@ -240,8 +308,21 @@ async function bulkExport(notes, type, callback) { for (var i = 0; i < notes.length; i++) { try { let note = notes[i]; - if (note.locked) continue; - let result = await exportAs(type, note, true); + let content; + if (note.locked) { + try { + let unlocked = !db.vault.unlocked ? await unlockVault() : true; + if (!unlocked) { + continue; + } + const unlockedNote = await db.vault.open(note.id); + content = unlockedNote.content; + } catch (e) { + DatabaseLogger.error(e); + continue; + } + } + let result = await exportAs(type, note, true, content); let fileName = sanitizeFilename(note.title, { replacement: "_" });