mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
mobile: support vault notes in tabs
This commit is contained in:
committed by
Abdullah Atta
parent
2ce31fc81c
commit
c33c365b62
@@ -23,7 +23,6 @@ import { InteractionManager, View } from "react-native";
|
||||
import Share from "react-native-share";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { db } from "../../../common/database";
|
||||
import { editorController } from "../../../screens/editor/tiptap/utils";
|
||||
import BiometricService from "../../../services/biometrics";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import {
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import {
|
||||
eClearEditor,
|
||||
eCloseActionSheet,
|
||||
eCloseVaultDialog,
|
||||
eOnLoadNote,
|
||||
|
||||
@@ -69,17 +69,6 @@ export const openNote = async (
|
||||
return;
|
||||
}
|
||||
|
||||
if (await db.vaults.itemExists(note)) {
|
||||
openVault({
|
||||
item: note,
|
||||
novault: true,
|
||||
locked: true,
|
||||
goToEditor: true,
|
||||
title: "Open note",
|
||||
description: "Unlock note to open it in editor."
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (isTrash) {
|
||||
if (!note.contentId) return;
|
||||
|
||||
|
||||
@@ -22,18 +22,21 @@ import { View } from "react-native";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
import { useTabStore } from "../../../screens/editor/tiptap/use-tab-store";
|
||||
import { editorController } from "../../../screens/editor/tiptap/utils";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import { eSendEvent, presentSheet } from "../../../services/event-manager";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { eUnlockNote } from "../../../utils/events";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
|
||||
type TabItem = {
|
||||
id: number;
|
||||
noteId?: string;
|
||||
previewTab?: boolean;
|
||||
locked?: boolean;
|
||||
};
|
||||
|
||||
const TabItemComponent = (props: {
|
||||
@@ -58,6 +61,9 @@ const TabItemComponent = (props: {
|
||||
if (!props.isFocused) {
|
||||
useTabStore.getState().focusTab(props.tab.id);
|
||||
props.close?.();
|
||||
if (props.tab.locked) {
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onLongPress={() => {
|
||||
@@ -70,9 +76,11 @@ const TabItemComponent = (props: {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start"
|
||||
justifyContent: "flex-start",
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
{props.tab.locked ? <Icon size={SIZE.md} name="lock" /> : null}
|
||||
<Paragraph
|
||||
color={
|
||||
props.isFocused
|
||||
|
||||
@@ -90,6 +90,7 @@ import {
|
||||
eLoginSessionExpired,
|
||||
eOnLoadNote,
|
||||
eOpenAnnouncementDialog,
|
||||
eUnlockNote,
|
||||
eUserLoggedIn,
|
||||
refreshNotesPage
|
||||
} from "../utils/events";
|
||||
@@ -464,6 +465,27 @@ export const useAppEvents = () => {
|
||||
EV.subscribe(EVENTS.uploadCanceled, (data) => {
|
||||
useAttachmentStore.getState().setUploading(data);
|
||||
}),
|
||||
EV.subscribe(EVENTS.vaultLocked, async () => {
|
||||
// Lock all notes in all tabs...
|
||||
for (const tab of useTabStore.getState().tabs) {
|
||||
const noteId = useTabStore.getState().getTab(tab.id)?.noteId;
|
||||
if (!noteId) continue;
|
||||
const note = await db.notes.note(noteId);
|
||||
if (note?.locked) {
|
||||
useTabStore.getState().updateTab(tab.id, {
|
||||
locked: true
|
||||
});
|
||||
if (
|
||||
tab.id === useTabStore.getState().currentTab &&
|
||||
note.locked &&
|
||||
!editorState().movedAway
|
||||
) {
|
||||
// Show unlock note screen.
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
eSubscribeEvent(eUserLoggedIn, onUserUpdated)
|
||||
];
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../services/event-manager";
|
||||
import { eDBItemUpdate } from "../utils/events";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
|
||||
type ItemTypeKey = {
|
||||
note: Note;
|
||||
@@ -94,7 +95,15 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
}
|
||||
}
|
||||
};
|
||||
if (useSettingStore.getState().isAppLoading) {
|
||||
useSettingStore.subscribe((state) => {
|
||||
if (!state.isAppLoading) {
|
||||
onUpdateItem();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
onUpdateItem();
|
||||
}
|
||||
eSubscribeEvent(eDBItemUpdate, onUpdateItem);
|
||||
return () => {
|
||||
eUnSubscribeEvent(eDBItemUpdate, onUpdateItem);
|
||||
|
||||
@@ -65,7 +65,8 @@ import {
|
||||
eClearEditor,
|
||||
eCloseFullscreenEditor,
|
||||
eOnLoadNote,
|
||||
eOpenFullscreenEditor
|
||||
eOpenFullscreenEditor,
|
||||
eUnlockNote
|
||||
} from "../utils/events";
|
||||
import { editorRef, tabBarRef } from "../utils/global-refs";
|
||||
import { sleep } from "../utils/time";
|
||||
@@ -516,6 +517,12 @@ const onChangeTab = async (obj) => {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
newNote: true
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
useTabStore.getState().getTab(useTabStore.getState().currentTab).locked
|
||||
) {
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (obj.from === 2) {
|
||||
|
||||
@@ -25,16 +25,31 @@ import React, {
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useLayoutEffect,
|
||||
useRef
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { Platform, ViewStyle } from "react-native";
|
||||
import {
|
||||
Dimensions,
|
||||
Keyboard,
|
||||
Platform,
|
||||
ScrollView,
|
||||
TextInput,
|
||||
View,
|
||||
ViewStyle,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import WebView from "react-native-webview";
|
||||
import { ShouldStartLoadRequest } from "react-native-webview/lib/WebViewTypes";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { db } from "../../common/database";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import useKeyboard from "../../hooks/use-keyboard";
|
||||
import { eSubscribeEvent } from "../../services/event-manager";
|
||||
import {
|
||||
ToastManager,
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
openVault
|
||||
} from "../../services/event-manager";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import EditorOverlay from "./loading";
|
||||
@@ -43,7 +58,18 @@ import { EditorProps, useEditorType } from "./tiptap/types";
|
||||
import { useEditor } from "./tiptap/use-editor";
|
||||
import { useEditorEvents } from "./tiptap/use-editor-events";
|
||||
import { useTabStore } from "./tiptap/use-tab-store";
|
||||
import { editorController } from "./tiptap/utils";
|
||||
import { editorController, editorState } from "./tiptap/utils";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { VaultDialog } from "../../components/dialogs/vault";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import Heading from "../../components/ui/typography/heading";
|
||||
import Seperator from "../../components/ui/seperator";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
import Input from "../../components/ui/input";
|
||||
import BiometicService from "../../services/biometrics";
|
||||
import { eOnLoadNote, eUnlockNote } from "../../utils/events";
|
||||
|
||||
const style: ViewStyle = {
|
||||
height: "100%",
|
||||
@@ -163,6 +189,7 @@ const Editor = React.memo(
|
||||
/>
|
||||
<EditorOverlay editorId={editorId || ""} editor={editor} />
|
||||
<ReadonlyButton editor={editor} />
|
||||
<LockOverlay />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -172,6 +199,216 @@ const Editor = React.memo(
|
||||
|
||||
export default Editor;
|
||||
|
||||
const LockOverlay = () => {
|
||||
const tab = useTabStore((state) =>
|
||||
state.tabs.find((t) => t.id === state.currentTab)
|
||||
);
|
||||
const { height } = useWindowDimensions();
|
||||
const [item] = useDBItem(tab?.noteId, "note");
|
||||
const isLocked = item?.locked && tab?.locked;
|
||||
const { colors } = useThemeColors();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const password = useRef<string>();
|
||||
const passInputRef = useRef<TextInput>(null);
|
||||
const [biometryEnrolled, setBiometryEnrolled] = useState(false);
|
||||
const [biometryAvailable, setBiometryAvailable] = useState(false);
|
||||
const [enrollBiometrics, setEnrollBiometrics] = useState(false);
|
||||
|
||||
console.log("Tab locked", item?.locked, tab?.locked);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let biometry = await BiometicService.isBiometryAvailable();
|
||||
let fingerprint = await BiometicService.hasInternetCredentials();
|
||||
setBiometryAvailable(!!biometry);
|
||||
setBiometryEnrolled(!!fingerprint);
|
||||
})();
|
||||
}, [isLocked]);
|
||||
|
||||
const unlockWithBiometrics = async () => {
|
||||
try {
|
||||
if (!item || !tab) return;
|
||||
console.log("Trying to unlock with biometrics...");
|
||||
let credentials = await BiometicService.getCredentials(
|
||||
"Unlock note",
|
||||
"Unlock note to open it in editor. If biometrics are not working, you can enter device pin to unlock vault."
|
||||
);
|
||||
|
||||
if (credentials && credentials?.password) {
|
||||
let note = await db.vault.open(item.id, credentials?.password);
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
useTabStore.getState().updateTab(tab.id, {
|
||||
locked: false
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const unlock = () => {
|
||||
if (
|
||||
isLocked &&
|
||||
biometryAvailable &&
|
||||
biometryEnrolled &&
|
||||
!editorState().movedAway
|
||||
) {
|
||||
unlockWithBiometrics();
|
||||
} else {
|
||||
console.log("Biometrics unavailable.");
|
||||
if (isLocked && !editorState().movedAway) {
|
||||
setTimeout(() => {
|
||||
passInputRef.current?.focus();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sub = eSubscribeEvent(eUnlockNote, unlock);
|
||||
unlock();
|
||||
return () => {
|
||||
sub.unsubscribe();
|
||||
};
|
||||
}, [isLocked, biometryAvailable, biometryEnrolled]);
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (!item || !tab) return;
|
||||
|
||||
if (!password.current || password.current.trim().length === 0) {
|
||||
ToastManager.show({
|
||||
heading: "Password not entered",
|
||||
message: "Enter a password for the vault and try again.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let note = await db.vault.open(item.id, password.current);
|
||||
if (enrollBiometrics) {
|
||||
try {
|
||||
await db.vault.unlock(password.current);
|
||||
await BiometicService.storeCredentials(password.current);
|
||||
eSendEvent("vaultUpdated");
|
||||
ToastManager.show({
|
||||
heading: "Biometric unlocking enabled!",
|
||||
message: "Now you can unlock notes in vault with biometrics.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
} catch (e) {
|
||||
ToastManager.show({
|
||||
heading: "Incorrect password",
|
||||
message:
|
||||
"Please enter the correct vault password to enable biometrics.",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
}
|
||||
}
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note
|
||||
});
|
||||
useTabStore.getState().updateTab(tab.id, {
|
||||
locked: false
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
ToastManager.show({
|
||||
heading: "Incorrect password",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return isLocked ? (
|
||||
<ScrollView
|
||||
style={{
|
||||
width: "100%",
|
||||
height: height,
|
||||
backgroundColor: colors.primary.background,
|
||||
position: "absolute",
|
||||
top: 50 + insets.top,
|
||||
zIndex: 999
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: height
|
||||
}}
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<Heading>{item.title}</Heading>
|
||||
<Paragraph>This note is locked.</Paragraph>
|
||||
<Seperator />
|
||||
<Input
|
||||
fwdRef={passInputRef}
|
||||
autoCapitalize="none"
|
||||
testID={notesnook.ids.dialogs.vault.pwd}
|
||||
onChangeText={(value) => {
|
||||
password.current = value;
|
||||
}}
|
||||
wrapperStyle={{
|
||||
width: 300
|
||||
}}
|
||||
marginBottom={10}
|
||||
onSubmit={() => {
|
||||
onSubmit();
|
||||
}}
|
||||
autoComplete="password"
|
||||
returnKeyLabel="Unlock"
|
||||
returnKeyType={"done"}
|
||||
secureTextEntry
|
||||
placeholder="Password"
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Unlock note"
|
||||
type="accent"
|
||||
onPress={() => {
|
||||
onSubmit();
|
||||
}}
|
||||
/>
|
||||
|
||||
{biometryAvailable && !biometryEnrolled ? (
|
||||
<Button
|
||||
title="Enable biometric unlocking"
|
||||
type={enrollBiometrics ? "accent" : "gray"}
|
||||
onPress={() => {
|
||||
setEnrollBiometrics(!enrollBiometrics);
|
||||
}}
|
||||
style={{
|
||||
marginTop: 10
|
||||
}}
|
||||
icon={
|
||||
enrollBiometrics
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
iconSize={20}
|
||||
/>
|
||||
) : biometryEnrolled && biometryAvailable ? (
|
||||
<IconButton
|
||||
name="fingerprint"
|
||||
type="gray"
|
||||
customStyle={{
|
||||
marginTop: 20
|
||||
}}
|
||||
size={40}
|
||||
onPress={() => {
|
||||
unlockWithBiometrics();
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</ScrollView>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
|
||||
const readonly = useTabStore(
|
||||
(state) => state.tabs.find((t) => t.id === state.currentTab)?.readonly
|
||||
|
||||
@@ -91,6 +91,10 @@ import {
|
||||
// 8. If app is killed, restore the note in background.
|
||||
// 9. During realtimes sync, tabs not focused will be updated so if focused, they have the latest and updated content loaded.
|
||||
|
||||
type NoteWithContent = Note & {
|
||||
content?: NoteContent<false>;
|
||||
};
|
||||
|
||||
export const useEditor = (
|
||||
editorId = "",
|
||||
readonly?: boolean,
|
||||
@@ -216,7 +220,8 @@ export const useEditor = (
|
||||
resetContent && (await commands.clearContent(tabId));
|
||||
resetContent && (await commands.clearTags(tabId));
|
||||
useTabStore.getState().updateTab(tabId, {
|
||||
noteId: undefined
|
||||
noteId: undefined,
|
||||
locked: false
|
||||
});
|
||||
},
|
||||
[commands, editorSessionHistory, postMessage]
|
||||
@@ -407,14 +412,21 @@ export const useEditor = (
|
||||
} else {
|
||||
if (!event.item) return;
|
||||
const item = event.item;
|
||||
const noteIsLocked =
|
||||
event.item.locked && !(event.item as NoteWithContent).content;
|
||||
|
||||
console.log("noteIsLocked", noteIsLocked);
|
||||
|
||||
// If note was already opened in a tab, focus that tab.
|
||||
if (typeof event.tabId !== "number") {
|
||||
if (useTabStore.getState().hasTabForNote(event.item.id)) {
|
||||
const tabId = useTabStore.getState().getTabForNote(event.item.id);
|
||||
if (typeof tabId === "number") {
|
||||
useTabStore.getState().updateTab(tabId, {
|
||||
readonly: event.item.readonly || readonly
|
||||
readonly: event.item.readonly || readonly,
|
||||
locked: noteIsLocked
|
||||
});
|
||||
console.log(noteIsLocked, "focused tab...");
|
||||
useTabStore.getState().focusTab(tabId);
|
||||
}
|
||||
console.log("Note already loaded, focusing the tab");
|
||||
@@ -423,7 +435,8 @@ export const useEditor = (
|
||||
// Otherwise we focus the preview tab or create one to open the note in.
|
||||
useTabStore.getState().focusPreviewTab(event.item.id, {
|
||||
readonly: event.item.readonly || readonly,
|
||||
locked: false
|
||||
locked:
|
||||
event.item.locked && !(event.item as NoteWithContent).content
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -446,7 +459,9 @@ export const useEditor = (
|
||||
state.current.movedAway = false;
|
||||
state.current.currentlyEditing = true;
|
||||
|
||||
if (!noteIsLocked) {
|
||||
await loadContent(item);
|
||||
}
|
||||
|
||||
if (
|
||||
currentNotes.current[item.id] &&
|
||||
|
||||
@@ -89,7 +89,7 @@ export type TabStore = {
|
||||
) => void;
|
||||
removeTab: (index: number) => void;
|
||||
moveTab: (index: number, toIndex: number) => void;
|
||||
newTab: (noteId?: string, previewTab?: boolean) => void;
|
||||
newTab: (options?: Omit<Partial<TabItem>, "id">) => void;
|
||||
focusTab: (id: number) => void;
|
||||
getNoteIdForTab: (id: number) => string | undefined;
|
||||
getTabForNote: (noteId: string) => number | undefined;
|
||||
@@ -148,12 +148,17 @@ export const useTabStore = create<TabStore>(
|
||||
options: Omit<Partial<TabItem>, "id" | "noteId">
|
||||
) => {
|
||||
const index = get().tabs.findIndex((t) => t.previewTab);
|
||||
if (index === -1) return get().newTab(noteId, true);
|
||||
if (index === -1)
|
||||
return get().newTab({
|
||||
noteId,
|
||||
previewTab: true,
|
||||
...options
|
||||
});
|
||||
const tabs = [...get().tabs];
|
||||
tabs[index] = {
|
||||
...tabs[index],
|
||||
previewTab: true,
|
||||
...options,
|
||||
previewTab: true,
|
||||
noteId: noteId
|
||||
};
|
||||
console.log("focus preview", noteId);
|
||||
@@ -183,14 +188,13 @@ export const useTabStore = create<TabStore>(
|
||||
);
|
||||
}
|
||||
},
|
||||
newTab: (noteId?: string, previewTab?: boolean) => {
|
||||
newTab: (options) => {
|
||||
const id = getId(get().tabs.length, get().tabs);
|
||||
const nextTabs = [
|
||||
...get().tabs,
|
||||
{
|
||||
id: id,
|
||||
noteId,
|
||||
previewTab: previewTab
|
||||
...options
|
||||
}
|
||||
];
|
||||
set({
|
||||
|
||||
@@ -167,3 +167,4 @@ export const eOnRefreshSearch = "612";
|
||||
export const eOpenAppLockPasswordDialog = "613";
|
||||
export const eCloseAppLocKPasswordDailog = "614";
|
||||
export const eEditorTabFocused = "613";
|
||||
export const eUnlockNote = "614";
|
||||
|
||||
@@ -34,7 +34,7 @@ mergedConfig.resolver = {
|
||||
resolveRequest: (context, moduleName, platform) => {
|
||||
if (moduleName === "node:crypto") {
|
||||
return {
|
||||
type: 'empty'
|
||||
type:"empty"
|
||||
}
|
||||
}
|
||||
if (moduleName ==='react') {
|
||||
|
||||
429
apps/mobile/package-lock.json
generated
429
apps/mobile/package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"@notesnook/editor": "file:../../packages/editor",
|
||||
"@notesnook/editor-mobile": "file:../../packages/editor-mobile",
|
||||
"@notesnook/logger": "file:../../packages/logger",
|
||||
"@notesnook/theme": "file:../../packages/theme",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0"
|
||||
@@ -32962,6 +32963,10 @@
|
||||
"resolved": "native",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@notesnook/theme": {
|
||||
"resolved": "../../packages/theme",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@notesnook/themes-server": {
|
||||
"resolved": "../../servers/themes",
|
||||
"link": true
|
||||
@@ -78964,6 +78969,430 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@notesnook/theme": {
|
||||
"version": "file:../../packages/theme",
|
||||
"requires": {
|
||||
"@emotion/react": "11.11.1",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
"@trpc/server": "^10.31.0",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"react": "18.2.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"ts-json-schema-generator": "^1.2.0",
|
||||
"zustand": "4.4.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.22.6",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "0.8.8",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/memoize": {
|
||||
"version": "0.7.4",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@styled-system/background": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/border": {
|
||||
"version": "5.1.5",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/color": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/core": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@styled-system/css": {
|
||||
"version": "5.1.5",
|
||||
"dev": true
|
||||
},
|
||||
"@styled-system/flexbox": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/grid": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/layout": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/position": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/shadow": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/should-forward-prop": {
|
||||
"version": "5.1.5",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@emotion/is-prop-valid": "^0.8.1",
|
||||
"@emotion/memoize": "^0.7.1",
|
||||
"styled-system": "^5.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/memoize": {
|
||||
"version": "0.7.5",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@styled-system/space": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/typography": {
|
||||
"version": "5.1.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/variant": {
|
||||
"version": "5.1.5",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2",
|
||||
"@styled-system/css": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"@theme-ui/color": {
|
||||
"version": "0.16.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@theme-ui/css": "^0.16.0",
|
||||
"polished": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@theme-ui/css": {
|
||||
"version": "0.16.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@theme-ui/components": {
|
||||
"version": "0.14.7",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/color": "^5.1.2",
|
||||
"@styled-system/should-forward-prop": "^5.1.2",
|
||||
"@styled-system/space": "^5.1.2",
|
||||
"@theme-ui/css": "0.14.7",
|
||||
"@types/styled-system": "^5.1.13"
|
||||
}
|
||||
},
|
||||
"@theme-ui/core": {
|
||||
"version": "0.14.7",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@theme-ui/css": "0.14.7",
|
||||
"@theme-ui/parse-props": "0.14.7",
|
||||
"deepmerge": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"@theme-ui/css": {
|
||||
"version": "0.14.7",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"@theme-ui/parse-props": {
|
||||
"version": "0.14.7",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@theme-ui/css": "0.14.7"
|
||||
}
|
||||
},
|
||||
"@trpc/server": {
|
||||
"version": "10.38.3",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.12",
|
||||
"dev": true
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.5",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "17.0.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@types/styled-system": {
|
||||
"version": "5.1.16",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@types/tinycolor2": {
|
||||
"version": "1.4.3",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "9.5.0",
|
||||
"dev": true
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.1.1",
|
||||
"dev": true
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"dev": true
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "3.0.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.1",
|
||||
"whatwg-fetch": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.3",
|
||||
"dev": true
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.12",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"polished": {
|
||||
"version": "4.2.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.17.8"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "17.0.2",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"dev": true
|
||||
},
|
||||
"safe-stable-stringify": {
|
||||
"version": "2.4.3",
|
||||
"dev": true
|
||||
},
|
||||
"styled-system": {
|
||||
"version": "5.1.5",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@styled-system/background": "^5.1.2",
|
||||
"@styled-system/border": "^5.1.5",
|
||||
"@styled-system/color": "^5.1.2",
|
||||
"@styled-system/core": "^5.1.2",
|
||||
"@styled-system/flexbox": "^5.1.2",
|
||||
"@styled-system/grid": "^5.1.2",
|
||||
"@styled-system/layout": "^5.1.2",
|
||||
"@styled-system/position": "^5.1.2",
|
||||
"@styled-system/shadow": "^5.1.2",
|
||||
"@styled-system/space": "^5.1.2",
|
||||
"@styled-system/typography": "^5.1.2",
|
||||
"@styled-system/variant": "^5.1.5",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"dev": true
|
||||
},
|
||||
"ts-json-schema-generator": {
|
||||
"version": "1.2.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.11",
|
||||
"commander": "^9.4.1",
|
||||
"glob": "^8.0.3",
|
||||
"json5": "^2.2.1",
|
||||
"normalize-path": "^3.0.0",
|
||||
"safe-stable-stringify": "^2.4.1",
|
||||
"typescript": "~4.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "8.1.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^5.0.1",
|
||||
"once": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "5.1.6",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.5",
|
||||
"dev": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"dev": true
|
||||
},
|
||||
"zustand": {
|
||||
"version": "4.3.8",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@notesnook/themes-server": {
|
||||
"version": "file:../../servers/themes",
|
||||
"requires": {
|
||||
|
||||
22
packages/editor-mobile/package-lock.json
generated
22
packages/editor-mobile/package-lock.json
generated
@@ -4360,7 +4360,7 @@
|
||||
"version": "15.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/q": {
|
||||
"version": "1.5.8",
|
||||
@@ -4384,7 +4384,7 @@
|
||||
"version": "18.2.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz",
|
||||
"integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
@@ -4419,7 +4419,7 @@
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.6",
|
||||
@@ -9715,7 +9715,7 @@
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
@@ -17776,20 +17776,6 @@
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||
|
||||
@@ -49,6 +49,7 @@ import Header from "./header";
|
||||
import StatusBar from "./statusbar";
|
||||
import Tags from "./tags";
|
||||
import Title from "./title";
|
||||
import FingerprintIcon from "mdi-react/FingerprintIcon";
|
||||
|
||||
globalThis.toBlobURL = toBlobURL as typeof globalThis.toBlobURL;
|
||||
|
||||
@@ -322,7 +323,8 @@ const Tiptap = ({ settings }: { settings: Settings }) => {
|
||||
style={{
|
||||
overflowY: "scroll",
|
||||
height: "100%",
|
||||
display: "block"
|
||||
display: "block",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
{settings.noHeader ? null : (
|
||||
@@ -341,14 +343,14 @@ const Tiptap = ({ settings }: { settings: Settings }) => {
|
||||
</>
|
||||
)}
|
||||
|
||||
{controller.loading ? (
|
||||
{controller.loading || tab.locked ? (
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
height: "95%",
|
||||
position: "absolute",
|
||||
zIndex: 999,
|
||||
backgroundColor: "white",
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingRight: 12,
|
||||
paddingLeft: 12,
|
||||
display: "flex",
|
||||
@@ -411,7 +413,7 @@ const Tiptap = ({ settings }: { settings: Settings }) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!layout ? null : (
|
||||
{!layout || tab.locked ? null : (
|
||||
<EmotionEditorToolbarTheme>
|
||||
<Toolbar
|
||||
className="theme-scope-editorToolbar"
|
||||
|
||||
@@ -22,15 +22,16 @@ import { createJSONStorage, persist } from "zustand/middleware";
|
||||
|
||||
globalThis.editorControllers = {};
|
||||
globalThis.editors = {};
|
||||
global.editorTags = {};
|
||||
global.editorTitles = {};
|
||||
global.statusBars = {};
|
||||
globalThis.editorTags = {};
|
||||
globalThis.editorTitles = {};
|
||||
globalThis.statusBars = {};
|
||||
|
||||
export type TabItem = {
|
||||
id: number;
|
||||
noteId?: string;
|
||||
previewTab?: boolean;
|
||||
readonly?: boolean;
|
||||
locked?: boolean;
|
||||
};
|
||||
|
||||
type NoteState = {
|
||||
|
||||
Reference in New Issue
Block a user