mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
mobile: fix migration bugs
This commit is contained in:
@@ -78,6 +78,7 @@ export async function setupDatabase(password) {
|
||||
createQueryCompiler: () => new SqliteQueryCompiler()
|
||||
}),
|
||||
tempStore: "memory",
|
||||
journalMode: Platform.OS === "ios" ? "DELETE" : "WAL",
|
||||
password: key
|
||||
}
|
||||
});
|
||||
|
||||
@@ -106,9 +106,9 @@ class RNSqliteConnection implements DatabaseConnection {
|
||||
: query.kind === "RawNode"
|
||||
? "raw"
|
||||
: "exec";
|
||||
|
||||
const result = await this.db.executeAsync(sql, parameters as any[]);
|
||||
|
||||
// console.log("SQLITE result:", result?.rows?._array);
|
||||
if (mode === "query" || !result.insertId)
|
||||
return {
|
||||
rows: result.rows?._array || []
|
||||
|
||||
@@ -20,12 +20,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
import { Platform, TextInput, View } from "react-native";
|
||||
//@ts-ignore
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { enabled } from "react-native-privacy-snapshot";
|
||||
import { db } from "../../common/database";
|
||||
import { DatabaseLogger } from "../../common/database";
|
||||
import {
|
||||
decrypt,
|
||||
encrypt,
|
||||
getCryptoKey,
|
||||
getDatabaseKey,
|
||||
setAppLockVerificationCipher,
|
||||
validateAppLockPassword
|
||||
} from "../../common/database/encryption";
|
||||
import { MMKV } from "../../common/database/mmkv";
|
||||
import { useAppState } from "../../hooks/use-app-state";
|
||||
import BiometricService from "../../services/biometrics";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { NotesnookModule } from "../../utils/notesnook-module";
|
||||
import { SIZE } from "../../utils/size";
|
||||
@@ -35,11 +45,37 @@ import Input from "../ui/input";
|
||||
import Seperator from "../ui/seperator";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { validateAppLockPassword } from "../../common/database/encryption";
|
||||
|
||||
const getUser = () => {
|
||||
const user = MMKV.getString("user");
|
||||
if (user) {
|
||||
return JSON.parse(user);
|
||||
}
|
||||
};
|
||||
|
||||
const verifyUserPassword = async (password: string) => {
|
||||
try {
|
||||
await getDatabaseKey();
|
||||
const key = await getCryptoKey();
|
||||
const user = getUser();
|
||||
const cipher = await encrypt(
|
||||
{
|
||||
key: key,
|
||||
salt: user.salt
|
||||
},
|
||||
"notesnook"
|
||||
);
|
||||
const plainText = await decrypt({ password }, cipher);
|
||||
return plainText === "notesnook";
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e as Error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const AppLockedOverlay = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const user = useUserStore((state) => state.user);
|
||||
const user = getUser();
|
||||
const appLocked = useUserStore((state) => state.appLocked);
|
||||
const lockApp = useUserStore((state) => state.lockApp);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
@@ -93,9 +129,18 @@ const AppLockedOverlay = () => {
|
||||
try {
|
||||
const unlocked = appLockHasPasswordSecurity
|
||||
? validateAppLockPassword(password.current)
|
||||
: await db.user.verifyPassword(password.current);
|
||||
: await verifyUserPassword(password.current);
|
||||
|
||||
if (unlocked) {
|
||||
if (!appLockHasPasswordSecurity) {
|
||||
await setAppLockVerificationCipher(password.current);
|
||||
SettingsService.set({
|
||||
appLockHasPasswordSecurity: true,
|
||||
applockKeyboardType: "default"
|
||||
});
|
||||
DatabaseLogger.info("App lock migrated to password security");
|
||||
}
|
||||
|
||||
lockApp(false);
|
||||
enabled(false);
|
||||
password.current = undefined;
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
setAppLockVerificationCipher,
|
||||
validateAppLockPassword
|
||||
} from "../../../common/database/encryption";
|
||||
import BiometicService from "../../../services/biometrics";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import {
|
||||
ToastManager,
|
||||
@@ -46,8 +47,6 @@ import { Toast } from "../../toast";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import BiometicService from "../../../services/biometrics";
|
||||
|
||||
export const AppLockPassword = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -322,13 +321,15 @@ export const AppLockPassword = () => {
|
||||
clearAppLockVerificationCipher();
|
||||
SettingsService.setProperty("appLockHasPasswordSecurity", false);
|
||||
|
||||
if (!useUserStore.getState().user) {
|
||||
if (
|
||||
!(await BiometicService.isBiometryAvailable()) ||
|
||||
!SettingsService.getProperty("biometricsAuthEnabled")
|
||||
) {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
}
|
||||
if (
|
||||
!(await BiometicService.isBiometryAvailable()) ||
|
||||
SettingsService.getProperty("biometricsAuthEnabled") === false
|
||||
) {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
ToastManager.show({
|
||||
message: "App lock disabled",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { FlatList } from "react-native-gesture-handler";
|
||||
import { ActivityIndicator, Text, View } from "react-native";
|
||||
|
||||
import { getFormattedDate, getTimeAgo } from "@notesnook/common";
|
||||
import { HistorySession, Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
import { presentSheet } from "../../services/event-manager";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import DialogHeader from "../dialog/dialog-header";
|
||||
@@ -32,22 +37,32 @@ import { PressableButton } from "../ui/pressable";
|
||||
import Seperator from "../ui/seperator";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import NotePreview from "./preview";
|
||||
import { getFormattedDate, getTimeAgo } from "@notesnook/common";
|
||||
|
||||
export default function NoteHistory({ note, fwdRef }) {
|
||||
const [history, setHistory] = useState([]);
|
||||
const [_loading, setLoading] = useState(true);
|
||||
const HistoryItem = ({
|
||||
index,
|
||||
items,
|
||||
note
|
||||
}: {
|
||||
index: number;
|
||||
items?: VirtualizedGrouping<HistorySession>;
|
||||
note?: Note;
|
||||
}) => {
|
||||
const [item] = useDBItem(index, "noteHistory", items);
|
||||
const { colors } = useThemeColors();
|
||||
const getDate = (start: number, end: number) => {
|
||||
const _start_date = getFormattedDate(start, "date");
|
||||
const _end_date = getFormattedDate(end + 60000, "date");
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setHistory([...(await db.noteHistory.get(note.id))]);
|
||||
setLoading(false);
|
||||
})();
|
||||
}, [note.id]);
|
||||
const _start_time = getFormattedDate(start, "time");
|
||||
const _end_time = getFormattedDate(end + 60000, "time");
|
||||
|
||||
const preview = useCallback(async (item) => {
|
||||
let content = await db.noteHistory.content(item.id);
|
||||
return `${_start_date} ${_start_time} - ${
|
||||
_end_date === _start_date ? " " : _end_date + " "
|
||||
}${_end_time}`;
|
||||
};
|
||||
|
||||
const preview = useCallback(async (item: HistorySession) => {
|
||||
const content = await db.noteHistory.content(item.id);
|
||||
presentSheet({
|
||||
component: (
|
||||
<NotePreview
|
||||
@@ -62,23 +77,14 @@ export default function NoteHistory({ note, fwdRef }) {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getDate = (start, end) => {
|
||||
let _start_date = getFormattedDate(start, "date");
|
||||
let _end_date = getFormattedDate(end + 60000, "date");
|
||||
|
||||
let _start_time = getFormattedDate(start, "time");
|
||||
let _end_time = getFormattedDate(end + 60000, "time");
|
||||
|
||||
return `${_start_date} ${_start_time} - ${
|
||||
_end_date === _start_date ? " " : _end_date + " "
|
||||
}${_end_time}`;
|
||||
};
|
||||
|
||||
const renderItem = useCallback(
|
||||
({ item }) => (
|
||||
return (
|
||||
item && (
|
||||
<PressableButton
|
||||
type="grayBg"
|
||||
onPress={() => preview(item)}
|
||||
onPress={() => {
|
||||
if (!item) return;
|
||||
preview(item);
|
||||
}}
|
||||
customStyle={{
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
@@ -93,8 +99,41 @@ export default function NoteHistory({ note, fwdRef }) {
|
||||
{getTimeAgo(item.dateModified)}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default function NoteHistory({
|
||||
note
|
||||
}: {
|
||||
note: Note;
|
||||
fwdRef: ActionSheetRef;
|
||||
}) {
|
||||
const [history, setHistory] = useState<VirtualizedGrouping<HistorySession>>();
|
||||
const [_loading, setLoading] = useState(true);
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
useEffect(() => {
|
||||
db.noteHistory
|
||||
.get(note.id)
|
||||
.sorted({
|
||||
sortBy: "dateModified",
|
||||
sortDirection: "desc"
|
||||
})
|
||||
.then((result) => {
|
||||
setHistory(result);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [note.id]);
|
||||
|
||||
const renderItem = useCallback(
|
||||
({ index }: { index: number }) => (
|
||||
<HistoryItem index={index} items={history} />
|
||||
),
|
||||
[colors.secondary.paragraph, preview]
|
||||
[history]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -108,34 +147,46 @@ export default function NoteHistory({ note, fwdRef }) {
|
||||
|
||||
<Seperator />
|
||||
|
||||
<FlatList
|
||||
onMomentumScrollEnd={() => {
|
||||
fwdRef?.current?.handleChildScrollEnd();
|
||||
}}
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: 12,
|
||||
height: !history?.placeholders.length
|
||||
? 300
|
||||
: (history.placeholders.length + 1) * 55,
|
||||
maxHeight: "100%"
|
||||
}}
|
||||
nestedScrollEnabled
|
||||
keyExtractor={(item) => item.id}
|
||||
data={history}
|
||||
ListFooterComponent={<View style={{ height: 250 }} />}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Icon name="history" size={60} color={colors.primary.icon} />
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
No note history found on this device.
|
||||
</Paragraph>
|
||||
</View>
|
||||
}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
>
|
||||
<FlashList
|
||||
data={history?.placeholders}
|
||||
estimatedItemSize={55}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 300,
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
{_loading ? (
|
||||
<ActivityIndicator
|
||||
size={SIZE.xl}
|
||||
color={colors.primary.accent}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Icon name="history" size={50} color={colors.primary.icon} />
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
No note history found on this device.
|
||||
</Paragraph>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</View>
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
@@ -146,10 +197,7 @@ export default function NoteHistory({ note, fwdRef }) {
|
||||
Note version history is local only.{" "}
|
||||
<Text
|
||||
onPress={() => {
|
||||
openLinkInBrowser(
|
||||
"https://docs.notesnook.com/versionhistory",
|
||||
colors
|
||||
);
|
||||
openLinkInBrowser("https://docs.notesnook.com/versionhistory");
|
||||
}}
|
||||
style={{
|
||||
color: colors.primary.accent,
|
||||
@@ -123,7 +123,7 @@ export default function NotePreview({ session, content, note }) {
|
||||
readonly
|
||||
editorId={editorId}
|
||||
onLoad={async () => {
|
||||
const _note = note || db.notes.note(session?.noteId)?.data;
|
||||
const _note = note || (await db.notes.note(session?.noteId));
|
||||
eSendEvent(eOnLoadNote + editorId, {
|
||||
item: {
|
||||
..._note,
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { eCloseSheet } from "../../../utils/events";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import { Dialog } from "../../dialog";
|
||||
@@ -90,21 +91,27 @@ export default function Migrate() {
|
||||
|
||||
const startMigration = useCallback(async () => {
|
||||
try {
|
||||
useUserStore.setState({
|
||||
disableAppLockRequests: true
|
||||
});
|
||||
setLoading(true);
|
||||
await sleep(1000);
|
||||
await sleep(1);
|
||||
const backupSaved = await BackupService.run(false, "local");
|
||||
if (!backupSaved) {
|
||||
ToastManager.show({
|
||||
heading: "Migration failed",
|
||||
message: "You must download a backup of your data before migrating.",
|
||||
context: "local"
|
||||
context: "local",
|
||||
type: "error"
|
||||
});
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
await db.migrations?.migrate();
|
||||
useUserStore.setState({
|
||||
disableAppLockRequests: false
|
||||
});
|
||||
eSendEvent(eCloseSheet);
|
||||
await sleep(500);
|
||||
setLoading(false);
|
||||
} catch (e) {
|
||||
setLoading(false);
|
||||
|
||||
@@ -35,8 +35,9 @@ import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { initialize } from "../../../stores";
|
||||
import { refreshAllStores } from "../../../stores/create-db-collection-store";
|
||||
import { eCloseRestoreDialog, eOpenRestoreDialog } from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Dialog } from "../../dialog";
|
||||
@@ -47,7 +48,6 @@ import { Button } from "../../ui/button";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import SheetWrapper from "../../ui/sheet";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import Navigation from "../../../services/navigation";
|
||||
|
||||
const RestoreDataSheet = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
@@ -235,10 +235,10 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
};
|
||||
|
||||
const restoreBackup = async (backup, password) => {
|
||||
await db.backup.import(backup, password);
|
||||
|
||||
await db.initCollections();
|
||||
initialize();
|
||||
await db.transaction(async () => {
|
||||
await db.backup.import(backup, password);
|
||||
});
|
||||
refreshAllStores();
|
||||
ToastManager.show({
|
||||
heading: "Backup restored successfully.",
|
||||
type: "success",
|
||||
@@ -280,30 +280,33 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
throw new Error("Backup file is invalid");
|
||||
}
|
||||
|
||||
let password;
|
||||
await db.transaction(async () => {
|
||||
let password;
|
||||
console.log(
|
||||
`Found ${backupFiles?.length} files to restore from backup`
|
||||
);
|
||||
for (const path of backupFiles) {
|
||||
if (path === ".nnbackup") continue;
|
||||
const filePath = `${zipOutputFolder}/${path}`;
|
||||
const data = await RNFetchBlob.fs.readFile(filePath, "utf8");
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
console.log(`Found ${backupFiles?.length} files to restore from backup`);
|
||||
for (const path of backupFiles) {
|
||||
if (path === ".nnbackup") continue;
|
||||
const filePath = `${zipOutputFolder}/${path}`;
|
||||
const data = await RNFetchBlob.fs.readFile(filePath, "utf8");
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
if (parsed.encrypted && !password) {
|
||||
console.log("Backup is encrypted...", "requesting password");
|
||||
password = await withPassword();
|
||||
if (!password) throw new Error("Failed to decrypt backup");
|
||||
if (parsed.encrypted && !password) {
|
||||
console.log("Backup is encrypted...", "requesting password");
|
||||
password = await withPassword();
|
||||
if (!password) throw new Error("Failed to decrypt backup");
|
||||
}
|
||||
await db.backup.import(parsed, password);
|
||||
console.log("Imported", path);
|
||||
}
|
||||
await db.backup.import(parsed, password);
|
||||
console.log("Imported", path);
|
||||
}
|
||||
});
|
||||
// Remove files from cache
|
||||
RNFetchBlob.fs.unlink(zipOutputFolder).catch(console.log);
|
||||
if (remove) {
|
||||
RNFetchBlob.fs.unlink(file).catch(console.log);
|
||||
}
|
||||
|
||||
await db.initCollections();
|
||||
refreshAllStores();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
setRestoring(false);
|
||||
close();
|
||||
|
||||
@@ -629,22 +629,6 @@ export const useAppEvents = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const IsDatabaseMigrationRequired = useCallback(() => {
|
||||
if (!db.migrations.required() || appLocked) return false;
|
||||
|
||||
presentSheet({
|
||||
component: <Migrate />,
|
||||
onClose: async () => {
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
}
|
||||
useSettingStore.getState().setAppLoading(false);
|
||||
},
|
||||
disableClosing: true
|
||||
});
|
||||
return true;
|
||||
}, [appLocked]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
onUserUpdated();
|
||||
@@ -652,45 +636,53 @@ export const useAppEvents = () => {
|
||||
}
|
||||
}, [loading, onUserUpdated]);
|
||||
|
||||
const initializeDatabase = useCallback(
|
||||
async (password?: string) => {
|
||||
if (useUserStore.getState().appLocked) return;
|
||||
if (!db.isInitialized) {
|
||||
RNBootSplash.hide({ fade: true });
|
||||
DatabaseLogger.info("Initializing database");
|
||||
try {
|
||||
await setupDatabase(password);
|
||||
await db.init();
|
||||
Notifications.setupReminders(true);
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e as Error);
|
||||
ToastManager.error(
|
||||
e as Error,
|
||||
"Error initializing database",
|
||||
"global"
|
||||
);
|
||||
}
|
||||
}
|
||||
const initializeDatabase = useCallback(async (password?: string) => {
|
||||
const IsDatabaseMigrationRequired = () => {
|
||||
if (!db.migrations.required() || useUserStore.getState().appLocked)
|
||||
return false;
|
||||
|
||||
if (db.isInitialized) {
|
||||
useSettingStore.getState().setAppLoading(false);
|
||||
presentSheet({
|
||||
component: <Migrate />,
|
||||
onClose: () => {
|
||||
if (!db.migrations.required()) {
|
||||
initializeDatabase();
|
||||
}
|
||||
},
|
||||
disableClosing: true
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
if (useUserStore.getState().appLocked) return;
|
||||
if (!db.isInitialized) {
|
||||
RNBootSplash.hide({ fade: true });
|
||||
DatabaseLogger.info("Initializing database");
|
||||
try {
|
||||
await setupDatabase(password);
|
||||
await db.init();
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e as Error);
|
||||
ToastManager.error(e as Error, "Error initializing database", "global");
|
||||
}
|
||||
if (IsDatabaseMigrationRequired()) return;
|
||||
Walkthrough.init();
|
||||
},
|
||||
[IsDatabaseMigrationRequired]
|
||||
);
|
||||
}
|
||||
|
||||
if (IsDatabaseMigrationRequired()) return;
|
||||
|
||||
if (db.isInitialized) {
|
||||
Notifications.setupReminders(true);
|
||||
useSettingStore.getState().setAppLoading(false);
|
||||
DatabaseLogger.info("Database initialized");
|
||||
}
|
||||
Walkthrough.init();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let sub: () => void;
|
||||
if (appLocked) {
|
||||
const sub = useUserStore.subscribe((state) => {
|
||||
if (!state.appLocked && useSettingStore.getState().isAppLoading) {
|
||||
initializeDatabase(useSettingStore.getState().dbPassword);
|
||||
useSettingStore.setState({
|
||||
dbPassword: undefined
|
||||
});
|
||||
|
||||
console.log("DB initialized");
|
||||
initializeDatabase();
|
||||
sub();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,7 +24,8 @@ import {
|
||||
Reminder,
|
||||
Shortcut,
|
||||
Tag,
|
||||
VirtualizedGrouping
|
||||
VirtualizedGrouping,
|
||||
HistorySession
|
||||
} from "@notesnook/core";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
@@ -43,6 +44,7 @@ type ItemTypeKey = {
|
||||
reminder: Reminder;
|
||||
attachment: Attachment;
|
||||
shortcut: Shortcut;
|
||||
noteHistory: HistorySession;
|
||||
};
|
||||
|
||||
function isValidIdOrIndex(idOrIndex?: string | number) {
|
||||
@@ -83,8 +85,6 @@ export const useDBItem = <T extends keyof ItemTypeKey>(
|
||||
`db.${type}s.${type}(id: string)`
|
||||
);
|
||||
} else {
|
||||
console.log("get notebook");
|
||||
|
||||
(db as any)[type + "s"]
|
||||
?.[type]?.(idOrIndex as string)
|
||||
.then((item: ItemTypeKey[T]) => {
|
||||
|
||||
@@ -70,6 +70,7 @@ import { useDragState } from "./editor/state";
|
||||
import { verifyUser } from "./functions";
|
||||
import { SettingSection } from "./types";
|
||||
import { getTimeLeft } from "./user-section";
|
||||
import { refreshAllStores } from "../../stores/create-db-collection-store";
|
||||
type User = any;
|
||||
|
||||
export const settingsGroups: SettingSection[] = [
|
||||
@@ -299,6 +300,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
await BiometicService.resetCredentials();
|
||||
MMKV.clearStore();
|
||||
clearAllStores();
|
||||
refreshAllStores();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
SettingsService.resetSettings();
|
||||
useUserStore.getState().setUser(null);
|
||||
@@ -853,14 +855,13 @@ export const settingsGroups: SettingSection[] = [
|
||||
|
||||
if (
|
||||
!(await BiometicService.isBiometryAvailable()) &&
|
||||
!useUserStore.getState().user &&
|
||||
!SettingsService.getProperty("appLockHasPasswordSecurity")
|
||||
) {
|
||||
ToastManager.show({
|
||||
heading: "Biometrics not enrolled",
|
||||
type: "error",
|
||||
message:
|
||||
"To use app lock, you must enable biometrics such as Fingerprint lock or Face ID on your phone or create an account."
|
||||
"To use app lock, you must enable biometrics such as Fingerprint lock or Face ID on your phone."
|
||||
});
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
return;
|
||||
@@ -889,8 +890,18 @@ export const settingsGroups: SettingSection[] = [
|
||||
},
|
||||
{
|
||||
id: "app-lock-pin",
|
||||
name: "Setup app lock password",
|
||||
description: "Set up a password or pin for app lock",
|
||||
name: () =>
|
||||
`Setup app lock ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}`,
|
||||
description: () =>
|
||||
`Set up a ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
} for app lock`,
|
||||
hidden: () => {
|
||||
return !!SettingsService.getProperty(
|
||||
"appLockHasPasswordSecurity"
|
||||
@@ -903,8 +914,18 @@ export const settingsGroups: SettingSection[] = [
|
||||
},
|
||||
{
|
||||
id: "app-lock-pin-change",
|
||||
name: "Change app lock pin",
|
||||
description: "Set up a password or pin for app lock",
|
||||
name: () =>
|
||||
`Change app lock ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}`,
|
||||
description: () =>
|
||||
`Set up a ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
} for app lock`,
|
||||
hidden: () => {
|
||||
return !SettingsService.getProperty("appLockHasPasswordSecurity");
|
||||
},
|
||||
@@ -915,16 +936,18 @@ export const settingsGroups: SettingSection[] = [
|
||||
},
|
||||
{
|
||||
id: "app-lock-pin-remove",
|
||||
name: `Remove app lock ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}`,
|
||||
description: `Remove app lock ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}, app lock will fallback to using account password to unlock the app`,
|
||||
name: () =>
|
||||
`Remove app lock ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}`,
|
||||
description: () =>
|
||||
`Remove app lock ${
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}, app lock will fallback to using account password to unlock the app`,
|
||||
hidden: () => {
|
||||
return !SettingsService.getProperty("appLockHasPasswordSecurity");
|
||||
},
|
||||
@@ -955,6 +978,16 @@ export const settingsGroups: SettingSection[] = [
|
||||
);
|
||||
SettingsService.setProperty("biometricsAuthEnabled", false);
|
||||
}
|
||||
if (
|
||||
!SettingsService.getProperty("biometricsAuthEnabled") &&
|
||||
!SettingsService.getProperty("appLockHasPasswordSecurity")
|
||||
) {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
ToastManager.show({
|
||||
heading: "App lock disabled",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: "fingerprint"
|
||||
}
|
||||
|
||||
@@ -213,7 +213,8 @@ async function run(progress, context) {
|
||||
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
|
||||
updateNextBackupTime();
|
||||
|
||||
let showBackupCompleteSheet = SettingsService.get().showBackupCompleteSheet;
|
||||
let showBackupCompleteSheet =
|
||||
progress && SettingsService.get().showBackupCompleteSheet;
|
||||
|
||||
if (context) return path;
|
||||
await sleep(300);
|
||||
@@ -233,7 +234,7 @@ async function run(progress, context) {
|
||||
return path;
|
||||
} catch (e) {
|
||||
await sleep(300);
|
||||
eSendEvent(eCloseSheet);
|
||||
progress && eSendEvent(eCloseSheet);
|
||||
ToastManager.error(e, "Backup failed!");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -47,18 +47,29 @@ function resetSettings() {
|
||||
|
||||
function migrateAppLock() {
|
||||
const appLockMode = get().appLockMode;
|
||||
if (appLockMode === "none") return;
|
||||
if (appLockMode === "none") {
|
||||
if (
|
||||
get().appLockEnabled &&
|
||||
!get().appLockHasPasswordSecurity &&
|
||||
!get().biometricsAuthEnabled
|
||||
) {
|
||||
setProperty("biometricsAuthEnabled", true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (appLockMode === "background") {
|
||||
set({
|
||||
appLockEnabled: true,
|
||||
appLockTimer: 0,
|
||||
appLockMode: "none"
|
||||
appLockMode: "none",
|
||||
biometricsAuthEnabled: true
|
||||
});
|
||||
} else if (appLockMode === "launch") {
|
||||
set({
|
||||
appLockEnabled: true,
|
||||
appLockTimer: -1,
|
||||
appLockMode: "none"
|
||||
appLockMode: "none",
|
||||
biometricsAuthEnabled: true
|
||||
});
|
||||
}
|
||||
DatabaseLogger.debug("App lock Migrated");
|
||||
|
||||
@@ -165,7 +165,6 @@ export const defaultSettings: SettingStore["settings"] = {
|
||||
colorScheme: "light",
|
||||
lighTheme: ThemeLight,
|
||||
darkTheme: ThemeDark,
|
||||
biometricsAuthEnabled: false,
|
||||
appLockHasPasswordSecurity: false
|
||||
};
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
6517B7C22B6838EB0079FF37 /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7BF2B6838EB0079FF37 /* OpenSans-SemiBold.ttf */; };
|
||||
6517B7C32B6838EB0079FF37 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */; };
|
||||
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */; };
|
||||
656DD2AB2B1891DF00A362EA /* (null) in Resources */ = {isa = PBXBuildFile; };
|
||||
656DD2AC2B1891DF00A362EA /* (null) in Resources */ = {isa = PBXBuildFile; };
|
||||
656DD2AD2B1891DF00A362EA /* (null) in Resources */ = {isa = PBXBuildFile; };
|
||||
656DD2AB2B1891DF00A362EA /* BuildFile in Resources */ = {isa = PBXBuildFile; };
|
||||
656DD2AC2B1891DF00A362EA /* BuildFile in Resources */ = {isa = PBXBuildFile; };
|
||||
656DD2AD2B1891DF00A362EA /* BuildFile in Resources */ = {isa = PBXBuildFile; };
|
||||
6593E4A3281C345400492C50 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6593E4A2281C345400492C50 /* AppDelegate.mm */; };
|
||||
659BE46725E11A5100E05671 /* notesnook-text.png in Resources */ = {isa = PBXBuildFile; fileRef = 659BE46625E11A5100E05671 /* notesnook-text.png */; };
|
||||
65AA857925E6DDEC00772A01 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65AA857825E6DDEC00772A01 /* WidgetKit.framework */; };
|
||||
@@ -582,9 +582,9 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
656DD2AB2B1891DF00A362EA /* (null) in Resources */,
|
||||
656DD2AC2B1891DF00A362EA /* (null) in Resources */,
|
||||
656DD2AD2B1891DF00A362EA /* (null) in Resources */,
|
||||
656DD2AB2B1891DF00A362EA /* BuildFile in Resources */,
|
||||
656DD2AC2B1891DF00A362EA /* BuildFile in Resources */,
|
||||
656DD2AD2B1891DF00A362EA /* BuildFile in Resources */,
|
||||
65C400DF2A80B6B600AA3DF5 /* MaterialCommunityIcons.ttf in Resources */,
|
||||
65C149872A61151B005C40F1 /* extension.bundle in Resources */,
|
||||
65B5014725A672B200E2D264 /* MainInterface.storyboard in Resources */,
|
||||
|
||||
@@ -130,7 +130,7 @@ post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
if target.name == "react-native-quick-sqlite" then
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'SQLITE_ENABLE_FTS5=1 SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT HAVE_CIPHER_AES_128_CBC=0 HAVE_CIPHER_AES_256_CBC=0 HAVE_CIPHER_SQLCIPHER=0 HAVE_CIPHER_RC4=0 HAVE_CIPHER_CHACHA20=1 SQLITE_ENABLE_FTS5 SQLITE_OMIT_PROGRESS_CALLBACK=1 SQLITE_MAX_EXPR_DEPTH=0 SQLITE_OMIT_DEPRECATED=1 SQLITE_DEFAULT_WAL_SYNCHRONOUS=1 SQLITE_LIKE_DOESNT_MATCH_BLOBS=1 SQLITE_DQS=0 SQLITE_DEFAULT_MEMSTATUS=0 SQLITE_USE_ALLOCA=1'
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'SQLITE_ENABLE_FTS5=1 SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT=1 HAVE_CIPHER_AES_128_CBC=0 HAVE_CIPHER_AES_256_CBC=0 HAVE_CIPHER_SQLCIPHER=0 HAVE_CIPHER_RC4=0 HAVE_CIPHER_CHACHA20=1 SQLITE_OMIT_PROGRESS_CALLBACK=1 SQLITE_MAX_EXPR_DEPTH=0 SQLITE_OMIT_DEPRECATED=1 SQLITE_LIKE_DOESNT_MATCH_BLOBS=1 SQLITE_DQS=0 SQLITE_DEFAULT_MEMSTATUS=0 SQLITE_USE_ALLOCA=1'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -946,6 +946,6 @@ SPEC CHECKSUMS:
|
||||
toolbar-android: 2a73856e98b750d7e71ce4644d3f41cc98211719
|
||||
Yoga: 1d6727ed193122f6adaf435c3de1a768326ff83b
|
||||
|
||||
PODFILE CHECKSUM: 10c0cc8b6b8b01431d2b42fc7d21667cf36209f0
|
||||
PODFILE CHECKSUM: c859ca7e037c52f80a95e3483c6ee99525c62cd9
|
||||
|
||||
COCOAPODS: 1.14.2
|
||||
|
||||
794
apps/mobile/package-lock.json
generated
794
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -39,4 +39,4 @@
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user