mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 11:08:47 +02:00
Compare commits
1 Commits
fix/app-lo
...
fix/pricin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cb6f51870 |
@@ -43,8 +43,9 @@ const IOS_KEYCHAIN_ACCESS_GROUP = "group.org.streetwriters.notesnook";
|
||||
const IOS_KEYCHAIN_SERVICE_NAME = "org.streetwriters.notesnook";
|
||||
const KEYCHAIN_SERVER_DBKEY = "notesnook:db";
|
||||
|
||||
const NOTESNOOK_APPLOCK_KEY_SALT = "kBwr1Kre86ebOZ8ThLu2OA";
|
||||
const NOTESNOOK_DB_KEY_SALT = "SNuzOcEK3amoqL0WvPeKqw";
|
||||
const NOTESNOOK_APPLOCK_KEY_SALT = "3dqclWbOYllfk9kk";
|
||||
const NOTESNOOK_DB_KEY_SALT = "2rcgSprDmRvZ1AAa";
|
||||
const NOTESNOOK_USER_KEY_SALT = "7qO4qeoM6PbsAJ0Q";
|
||||
|
||||
const DB_KEY_CIPHER = "databaseKeyCipher";
|
||||
const USER_KEY_CIPHER = "userKeyCipher";
|
||||
@@ -116,7 +117,9 @@ export async function setAppLockVerificationCipher(appLockPassword) {
|
||||
NOTESNOOK_APPLOCK_KEY_SALT
|
||||
);
|
||||
const encrypted = await encrypt(appLockCredentials, generatePassword());
|
||||
|
||||
CipherStorage.setMap(APPLOCK_CIPHER, encrypted);
|
||||
|
||||
DatabaseLogger.info("setAppLockVerificationCipher");
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
@@ -132,8 +135,10 @@ export async function validateAppLockPassword(appLockPassword) {
|
||||
try {
|
||||
const appLockCipher = CipherStorage.getMap(APPLOCK_CIPHER);
|
||||
if (!appLockCipher) return true;
|
||||
const key = await Sodium.deriveKey(appLockPassword, appLockCipher.salt);
|
||||
const decrypted = await decrypt(key, appLockCipher);
|
||||
const decrypted = await decrypt(
|
||||
await Sodium.deriveKey(appLockPassword, NOTESNOOK_APPLOCK_KEY_SALT),
|
||||
appLockCipher
|
||||
);
|
||||
|
||||
DatabaseLogger.info(
|
||||
`validateAppLockPassword: ${typeof decrypted === "string"}`
|
||||
@@ -208,7 +213,8 @@ export async function getDatabaseKey(appLockPassword) {
|
||||
if (userKeyCredentials) {
|
||||
const userKeyCipher = await encrypt(
|
||||
{
|
||||
key: DB_KEY
|
||||
key: DB_KEY,
|
||||
salt: NOTESNOOK_USER_KEY_SALT
|
||||
},
|
||||
userKeyCredentials.password
|
||||
);
|
||||
@@ -232,7 +238,8 @@ export async function deriveCryptoKey(data) {
|
||||
let credentials = await Sodium.deriveKey(data.password, data.salt);
|
||||
const userKeyCipher = await encrypt(
|
||||
{
|
||||
key: await getDatabaseKey()
|
||||
key: await getDatabaseKey(),
|
||||
salt: NOTESNOOK_USER_KEY_SALT
|
||||
},
|
||||
credentials.key
|
||||
);
|
||||
|
||||
@@ -34,13 +34,11 @@ import {
|
||||
import { MMKV } from "../../common/database/mmkv";
|
||||
import { useAppState } from "../../hooks/use-app-state";
|
||||
import BiometricService from "../../services/biometrics";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { NotesnookModule } from "../../utils/notesnook-module";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Toast } from "../toast";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Input from "../ui/input";
|
||||
@@ -150,12 +148,6 @@ const AppLockedOverlay = () => {
|
||||
lockApp(false);
|
||||
enabled(false);
|
||||
password.current = undefined;
|
||||
} else {
|
||||
ToastManager.show({
|
||||
heading: `Invalid ${keyboardType === "numeric" ? "pin" : "password"}`,
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -196,7 +188,11 @@ const AppLockedOverlay = () => {
|
||||
enabled(false);
|
||||
} else {
|
||||
SettingsService.appEnteredBackground();
|
||||
if (SettingsService.get().privacyScreen) {
|
||||
|
||||
if (
|
||||
SettingsService.get().privacyScreen ||
|
||||
SettingsService.getProperty("appLockEnabled")
|
||||
) {
|
||||
enabled(true);
|
||||
}
|
||||
}
|
||||
@@ -213,7 +209,6 @@ const AppLockedOverlay = () => {
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Toast context="local" />
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
|
||||
@@ -44,7 +44,6 @@ type DialogInfo = {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
context: "global" | "local" | (string & {});
|
||||
secureTextEntry?: boolean;
|
||||
keyboardType?: string;
|
||||
};
|
||||
|
||||
export function presentDialog(data: Partial<DialogInfo>): void {
|
||||
|
||||
@@ -174,7 +174,6 @@ export const Dialog = ({ context = "global" }) => {
|
||||
onSubmit={onPressPositive}
|
||||
returnKeyLabel="Done"
|
||||
returnKeyType="done"
|
||||
keyboardType={dialogInfo.keyboardType || "default"}
|
||||
placeholder={dialogInfo.inputPlaceholder}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { db } from "../../../common/database";
|
||||
import {
|
||||
clearAppLockVerificationCipher,
|
||||
setAppLockVerificationCipher,
|
||||
@@ -45,7 +44,6 @@ import BaseDialog from "../../dialog/base-dialog";
|
||||
import DialogButtons from "../../dialog/dialog-buttons";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import { Toast } from "../../toast";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
@@ -68,7 +66,6 @@ export const AppLockPassword = () => {
|
||||
confirmPassword?: string;
|
||||
}>({});
|
||||
const [secureTextEntry, setSecureTextEntry] = useState(true);
|
||||
const [accountPass, setAccountPass] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const subs = [
|
||||
@@ -76,7 +73,6 @@ export const AppLockPassword = () => {
|
||||
eOpenAppLockPasswordDialog,
|
||||
(mode: "create" | "change" | "remove") => {
|
||||
setMode(mode);
|
||||
setAccountPass(false);
|
||||
setVisible(true);
|
||||
}
|
||||
),
|
||||
@@ -130,9 +126,7 @@ export const AppLockPassword = () => {
|
||||
mode === "change"
|
||||
? `Change app lock ${keyboardType}`
|
||||
: mode === "remove"
|
||||
? `Enter ${
|
||||
accountPass ? "account password" : `app lock ${keyboardType}`
|
||||
} to remove ${keyboardType}`
|
||||
? `Remove app lock ${keyboardType}`
|
||||
: `Set up a custom app lock ${keyboardType} to unlock the app`
|
||||
}
|
||||
icon="shield"
|
||||
@@ -175,39 +169,33 @@ export const AppLockPassword = () => {
|
||||
confirmPasswordInputRef.current?.focus();
|
||||
}}
|
||||
defaultValue={values.current.password}
|
||||
keyboardType={
|
||||
keyboardType === "pin" && !accountPass ? "number-pad" : "default"
|
||||
}
|
||||
keyboardType={keyboardType === "pin" ? "number-pad" : "default"}
|
||||
autoComplete="password"
|
||||
returnKeyLabel={mode !== "remove" ? "Next" : "Remove"}
|
||||
returnKeyType={mode !== "remove" ? "next" : "done"}
|
||||
secureTextEntry={secureTextEntry}
|
||||
buttonLeft={
|
||||
accountPass ? null : (
|
||||
<IconButton
|
||||
name={keyboardType === "password" ? "numeric" : "keyboard"}
|
||||
onPress={() => {
|
||||
setKeyboardType(
|
||||
keyboardType === "password" ? "pin" : "password"
|
||||
);
|
||||
setSecureTextEntry(false);
|
||||
setImmediate(() => {
|
||||
setSecureTextEntry(true);
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
marginRight: 5
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
)
|
||||
<IconButton
|
||||
name={keyboardType === "password" ? "numeric" : "keyboard"}
|
||||
onPress={() => {
|
||||
setKeyboardType(
|
||||
keyboardType === "password" ? "pin" : "password"
|
||||
);
|
||||
setSecureTextEntry(false);
|
||||
setImmediate(() => {
|
||||
setSecureTextEntry(true);
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
marginRight: 5
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
}
|
||||
placeholder={
|
||||
accountPass
|
||||
? "Account password"
|
||||
: mode === "change"
|
||||
mode === "change"
|
||||
? `New ${keyboardType}`
|
||||
: `${keyboardType === "pin" ? "Pin" : "Password"}`
|
||||
}
|
||||
@@ -234,36 +222,6 @@ export const AppLockPassword = () => {
|
||||
placeholder={`Confirm ${keyboardType}`}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{mode === "remove" ? (
|
||||
<>
|
||||
<Button
|
||||
icon={
|
||||
accountPass ? "checkbox-marked" : "checkbox-blank-outline"
|
||||
}
|
||||
onPress={() => {
|
||||
setSecureTextEntry(false);
|
||||
setAccountPass(!accountPass);
|
||||
setTimeout(() => {
|
||||
setSecureTextEntry(true);
|
||||
});
|
||||
}}
|
||||
iconSize={SIZE.lg}
|
||||
type="plain"
|
||||
iconColor={
|
||||
accountPass ? colors.primary.accent : colors.primary.icon
|
||||
}
|
||||
title="Use account password instead"
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "flex-start",
|
||||
justifyContent: "flex-start",
|
||||
paddingHorizontal: 0,
|
||||
height: 30
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<DialogButtons
|
||||
@@ -291,8 +249,7 @@ export const AppLockPassword = () => {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const password = values.current.password;
|
||||
setAppLockVerificationCipher(password);
|
||||
await setAppLockVerificationCipher(values.current.password);
|
||||
SettingsService.setProperty("appLockHasPasswordSecurity", true);
|
||||
} else if (mode === "change") {
|
||||
if (
|
||||
@@ -320,6 +277,7 @@ export const AppLockPassword = () => {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const isCurrentPasswordCorrect = await validateAppLockPassword(
|
||||
values.current.currentPassword
|
||||
);
|
||||
@@ -335,10 +293,8 @@ export const AppLockPassword = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const password = values.current.password;
|
||||
await clearAppLockVerificationCipher();
|
||||
SettingsService.setProperty("appLockHasPasswordSecurity", true);
|
||||
await setAppLockVerificationCipher(password);
|
||||
await setAppLockVerificationCipher(values.current.password);
|
||||
} else if (mode === "remove") {
|
||||
if (!values.current.password) {
|
||||
ToastManager.error(
|
||||
@@ -349,18 +305,14 @@ export const AppLockPassword = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const isCurrentPasswordCorrect = accountPass
|
||||
? await db.user.verifyPassword(values.current.password)
|
||||
: await validateAppLockPassword(values.current.password);
|
||||
const isCurrentPasswordCorrect = await validateAppLockPassword(
|
||||
values.current.password
|
||||
);
|
||||
|
||||
if (!isCurrentPasswordCorrect) {
|
||||
ToastManager.error(
|
||||
new Error(
|
||||
accountPass
|
||||
? "Account password incorrect"
|
||||
: `${
|
||||
keyboardType === "pin" ? "Pin" : "Password"
|
||||
} incorrect`
|
||||
`${keyboardType === "pin" ? "Pin" : "Password"} incorrect`
|
||||
),
|
||||
undefined,
|
||||
"local"
|
||||
@@ -390,9 +342,7 @@ export const AppLockPassword = () => {
|
||||
|
||||
close();
|
||||
}}
|
||||
positiveTitle={
|
||||
mode === "remove" ? "Remove" : mode === "change" ? "Change" : "Save"
|
||||
}
|
||||
positiveTitle="Save"
|
||||
negativeTitle="Cancel"
|
||||
positiveType="transparent"
|
||||
loading={false}
|
||||
|
||||
@@ -22,20 +22,14 @@ import { Platform } from "react-native";
|
||||
import { Subscription } from "react-native-iap";
|
||||
import PremiumService from "../services/premium";
|
||||
import { db } from "../common/database";
|
||||
import { Product } from "@notesnook/core/dist/api/pricing";
|
||||
|
||||
type PurchaseInfo = {
|
||||
country: string;
|
||||
countryCode: string;
|
||||
sku: string;
|
||||
discount: number;
|
||||
};
|
||||
|
||||
const skuInfos: { [name: string]: PurchaseInfo | undefined } = {};
|
||||
const skuInfos: { [name: string]: Product | undefined } = {};
|
||||
|
||||
export const usePricing = (period: "monthly" | "yearly") => {
|
||||
const [current, setCurrent] = useState<{
|
||||
period: string;
|
||||
info?: PurchaseInfo;
|
||||
info?: Product;
|
||||
product?: Subscription;
|
||||
}>();
|
||||
|
||||
@@ -54,6 +48,7 @@ export const usePricing = (period: "monthly" | "yearly") => {
|
||||
period
|
||||
));
|
||||
skuInfos[period] = skuInfo;
|
||||
|
||||
const products = (await PremiumService.getProducts()) as Subscription[];
|
||||
let product = products.find((p) => p.productId === skuInfo?.sku);
|
||||
if (!product)
|
||||
|
||||
@@ -90,26 +90,13 @@ export async function verifyUserWithApplock() {
|
||||
positiveText: "Verify",
|
||||
secureTextEntry: true,
|
||||
negativeText: "Cancel",
|
||||
keyboardType: keyboardType,
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
const verified = await validateAppLockPassword(value);
|
||||
if (!verified) {
|
||||
ToastManager.show({
|
||||
heading: `Invalid ${
|
||||
keyboardType === "numeric" ? "pin" : "password"
|
||||
}`,
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
resolve(verified);
|
||||
} catch (e) {
|
||||
resolve(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -871,9 +871,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
type: "switch",
|
||||
property: "appLockEnabled",
|
||||
onChange: () => {
|
||||
SettingsService.set({
|
||||
privacyScreen: true
|
||||
});
|
||||
SettingsService.setPrivacyScreen(SettingsService.get());
|
||||
},
|
||||
onVerify: async () => {
|
||||
@@ -976,7 +973,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
SettingsService.getProperty("applockKeyboardType") === "numeric"
|
||||
? "pin"
|
||||
: "password"
|
||||
}, app lock will be disabled if no other security method is enabled.`,
|
||||
}, app lock will fallback to using account password to unlock the app`,
|
||||
hidden: () => {
|
||||
return !SettingsService.getProperty("appLockHasPasswordSecurity");
|
||||
},
|
||||
|
||||
@@ -76,17 +76,6 @@ function migrateAppLock() {
|
||||
DatabaseLogger.debug("App lock Migrated");
|
||||
}
|
||||
|
||||
function migrateSettings(settings: SettingStore["settings"]) {
|
||||
const version = settings.settingsVersion;
|
||||
if (!version) {
|
||||
settings.settingsVersion = 1;
|
||||
settings.privacyScreen = settings.appLockEnabled
|
||||
? true
|
||||
: settings.privacyScreen;
|
||||
MMKV.setString("appSettings", JSON.stringify(settings));
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
scale.fontScale = 1;
|
||||
const settingsJson = MMKV.getString("appSettings");
|
||||
@@ -94,17 +83,14 @@ function init() {
|
||||
if (!settingsJson) {
|
||||
MMKV.setString("appSettings", JSON.stringify(settings));
|
||||
} else {
|
||||
const settingsParsed = JSON.parse(settingsJson);
|
||||
migrateSettings(settingsParsed);
|
||||
settings = {
|
||||
...settings,
|
||||
...settingsParsed
|
||||
...JSON.parse(settingsJson)
|
||||
};
|
||||
}
|
||||
if (settings.fontScale) {
|
||||
scale.fontScale = settings.fontScale;
|
||||
}
|
||||
|
||||
setTimeout(() => setPrivacyScreen(settings), 1);
|
||||
updateSize();
|
||||
useSettingStore.getState().setSettings({ ...settings });
|
||||
@@ -112,7 +98,7 @@ function init() {
|
||||
}
|
||||
|
||||
function setPrivacyScreen(settings: SettingStore["settings"]) {
|
||||
if (settings.privacyScreen) {
|
||||
if (settings.privacyScreen || settings.appLockEnabled) {
|
||||
if (Platform.OS === "android") {
|
||||
NotesnookModule.setSecureMode(true);
|
||||
} else {
|
||||
|
||||
@@ -81,7 +81,6 @@ export type Settings = {
|
||||
biometricsAuthEnabled?: boolean;
|
||||
backgroundSync?: boolean;
|
||||
applockKeyboardType: "numeric" | "default";
|
||||
settingsVersion?: number;
|
||||
};
|
||||
|
||||
type DimensionsType = {
|
||||
@@ -170,8 +169,7 @@ export const defaultSettings: SettingStore["settings"] = {
|
||||
markdownShortcuts: true,
|
||||
biometricsAuthEnabled: false,
|
||||
appLockHasPasswordSecurity: false,
|
||||
backgroundSync: true,
|
||||
settingsVersion: 0
|
||||
backgroundSync: true
|
||||
};
|
||||
|
||||
export const useSettingStore = create<SettingStore>((set, get) => ({
|
||||
|
||||
@@ -179,7 +179,7 @@ class Database {
|
||||
subscriptions = new Subscriptions(this.tokenManager);
|
||||
offers = new Offers();
|
||||
debug = new Debug();
|
||||
pricing = new Pricing();
|
||||
pricing = Pricing;
|
||||
|
||||
user = new UserManager(this);
|
||||
syncer = new Sync(this);
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import http from "../utils/http";
|
||||
|
||||
type Product = {
|
||||
export type Product = {
|
||||
country: string;
|
||||
countryCode: string;
|
||||
sku?: string;
|
||||
|
||||
Reference in New Issue
Block a user