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: "_"
});