mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-03 04:31:46 +02:00
mobile: improve verification flow before changing app lock
This commit is contained in:
@@ -43,6 +43,7 @@ const DialogProvider = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppLockPassword />
|
||||
<LoadingDialog />
|
||||
<Dialog context="global" />
|
||||
<PremiumDialog colors={colors} />
|
||||
@@ -61,7 +62,6 @@ const DialogProvider = () => {
|
||||
<SessionExpired />
|
||||
<PDFPreview />
|
||||
<JumpToSectionDialog />
|
||||
<AppLockPassword />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ export const AppLockPassword = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
return !visible ? null : (
|
||||
<BaseDialog
|
||||
onShow={async () => {
|
||||
await sleep(100);
|
||||
|
||||
@@ -17,9 +17,13 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { presentDialog } from "../../components/dialog/functions";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import { db } from "../../common/database";
|
||||
import { validateAppLockPassword } from "../../common/database/encryption";
|
||||
import { presentDialog } from "../../components/dialog/functions";
|
||||
import BiometicService from "../../services/biometrics";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { sleep } from "../../utils/time";
|
||||
|
||||
export async function verifyUser(
|
||||
@@ -69,3 +73,54 @@ export async function verifyUser(
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function verifyUserWithApplock() {
|
||||
const keyboardType = SettingsService.getProperty("applockKeyboardType");
|
||||
return new Promise((resolve) => {
|
||||
if (SettingsService.getProperty("appLockHasPasswordSecurity")) {
|
||||
presentDialog({
|
||||
title: "Verify it's you",
|
||||
input: true,
|
||||
inputPlaceholder: `Enter app lock ${
|
||||
keyboardType === "numeric" ? "pin" : "password"
|
||||
}`,
|
||||
paragraph: `Please enter your app lock ${
|
||||
keyboardType === "numeric" ? "pin" : "password"
|
||||
}`,
|
||||
positiveText: "Disable",
|
||||
secureTextEntry: true,
|
||||
negativeText: "Cancel",
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
const verified = await validateAppLockPassword(value);
|
||||
resolve(verified);
|
||||
} catch (e) {
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
BiometicService.isBiometryAvailable().then((available) => {
|
||||
if (available) {
|
||||
BiometicService.validateUser("Verify it's you").then((verified) => {
|
||||
resolve(verified);
|
||||
});
|
||||
} else if (useUserStore.getState().user) {
|
||||
let verified = false;
|
||||
verifyUser(
|
||||
null,
|
||||
() => {
|
||||
resolve(true);
|
||||
},
|
||||
false,
|
||||
() => {
|
||||
resolve(verified);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ interface PickerOptions<T> {
|
||||
premium?: boolean;
|
||||
onCheckOptionIsPremium?: (item: T) => boolean;
|
||||
requiresVerification?: () => boolean;
|
||||
onVerify?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
export function SettingsPicker<T>({
|
||||
@@ -51,7 +52,8 @@ export function SettingsPicker<T>({
|
||||
getItemKey,
|
||||
premium,
|
||||
onCheckOptionIsPremium = () => true,
|
||||
requiresVerification = () => false
|
||||
requiresVerification = () => false,
|
||||
onVerify
|
||||
}: PickerOptions<T>) {
|
||||
const { colors } = useThemeColors("contextMenu");
|
||||
const menuRef = useRef<any>();
|
||||
@@ -105,6 +107,7 @@ export function SettingsPicker<T>({
|
||||
anchor={
|
||||
<PressableButton
|
||||
onPress={async () => {
|
||||
if (onVerify && !(await onVerify())) return;
|
||||
menuRef.current?.show();
|
||||
}}
|
||||
type="grayBg"
|
||||
|
||||
@@ -27,6 +27,7 @@ import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
|
||||
import { DATE_FORMATS, TIME_FORMATS } from "@notesnook/core/dist/common";
|
||||
import dayjs from "dayjs";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { verifyUserWithApplock } from "../functions";
|
||||
|
||||
export const FontPicker = createSettingsPicker({
|
||||
getValue: () => useSettingStore.getState().settings.defaultFontFamily,
|
||||
@@ -156,5 +157,7 @@ export const ApplockTimerPicker = createSettingsPicker({
|
||||
getItemKey: (item) => item.toString(),
|
||||
options: [-1, 0, 1, 5, 15, 30],
|
||||
compareValue: (current, item) => current === item,
|
||||
premium: true
|
||||
onVerify: () => {
|
||||
return verifyUserWithApplock();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -104,17 +104,26 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
|
||||
borderRadius: 0,
|
||||
...styles
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress={async () => {
|
||||
switch (item.type) {
|
||||
case "screen":
|
||||
navigation.dispatch(StackActions.push("SettingsGroup", item));
|
||||
useNavigationStore.getState().update("Settings");
|
||||
{
|
||||
if (item.onVerify && !(await item.onVerify())) return;
|
||||
navigation.dispatch(StackActions.push("SettingsGroup", item));
|
||||
useNavigationStore.getState().update("Settings");
|
||||
}
|
||||
break;
|
||||
case "switch":
|
||||
onChangeSettings();
|
||||
{
|
||||
if (item.onVerify && !(await item.onVerify())) return;
|
||||
onChangeSettings();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
item.modifer && item.modifer(current);
|
||||
{
|
||||
if (item.onVerify && !(await item.onVerify())) return;
|
||||
item.modifer && item.modifer(current);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -25,7 +25,6 @@ import { getVersion } from "react-native-device-info";
|
||||
import * as RNIap from "react-native-iap";
|
||||
import { enabled } from "react-native-privacy-snapshot";
|
||||
import { db } from "../../common/database";
|
||||
import { validateAppLockPassword } from "../../common/database/encryption";
|
||||
import { MMKV } from "../../common/database/mmkv";
|
||||
import { AttachmentDialog } from "../../components/attachments";
|
||||
import { ChangePassword } from "../../components/auth/change-password";
|
||||
@@ -53,6 +52,7 @@ import PremiumService from "../../services/premium";
|
||||
import SettingsService from "../../services/settings";
|
||||
import Sync from "../../services/sync";
|
||||
import { clearAllStores } from "../../stores";
|
||||
import { refreshAllStores } from "../../stores/create-db-collection-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
@@ -67,10 +67,9 @@ import { NotesnookModule } from "../../utils/notesnook-module";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { MFARecoveryCodes, MFASheet } from "./2fa";
|
||||
import { useDragState } from "./editor/state";
|
||||
import { verifyUser } from "./functions";
|
||||
import { verifyUser, verifyUserWithApplock } 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[] = [
|
||||
@@ -790,94 +789,33 @@ export const settingsGroups: SettingSection[] = [
|
||||
icon: "lock",
|
||||
type: "switch",
|
||||
property: "appLockEnabled",
|
||||
onChange: async () => {
|
||||
onVerify: async () => {
|
||||
const verified = await verifyUserWithApplock();
|
||||
if (!verified) return false;
|
||||
|
||||
if (!SettingsService.getProperty("appLockEnabled")) {
|
||||
const keyboardType = SettingsService.getProperty(
|
||||
"applockKeyboardType"
|
||||
);
|
||||
if (SettingsService.getProperty("appLockHasPasswordSecurity")) {
|
||||
presentDialog({
|
||||
title: "Verify it's you",
|
||||
input: true,
|
||||
inputPlaceholder: `Enter app lock ${
|
||||
keyboardType === "numeric" ? "pin" : "password"
|
||||
}`,
|
||||
paragraph: `Please enter your app lock ${
|
||||
keyboardType === "numeric" ? "pin" : "password"
|
||||
} to disable app lock`,
|
||||
positiveText: "Disable",
|
||||
secureTextEntry: true,
|
||||
negativeText: "Cancel",
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
const verified = await validateAppLockPassword(value);
|
||||
if (!verified) {
|
||||
SettingsService.setProperty("appLockEnabled", true);
|
||||
return false;
|
||||
} else {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
}
|
||||
} catch (e) {
|
||||
SettingsService.setProperty("appLockEnabled", true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (await BiometicService.isBiometryAvailable()) {
|
||||
const verified = await BiometicService.validateUser(
|
||||
"Verify it's you"
|
||||
);
|
||||
if (!verified) {
|
||||
SettingsService.setProperty("appLockEnabled", true);
|
||||
return;
|
||||
} else {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
}
|
||||
} else if (useUserStore.getState().user) {
|
||||
let verified = false;
|
||||
verifyUser(
|
||||
null,
|
||||
() => {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
verified = true;
|
||||
},
|
||||
false,
|
||||
() => {
|
||||
if (!verified)
|
||||
SettingsService.setProperty("appLockEnabled", true);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!(await BiometicService.isBiometryAvailable()) &&
|
||||
!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."
|
||||
});
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SettingsService.getProperty("appLockHasPasswordSecurity")) {
|
||||
const verified = await BiometicService.validateUser(
|
||||
"Verify it's you"
|
||||
);
|
||||
if (verified) {
|
||||
if (
|
||||
!SettingsService.getProperty("appLockHasPasswordSecurity") &&
|
||||
(await BiometicService.isBiometryAvailable())
|
||||
) {
|
||||
SettingsService.setProperty("biometricsAuthEnabled", true);
|
||||
} else {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!(await BiometicService.isBiometryAvailable()) &&
|
||||
!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."
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return verified;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -907,8 +845,12 @@ export const settingsGroups: SettingSection[] = [
|
||||
"appLockHasPasswordSecurity"
|
||||
);
|
||||
},
|
||||
onVerify: () => {
|
||||
return verifyUserWithApplock();
|
||||
},
|
||||
property: "appLockHasPasswordSecurity",
|
||||
modifer: () => {
|
||||
console.log("called modifier..");
|
||||
AppLockPassword.present("create");
|
||||
}
|
||||
},
|
||||
@@ -962,32 +904,23 @@ export const settingsGroups: SettingSection[] = [
|
||||
description: "Allow biometric authentication to unlock the app",
|
||||
type: "switch",
|
||||
property: "biometricsAuthEnabled",
|
||||
onChange: async () => {
|
||||
if (await BiometicService.isBiometryAvailable()) {
|
||||
const verified = await BiometicService.validateUser(
|
||||
"Verify it's you"
|
||||
);
|
||||
if (!verified) {
|
||||
SettingsService.setProperty("biometricsAuthEnabled", false);
|
||||
onVerify: async () => {
|
||||
const verified = await verifyUserWithApplock();
|
||||
if (!verified) return false;
|
||||
|
||||
if (SettingsService.getProperty("biometricsAuthEnabled")) {
|
||||
if (
|
||||
!SettingsService.getProperty("appLockHasPasswordSecurity")
|
||||
) {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
ToastManager.show({
|
||||
heading: "App lock disabled",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ToastManager.error(
|
||||
new Error(
|
||||
"Biometric authentication is unavailable on this device."
|
||||
)
|
||||
);
|
||||
SettingsService.setProperty("biometricsAuthEnabled", false);
|
||||
}
|
||||
if (
|
||||
!SettingsService.getProperty("biometricsAuthEnabled") &&
|
||||
!SettingsService.getProperty("appLockHasPasswordSecurity")
|
||||
) {
|
||||
SettingsService.setProperty("appLockEnabled", false);
|
||||
ToastManager.show({
|
||||
heading: "App lock disabled",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
|
||||
return verified;
|
||||
},
|
||||
icon: "fingerprint"
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export type SettingSection = {
|
||||
options?: any[];
|
||||
minInputValue?: number;
|
||||
maxInputValue?: number;
|
||||
onVerify?: () => Promise<boolean>;
|
||||
};
|
||||
|
||||
export type SettingsGroup = {
|
||||
|
||||
@@ -154,7 +154,7 @@ async function updateNextBackupTime() {
|
||||
* @param {string} context
|
||||
* @returns {Promise<{path?: string, error?: Error}}>
|
||||
*/
|
||||
async function run(progress, context) {
|
||||
async function run(progress, context = "global") {
|
||||
let androidBackupDirectory = await checkBackupDirExists(false, context);
|
||||
if (!androidBackupDirectory)
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user