Compare commits

...

46 Commits

Author SHA1 Message Date
Ammar Ahmed
cdd50f0258 mobile: release v3.2.7 2025-07-11 14:01:22 +05:00
Ammar Ahmed
8c8b92cc4e Merge pull request #8333 from streetwriters/mobile-fix-empty-note-bug
Fix notes open empty from notes list randomly
2025-07-11 13:57:47 +05:00
Ammar Ahmed
31dd1619f6 mobile: fix notes open empty from notes list 2025-07-11 12:54:39 +05:00
Ammar Ahmed
afb010e3ae Merge pull request #8331 from streetwriters/release-mobile-3.2.6
mobile: release v3.2.6
2025-07-11 11:15:19 +05:00
Abdullah Atta
ddebdcda43 Merge pull request #8330 from 01zulfi/monograph/fix-api-host-on-client 2025-07-11 10:51:35 +05:00
01zulfi
29c69418e9 monograph: fix API_HOST on client
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-07-11 09:44:09 +05:00
Ammar Ahmed
3e565e5195 mobile: release v3.2.6 2025-07-08 14:42:46 +05:00
Ammar Ahmed
1b352d6162 Merge pull request #8320 from streetwriters/mobile-fix-crash-on-boot-android
Fix boot crash on android
2025-07-08 14:40:08 +05:00
Ammar Ahmed
258aa77cdd mobile: fix boot crash on android 2025-07-08 14:23:25 +05:00
Ammar Ahmed
7789bd145c Merge pull request #8319 from streetwriters/mobile-fix-duplicate-body-notification
Fix reminder description duplicated in notification on iOS
2025-07-08 12:24:52 +05:00
Ammar Ahmed
c1457cc83f mobile: show description in notification body only 2025-07-08 12:20:40 +05:00
Ammar Ahmed
8b316d0abf mobile: release v3.2.5 2025-06-30 13:54:35 +05:00
Ammar Ahmed
def5059ff1 mobile: fix app stuck on logout on ios 2025-06-30 13:44:30 +05:00
Ammar Ahmed
e1febfae31 mobile: fix loading pdf screen not showing 2025-06-30 13:37:13 +05:00
Ammar Ahmed
82aa506902 mobile: fix pdf view gets stuck 2025-06-30 12:40:18 +05:00
Ammar Ahmed
de1aa40ecf mobile: fix sqlite3-fts5-html lib loading error on ios 2025-06-30 12:39:12 +05:00
Ammar Ahmed
53724b437e mobile: fix app store error invalid swift support 2025-06-28 14:24:46 +05:00
Ammar Ahmed
1592620141 mobile: release v3.2.4 2025-06-28 13:35:21 +05:00
Ammar Ahmed
28ef87a95b mobile: fix ts error 2025-06-28 13:33:24 +05:00
Ammar Ahmed
d4019eea99 mobile: fix sync taking too long to complete 2025-06-28 13:33:24 +05:00
Ammar Ahmed
d13b46cbba mobile: do not embed framework in appex on ios 2025-06-28 08:50:31 +05:00
Ammar Ahmed
f1eb215bb9 mobile: release v3.2.3 2025-06-27 14:11:28 +05:00
Ammar Ahmed
92c7e1a0c1 mobile: wrap add-reminder screen in keyboard avoiding view on ios 2025-06-27 13:54:50 +05:00
Ammar Ahmed
b81cc178b5 mobile: make add-reminder sheet a screen 2025-06-27 12:56:50 +05:00
Abdullah Atta
dd42b82dbf web: bump version to v3.2.3 2025-06-27 09:49:40 +05:00
Abdullah Atta
ca6b50081a web: remove navigating to Monographs test 2025-06-27 09:48:44 +05:00
Abdullah Atta
4fa27046bb global: update lockfiles 2025-06-27 09:44:46 +05:00
01zulfi
8cadc6eaa4 web: fix scrolling toolbar also scrolling editor (#8256)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-06-27 09:43:36 +05:00
Abdullah Atta
ccae629833 web: update sqlite3-fts5-html to 0.0.4 2025-06-27 09:43:36 +05:00
Abdullah Atta
4a1cbe632c desktop: update sqlite3-fts5-html to 0.0.4 2025-06-27 09:43:36 +05:00
Abdullah Atta
579813d93a core: update sqlite3-fts5-html to 0.0.4 2025-06-27 09:43:36 +05:00
01zulfi
00d00c02ea editor: don't clear nodes in clear formatting option (#8260)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-06-27 09:43:36 +05:00
Abdullah Atta
765949cc40 web: only subscribe to note title if its generated 2025-06-27 09:43:36 +05:00
01zulfi
87cea6a120 web: hide monograph list for not logged in users (#8258)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-06-27 09:43:36 +05:00
Ammar Ahmed
83526d4d46 mobile: fix android toolbar 2025-06-27 09:37:17 +05:00
Ammar Ahmed
fca4faa114 mobile: convert sheets with input to screens or simple modals 2025-06-27 09:36:26 +05:00
Ammar Ahmed
171628c7b9 mobile: fix sqlite fts5 2025-06-27 09:34:16 +05:00
Ammar Ahmed
d456132dd6 mobile: release v3.2.2 2025-06-21 13:24:24 +05:00
Abdullah Atta
4ac7941b6c web: bump version to v3.2.2 2025-06-21 13:03:04 +05:00
Abdullah Atta
e7f5463917 web: make trash actions multi selectable 2025-06-21 13:00:46 +05:00
Abdullah Atta
6e41ce459d common: bulk resolve notebooks & tags 2025-06-21 13:00:46 +05:00
Abdullah Atta
f5095fa643 core: fix hang when resolving recursive nested notebooks 2025-06-21 13:00:46 +05:00
Abdullah Atta
e6a5b85011 web: register fts5 extensions after db decryption 2025-06-21 13:00:46 +05:00
Abdullah Atta
409c5a2517 web: update sqlite to v3.49.2 2025-06-21 13:00:46 +05:00
Abdullah Atta
35cd6b101d desktop: update better-sqlite3-multiple-ciphers to v11.10.0 2025-06-21 13:00:46 +05:00
Ammar Ahmed
2440e28168 mobile: fix android toolbar hidden behind keyboard on android 15 2025-06-20 14:49:15 +05:00
83 changed files with 630369 additions and 3147 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "3.2.1",
"version": "3.2.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "3.2.1",
"version": "3.2.3",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
@@ -15,12 +15,12 @@
"@notesnook/ui": "file:../../packages/ui",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"better-sqlite3-multiple-ciphers": "11.3.0",
"better-sqlite3-multiple-ciphers": "^11.10.0",
"electron-trpc": "0.7.1",
"electron-updater": "^6.6.2",
"icojs": "^0.19.5",
"sqlite-better-trigram": "0.0.3",
"sqlite3-fts5-html": "^0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"typed-emitter": "^2.1.0",
"yargs": "^17.7.2",
"zod": "3.24.3"
@@ -2282,9 +2282,9 @@
"license": "MIT"
},
"node_modules/better-sqlite3-multiple-ciphers": {
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/better-sqlite3-multiple-ciphers/-/better-sqlite3-multiple-ciphers-11.3.0.tgz",
"integrity": "sha512-F0+gYaT8drCyHpujgMjS4RRAElVdAtif0uH/v4rA5cLTR5v25zDXTkj2/ie8A26Www+vT60pCF0MD+MGzIcUzw==",
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3-multiple-ciphers/-/better-sqlite3-multiple-ciphers-11.10.0.tgz",
"integrity": "sha512-/dKO3lKuJFbmuzh80uN2cmMsz8iyTskGB2l/fd9X6rt1P3EPIOvRUIxD7Qim8gLygUPB/u+db8byZGumOOdp3g==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -6296,23 +6296,23 @@
]
},
"node_modules/sqlite3-fts5-html": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html/-/sqlite3-fts5-html-0.0.3.tgz",
"integrity": "sha512-omJELqavUID4rikHgSnprzR8KoD+k34Gr//Z6Ju5ZJLI9d2MTv+63gJ/9iCWCmvt5dLODDBL25RzmXsChltjkQ==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html/-/sqlite3-fts5-html-0.0.4.tgz",
"integrity": "sha512-2d7G3QAgNtndTYRFDXVzzsjlDkjXLo9YB4xxVFIxHIotKnVTNmd0/iSQMMjOMFY5XEpW8yemFkD0EyuN0wFuSQ==",
"license": "Public Domain",
"optionalDependencies": {
"sqlite3-fts5-html-darwin-arm64": "0.0.3",
"sqlite3-fts5-html-darwin-x64": "0.0.3",
"sqlite3-fts5-html-linux-arm64": "0.0.3",
"sqlite3-fts5-html-linux-x64": "0.0.3",
"sqlite3-fts5-html-windows-arm64": "0.0.3",
"sqlite3-fts5-html-windows-x64": "0.0.3"
"sqlite3-fts5-html-darwin-arm64": "0.0.4",
"sqlite3-fts5-html-darwin-x64": "0.0.4",
"sqlite3-fts5-html-linux-arm64": "0.0.4",
"sqlite3-fts5-html-linux-x64": "0.0.4",
"sqlite3-fts5-html-windows-arm64": "0.0.4",
"sqlite3-fts5-html-windows-x64": "0.0.4"
}
},
"node_modules/sqlite3-fts5-html-darwin-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-arm64/-/sqlite3-fts5-html-darwin-arm64-0.0.3.tgz",
"integrity": "sha512-jLmBOslggCYG+N8CVbjN/ok6kP9kCkWVYiXVF+ms4NylBPevs6+QBgY4GXNcVuSoRs2T8PT/gxI3yTnjdv/uDg==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-arm64/-/sqlite3-fts5-html-darwin-arm64-0.0.4.tgz",
"integrity": "sha512-zkg+LxOtq7ONz82C4+1mxsmasOIt9tbUReEMlmu29pJosfgDsMftp9k1Jy46FEk3VDFODlg9uDX7f0O0rspayg==",
"cpu": [
"arm64"
],
@@ -6323,9 +6323,9 @@
]
},
"node_modules/sqlite3-fts5-html-darwin-x64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-x64/-/sqlite3-fts5-html-darwin-x64-0.0.3.tgz",
"integrity": "sha512-zhfkryrXf9UOS/vG9LEUs3sLZTyfgkkX316OAuSVhB0At9oa7rYc3FDiypNh6n3PMr+B3uySC6f9mipBGcsOiQ==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-x64/-/sqlite3-fts5-html-darwin-x64-0.0.4.tgz",
"integrity": "sha512-LtBRTVfB3eNkupKJEobZVOyf4TbmWw4XgdBGvi4wFHHcv96gqhVlHdUGlavul561AOpab+T7DOgaPgph1y5M5A==",
"cpu": [
"x64"
],
@@ -6336,9 +6336,9 @@
]
},
"node_modules/sqlite3-fts5-html-linux-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-arm64/-/sqlite3-fts5-html-linux-arm64-0.0.3.tgz",
"integrity": "sha512-FoEwY8I7KHQ0iOtobskKFCqIgFbi+X97h733kDgAN5B9ADK6VHL6/jpLhJLzLtlSVgPedPLTTXnhgP2abQR0eg==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-arm64/-/sqlite3-fts5-html-linux-arm64-0.0.4.tgz",
"integrity": "sha512-+xgRPpD7fdXNJPKaggHE5BkSF00aI8rlHHZ+pXZR2GmFyRr++jt34MA5avO4G6oI4mrZ82wUV+sWUqd8FErRBw==",
"cpu": [
"arm64"
],
@@ -6349,9 +6349,9 @@
]
},
"node_modules/sqlite3-fts5-html-linux-x64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-x64/-/sqlite3-fts5-html-linux-x64-0.0.3.tgz",
"integrity": "sha512-wJ2ONYC8Rs/HSWHVI2Y3j7vMh+6QzcA3SYCWAFJEFPM3aCyvf7lFhQgLSHZWkS+OUKduP+9/e/Hc7g3UwHdtig==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-x64/-/sqlite3-fts5-html-linux-x64-0.0.4.tgz",
"integrity": "sha512-4ah7IiDUH9kpCPoqT4lW6FcWcNiVF0wtpywW82UVfkawbmgLFPGqSQFqHyChmp1b2dsrkO5X/bhq9E2BR0/xmQ==",
"cpu": [
"x64"
],
@@ -6362,9 +6362,9 @@
]
},
"node_modules/sqlite3-fts5-html-windows-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-arm64/-/sqlite3-fts5-html-windows-arm64-0.0.3.tgz",
"integrity": "sha512-58Qi1NTe/Po+nWz2z24ziSvghQcrMC94/mxmErNeC4SFPZMN7JLx56W2eybJB436CiUUUN9MT4a6BD1bn7RpQg==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-arm64/-/sqlite3-fts5-html-windows-arm64-0.0.4.tgz",
"integrity": "sha512-rQKLrX90TKIMgD7zSrOk/U6q0KEBiTaB2nXDONcK4d/CYpnG9g/N/i6Ady98j5+v/PgqymchgtRwvgBHOZzCfw==",
"cpu": [
"arm64"
],
@@ -6375,9 +6375,9 @@
]
},
"node_modules/sqlite3-fts5-html-windows-x64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-x64/-/sqlite3-fts5-html-windows-x64-0.0.3.tgz",
"integrity": "sha512-LYilVGRYXpN0nra3LL7CXlfaLbPmBK78rtaG9Aivufqexqjc2sB+v+xMGT8vxLXOjIAtkBLko2ELuYe8hRLQMw==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-x64/-/sqlite3-fts5-html-windows-x64-0.0.4.tgz",
"integrity": "sha512-FxLloBxWtsIGUMxbvOLNY9+FXJYEcku1xVqJarLfGKcXX7zvS3yXMP0dYeld2lQ/evNiLgvLMNLhUFwhEln1vA==",
"cpu": [
"x64"
],

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.2.1",
"version": "3.2.3",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/cjs/index.js",
@@ -30,12 +30,12 @@
"@notesnook/ui": "file:../../packages/ui",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"better-sqlite3-multiple-ciphers": "11.3.0",
"better-sqlite3-multiple-ciphers": "^11.10.0",
"electron-trpc": "0.7.1",
"electron-updater": "^6.6.2",
"icojs": "^0.19.5",
"sqlite-better-trigram": "0.0.3",
"sqlite3-fts5-html": "^0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"typed-emitter": "^2.1.0",
"yargs": "^17.7.2",
"zod": "3.24.3"

View File

@@ -1,5 +1,5 @@
diff --git a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
index 1a14ecd..dabcce2 100644
index 1a14ecd..ff938f1 100644
--- a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
+++ b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
@@ -38,5 +38,6 @@

View File

@@ -33,6 +33,8 @@ export class SQLite {
initialized = false;
preparedStatements: Map<string, Statement<unknown[]>> = new Map();
retryCounter: Record<string, number> = {};
extensionsLoaded = false;
constructor() {
console.log("new sqlite worker");
}
@@ -46,10 +48,6 @@ export class SQLite {
this.sqlite = require("better-sqlite3-multiple-ciphers")(
filePath
).unsafeMode(true);
const betterTrigram = require("sqlite-better-trigram");
const fts5Html = require("sqlite3-fts5-html");
betterTrigram.load(this.sqlite);
fts5Html.load(this.sqlite);
}
/**
@@ -117,6 +115,20 @@ export class SQLite {
} catch (e) {
if (e instanceof Error) e.message += ` (query: ${sql})`;
throw e;
} finally {
// Since SQLite 3.48.0 (SQLite3MC v2.0.2) it's not possible to load fts5
// extensions before database has been decrypting. This is because
// executing a `SELECT` now accesses the underlying databases resulting in
// an error. Since FTS5 extensions depend on `SELECT fts5` to load the
// fts5 API, we must wait decrypt the database before we can load
// the extensions.
if (!this.extensionsLoaded && (await this.isDatabaseReady())) {
const betterTrigram = require("sqlite-better-trigram");
const fts5Html = require("sqlite3-fts5-html");
betterTrigram.load(this.sqlite);
fts5Html.load(this.sqlite);
this.extensionsLoaded = true;
}
}
}
@@ -144,4 +156,22 @@ export class SQLite {
retryDelay: 500
});
}
/**
* This just executes `SELECT 1` on the database to make sure its ready.
* On an encrypted database, this will fail until `PRAGMA key` has been
* called.
*/
private async isDatabaseReady() {
// return this.exec(`SELECT 1;`)
// .then(() => true)
// .catch(() => false);
if (!this.sqlite) return false;
try {
this.sqlite.prepare(`SELECT 1;`).run();
return true;
} catch {
return false;
}
}
}

View File

@@ -17,26 +17,20 @@ 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 { strings } from "@notesnook/intl";
import React, { useRef, useState } from "react";
import { View } from "react-native";
import { db } from "../../common/database";
import {
eSendEvent,
presentSheet,
ToastManager
} from "../../services/event-manager";
import BackupService from "../../services/backup";
import { eSendEvent, ToastManager } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet, eOpenRecoveryKeyDialog } from "../../utils/events";
import DialogHeader from "../dialog/dialog-header";
import { eOpenRecoveryKeyDialog } from "../../utils/events";
import { DefaultAppStyles } from "../../utils/styles";
import { Dialog } from "../dialog";
import { Button } from "../ui/button";
import Input from "../ui/input";
import { Notice } from "../ui/notice";
import Seperator from "../ui/seperator";
import { Dialog } from "../dialog";
import BackupService from "../../services/backup";
import { sleep } from "../../utils/time";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../utils/styles";
export const ChangePassword = () => {
const passwordInputRef = useRef();
@@ -85,8 +79,7 @@ export const ChangePassword = () => {
context: "global"
});
setLoading(false);
eSendEvent(eCloseSheet);
await sleep(300);
Navigation.goBack();
eSendEvent(eOpenRecoveryKeyDialog);
} catch (e) {
setLoading(false);
@@ -108,9 +101,6 @@ export const ChangePassword = () => {
}}
>
<Dialog context="change-password-dialog" />
<DialogHeader title={strings.changePassword()} />
<Seperator />
<Input
fwdRef={oldPasswordInputRef}
onChangeText={(value) => {
@@ -160,9 +150,3 @@ export const ChangePassword = () => {
</View>
);
};
ChangePassword.present = () => {
presentSheet({
component: <ChangePassword />
});
};

View File

@@ -39,6 +39,7 @@ import { hideAuth } from "./common";
import { ForgotPassword } from "./forgot-password";
import { useLogin } from "./use-login";
import { DefaultAppStyles } from "../../utils/styles";
import { Dialog } from "../dialog";
const LoginSteps = {
emailAuth: 1,
@@ -89,7 +90,7 @@ export const Login = ({ changeMode }) => {
return (
<>
<ForgotPassword />
<SheetProvider context="two_factor_verify" />
<Dialog context="two_factor_verify" />
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,

View File

@@ -24,12 +24,8 @@ import { View } from "react-native";
import { ScrollView } from "react-native-actions-sheet";
import { db } from "../../common/database/index";
import useTimer from "../../hooks/use-timer";
import {
eSendEvent,
presentSheet,
ToastManager
} from "../../services/event-manager";
import { eCloseSheet } from "../../utils/events";
import { eSendEvent, ToastManager } from "../../services/event-manager";
import { eCloseSimpleDialog } from "../../utils/events";
import { AppFontSize } from "../../utils/size";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
@@ -38,6 +34,7 @@ import { Pressable } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { DefaultAppStyles } from "../../utils/styles";
import { presentDialog } from "../dialog/functions";
const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
const { colors } = useThemeColors();
@@ -46,7 +43,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
method: mfaInfo?.primaryMethod,
isPrimary: true
});
const { seconds, start } = useTimer(currentMethod.method);
const { seconds, start, reset } = useTimer(currentMethod.method);
const [loading, setLoading] = useState(false);
const inputRef = useRef();
const [sending, setSending] = useState(false);
@@ -62,7 +59,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
},
(result) => {
if (result) {
eSendEvent(eCloseSheet, "two_factor_verify");
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
}
setLoading(false);
}
@@ -133,6 +130,12 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
<ScrollView
keyboardShouldPersistTaps="handled"
keyboardDismissMode="interactive"
style={{
width: "100%",
height: "100%",
backgroundColor: colors.primary.background,
paddingTop: 60
}}
>
<View
style={{
@@ -236,7 +239,10 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
<Button
title={strings.cancel()}
type="secondaryAccented"
onPress={onCancel}
onPress={() => {
reset();
onCancel();
}}
width={250}
/>
@@ -297,7 +303,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
};
TwoFactorVerification.present = (onMfaLogin, data, onCancel, context) => {
presentSheet({
presentDialog({
component: () => (
<TwoFactorVerification
onMfaLogin={onMfaLogin}
@@ -306,7 +312,9 @@ TwoFactorVerification.present = (onMfaLogin, data, onCancel, context) => {
/>
),
context: context || "two_factor_verify",
disableClosing: true
disableClosing: true,
transparent: false,
statusBarTranslucent: true
});
};

View File

@@ -24,7 +24,7 @@ import { clearMessage } from "../../services/message";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet } from "../../utils/events";
import { eCloseSimpleDialog } from "../../utils/events";
import TwoFactorVerification from "./two-factor";
import { strings } from "@notesnook/intl";
@@ -92,7 +92,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
} catch (e) {
callback && callback(false);
if (e.message === "invalid_grant") {
eSendEvent(eCloseSheet, "two_factor_verify");
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
setLoading(false);
setStep(LoginSteps.emailAuth);
}
@@ -100,7 +100,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
},
mfaInfo,
() => {
eSendEvent(eCloseSheet, "two_factor_verify");
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
setLoading(false);
setStep(LoginSteps.emailAuth);
}

View File

@@ -36,6 +36,7 @@ import { getElevationStyle } from "../../utils/elevation";
import { AppFontSize, normalize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { hexToRGBA, RGB_Linear_Shade } from "../../utils/colors";
import useKeyboard from "../../hooks/use-keyboard";
interface FloatingButtonProps {
onPress: () => void;

View File

@@ -21,7 +21,7 @@ import React from "react";
import { Text, View, ViewStyle } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import { AppFontSize } from "../../utils/size";
import { Button } from "../ui/button";
import { Button, ButtonProps } from "../ui/button";
import { PressableProps } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
@@ -31,13 +31,7 @@ type DialogHeaderProps = {
icon?: string;
title?: string;
paragraph?: string;
button?: {
onPress?: () => void;
loading?: boolean;
title?: string;
type?: PressableProps["type"];
icon?: string;
};
button?: ButtonProps;
paragraphColor?: string;
padding?: number;
centered?: boolean;
@@ -95,17 +89,14 @@ const DialogHeader = ({
{button ? (
<Button
onPress={button.onPress}
style={{
borderRadius: 100,
paddingHorizontal: DefaultAppStyles.GAP
}}
loading={button.loading}
fontSize={13}
title={button.title}
icon={button.icon}
type={button.type || "secondary"}
height={30}
{...button}
/>
) : null}
</View>

View File

@@ -27,6 +27,9 @@ export type DialogInfo = {
paragraph?: string;
positiveText: string;
negativeText: string;
background?: string;
transparent?: boolean;
statusBarTranslucent?: boolean;
positivePress?: (...args: any[]) => Promise<any>;
onClose?: () => void;
positiveType?:

View File

@@ -130,9 +130,17 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
return visible && dialogInfo ? (
<BaseDialog
statusBarTranslucent={false}
statusBarTranslucent={
dialogInfo.statusBarTranslucent === undefined
? false
: dialogInfo.statusBarTranslucent
}
bounce={!dialogInfo.input}
closeOnTouch={!dialogInfo.disableBackdropClosing}
background={dialogInfo.background}
transparent={
dialogInfo.transparent === undefined ? true : dialogInfo.transparent
}
onShow={async () => {
if (dialogInfo.input) {
inputRef.current?.setNativeProps({

View File

@@ -17,12 +17,15 @@ 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 { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { Dimensions, TextInput, View } from "react-native";
import Orientation from "react-native-orientation-locker";
import Pdf from "react-native-pdf";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import { MMKV } from "../../../common/database/mmkv";
import downloadAttachment from "../../../common/filesystem/download-attachment";
import { deleteCacheFileByPath, exists } from "../../../common/filesystem/io";
import { cacheDir } from "../../../common/filesystem/utils";
import { useAttachmentProgress } from "../../../hooks/use-attachment-progress";
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
@@ -30,8 +33,9 @@ import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import BaseDialog from "../../dialog/base-dialog";
import { presentDialog } from "../../dialog/functions";
@@ -39,11 +43,6 @@ import SheetProvider from "../../sheet-provider";
import { IconButton } from "../../ui/icon-button";
import { ProgressBarComponent } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
import { sleep } from "../../../utils/time";
import { MMKV } from "../../../common/database/mmkv";
import { deleteCacheFileByPath } from "../../../common/filesystem/io";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../../utils/styles";
const WIN_WIDTH = Dimensions.get("window").width;
const WIN_HEIGHT = Dimensions.get("window").height;
@@ -105,15 +104,19 @@ const PDFPreview = () => {
const open = useCallback(
async (attachment) => {
setVisible(true);
setLoading(true);
setTimeout(async () => {
setAttachment(attachment);
let hash = attachment.hash;
if (!hash) return;
if (!(await exists(hash))) setLoading(true);
const uri = await downloadAttachment(hash, false, {
silent: true,
cache: true
});
if (!(await exists(hash))) {
setVisible(false);
return;
}
const path = `${cacheDir}/${uri}`;
snapshotValue.current = snapshot.current;
setPDFSource("file://" + path);
@@ -166,8 +169,7 @@ const PDFPreview = () => {
}}
>
{loading ? (
<Animated.View
exiting={FadeOut}
<View
style={{
flex: 1,
justifyContent: "center",
@@ -188,7 +190,7 @@ const PDFPreview = () => {
>
{strings.loadingWithProgress(progress?.percent)}
</Paragraph>
</Animated.View>
</View>
) : (
<>
<View
@@ -270,49 +272,42 @@ const PDFPreview = () => {
</View>
</View>
{pdfSource ? (
<Animated.View
style={{
flex: 1
<Pdf
source={{
uri: pdfSource
}}
entering={FadeIn}
>
<Pdf
source={{
uri: pdfSource
}}
ref={pdfRef}
onLoadComplete={(numberOfPages) => {
setNumPages(numberOfPages);
}}
onPageChanged={(page) => {
setCurrentPage(page);
inputRef.current?.setNativeProps({
text: page + ""
});
saveSnapshot({
currentPage: page,
scale: snapshot?.current?.scale
});
}}
// scale={snapshotValue.current?.scale}
// onScaleChanged={(scale) => {
// saveSnapshot({
// currentPage: snapshot?.current?.currentPage,
// scale: scale
// });
// }}
page={snapshotValue?.current?.currentPage}
password={password}
maxScale={6}
onError={onError}
onPressLink={(uri) => {}}
style={{
flex: 1,
width: width,
height: Dimensions.get("window").height
}}
/>
</Animated.View>
ref={pdfRef}
onLoadComplete={(numberOfPages) => {
setNumPages(numberOfPages);
}}
onPageChanged={(page) => {
setCurrentPage(page);
inputRef.current?.setNativeProps({
text: page + ""
});
saveSnapshot({
currentPage: page,
scale: snapshot?.current?.scale
});
}}
// scale={snapshotValue.current?.scale}
// onScaleChanged={(scale) => {
// saveSnapshot({
// currentPage: snapshot?.current?.currentPage,
// scale: scale
// });
// }}
page={snapshotValue?.current?.currentPage}
password={password}
maxScale={6}
onError={onError}
onPressLink={(uri) => {}}
style={{
flex: 1,
width: width,
height: Dimensions.get("window").height
}}
/>
) : null}
</>
)}

View File

@@ -17,24 +17,26 @@ 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 { Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
import useIsSelected from "../../../hooks/use-selected";
import AddReminder from "../../../screens/add-reminder";
import { eSendEvent } from "../../../services/event-manager";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { eCloseSheet } from "../../../utils/events";
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { Properties } from "../../properties";
import ReminderSheet from "../../sheets/reminder";
import AppIcon from "../../ui/AppIcon";
import { IconButton } from "../../ui/icon-button";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
import { strings } from "@notesnook/intl";
import { useSelectionStore } from "../../../stores/use-selection-store";
import useIsSelected from "../../../hooks/use-selected";
import AppIcon from "../../ui/AppIcon";
import { DefaultAppStyles } from "../../../utils/styles";
const ReminderItem = React.memo(
({
@@ -49,8 +51,10 @@ const ReminderItem = React.memo(
const { colors } = useThemeColors();
const openReminder = () => {
if (selectItem(item)) return;
ReminderSheet.present(item, undefined, isSheet);
AddReminder.present(item, undefined);
if (isSheet) {
eSendEvent(eCloseSheet);
}
};
const selectionMode = useSelectionStore((state) => state.selectionMode);
const [selected] = useIsSelected(item);

View File

@@ -34,7 +34,7 @@ import { AppFontSize } from "../../../utils/size";
import DialogHeader from "../../dialog/dialog-header";
import List from "../../list";
import SheetProvider from "../../sheet-provider";
import { Button } from "../../ui/button";
import { Button, ButtonProps } from "../../ui/button";
import { PressableProps } from "../../ui/pressable";
import Paragraph from "../../ui/typography/paragraph";
import { DefaultAppStyles } from "../../../utils/styles";
@@ -47,18 +47,10 @@ type RelationsListProps = {
referenceType: string;
relationType: "to" | "from";
title: string;
button?: Button;
button?: ButtonProps;
onAdd: () => void;
};
type Button = {
onPress?: (() => void) | undefined;
loading?: boolean | undefined;
title?: string | undefined;
type?: PressableProps["type"];
icon?: string;
};
const IconsByType = {
reminder: "bell"
};
@@ -154,7 +146,7 @@ RelationsList.present = ({
referenceType: string;
relationType: "to" | "from";
title: string;
button?: Button;
button?: ButtonProps;
onAdd: () => void;
}) => {
presentSheet({

View File

@@ -1,630 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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 { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useRef, useState } from "react";
import {
Platform,
TextInput,
View,
ScrollView as RNScrollView
} from "react-native";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
PresentSheetOptions,
ToastManager,
presentSheet
} from "../../../services/event-manager";
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import dayjs from "dayjs";
import DatePicker from "react-native-date-picker";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import Navigation from "../../../services/navigation";
import Notifications from "../../../services/notifications";
import PremiumService from "../../../services/premium";
import SettingsService from "../../../services/settings";
import { useRelationStore } from "../../../stores/use-relation-store";
import { Dialog } from "../../dialog";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { Note, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../../utils/styles";
type ReminderSheetProps = {
actionSheetRef: RefObject<ActionSheetRef>;
close?: (ctx?: string) => void;
update?: (options: PresentSheetOptions) => void;
reminder?: Reminder;
reference?: Note;
};
const ReminderModes =
Platform.OS === "ios"
? {
Once: "once",
Repeat: "repeat"
}
: {
Once: "once",
Repeat: "repeat",
Permanent: "permanent"
};
const RecurringModes = {
Daily: "day",
Week: "week",
Month: "month",
Year: "year"
};
const WeekDays = new Array(7).fill(true);
const MonthDays = new Array(31).fill(true);
const WeekDayNames = {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday"
};
const ReminderNotificationModes = {
Silent: "silent",
Vibrate: "vibrate",
Urgent: "urgent"
};
export default function ReminderSheet({
actionSheetRef,
close,
update,
reminder,
reference
}: ReminderSheetProps) {
const { colors, isDark } = useThemeColors();
const [reminderMode, setReminderMode] = useState<Reminder["mode"]>(
reminder?.mode || "once"
);
const [recurringMode, setRecurringMode] = useState<Reminder["recurringMode"]>(
reminder?.recurringMode || "week"
);
const [selectedDays, setSelectedDays] = useState<number[]>(
reminder?.selectedDays || []
);
const [date, setDate] = useState<Date>(
new Date(reminder?.date || Date.now())
);
const [reminderNotificationMode, setReminderNotificatioMode] = useState<
Reminder["priority"]
>(reminder?.priority || SettingsService.get().reminderNotificationMode);
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const [repeatFrequency, setRepeatFrequency] = useState(1);
const referencedItem = reference ? (reference as Note) : null;
const title = useRef<string | undefined>(
!reminder ? referencedItem?.title : reminder?.title
);
const details = useRef<string | undefined>(
!reminder ? referencedItem?.headline : reminder?.description
);
const titleRef = useRef<TextInput>(null);
const timer = useRef<NodeJS.Timeout>();
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = (date: Date) => {
timer.current = setTimeout(() => {
hideDatePicker();
setDate(date);
}, 50);
};
function nth(n: number) {
return (
["st", "nd", "rd"][(((((n < 0 ? -n : n) + 90) % 100) - 10) % 10) - 1] ||
"th"
);
}
function getSelectedDaysText(selectedDays: number[]) {
const text = selectedDays
.sort((a, b) => a - b)
.map((day, index) => {
const isLast = index === selectedDays.length - 1;
const isSecondLast = index === selectedDays.length - 2;
const joinWith = isSecondLast ? " & " : isLast ? "" : ", ";
return recurringMode === RecurringModes.Week
? WeekDayNames[day as keyof typeof WeekDayNames] + joinWith
: `${day}${nth(day)} ${joinWith}`;
})
.join("");
return text;
}
async function saveReminder() {
try {
if (!(await Notifications.checkAndRequestPermissions(true)))
throw new Error(strings.noNotificationPermission());
if (!date && reminderMode !== ReminderModes.Permanent) return;
if (
reminderMode === ReminderModes.Repeat &&
recurringMode !== "day" &&
recurringMode !== "year" &&
selectedDays.length === 0
)
throw new Error(strings.selectDayError());
if (!title.current) throw new Error(strings.setTitleError());
if (date.getTime() < Date.now() && reminderMode === "once") {
titleRef?.current?.focus();
throw new Error(strings.dateError());
}
date.setSeconds(0, 0);
const reminderId = await db.reminders?.add({
id: reminder?.id,
date: date?.getTime(),
priority: reminderNotificationMode,
title: title.current,
description: details.current,
recurringMode: recurringMode,
selectedDays: selectedDays,
mode: reminderMode,
localOnly: reminderMode === "permanent",
snoozeUntil:
date?.getTime() > Date.now() ? undefined : reminder?.snoozeUntil,
disabled: false
});
if (!reminderId) return;
const _reminder = await db.reminders?.reminder(reminderId);
if (reference && _reminder) {
await db.relations?.add(reference, {
id: _reminder?.id as string,
type: _reminder?.type
});
}
Notifications.scheduleNotification(_reminder as Reminder);
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
close?.();
} catch (e) {
ToastManager.error(e as Error, undefined, "local");
}
}
return (
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
maxHeight: "100%"
}}
>
<Heading size={AppFontSize.lg}>
{reminder ? strings.editReminder() : strings.newReminder()}
</Heading>
<Dialog context="local" />
<ScrollView
bounces={false}
style={{
marginBottom: DDS.isTab ? 25 : undefined
}}
>
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
placeholder={strings.remindeMeOf()}
onChangeText={(text) => (title.current = text)}
wrapperStyle={{
marginTop: DefaultAppStyles.GAP_VERTICAL
}}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
placeholder={strings.addShortNote()}
onChangeText={(text) => (details.current = text)}
containerStyle={{
maxHeight: 80
}}
multiline
textAlignVertical="top"
inputStyle={{
minHeight: 80,
paddingVertical: DefaultAppStyles.GAP_VERTICAL
}}
height={80}
wrapperStyle={{
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
/>
<ScrollView
style={{
flexDirection: "row",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
height: 50
}}
horizontal
>
{Object.keys(ReminderModes).map((mode) => (
<Button
key={mode}
title={strings.reminderModes(
ReminderModes[mode as keyof typeof ReminderModes] as string
)}
style={{
marginRight: 12,
borderRadius: 100,
minWidth: 70,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
proTag={mode === "Repeat"}
height={35}
type={
reminderMode ===
ReminderModes[mode as keyof typeof ReminderModes]
? "selected"
: "plain"
}
onPress={() => {
if (mode === "Repeat" && !PremiumService.get()) return;
setReminderMode(
ReminderModes[
mode as keyof typeof ReminderModes
] as Reminder["mode"]
);
if (mode === "Repeat") {
setSelectedDays((days) => {
if (days.length > 0) return days;
if (days.indexOf(date.getDay()) > -1) {
return days;
}
days.push(date.getDay());
return [...days];
});
}
}}
/>
))}
</ScrollView>
{reminderMode === ReminderModes.Repeat ? (
<View
style={{
backgroundColor: colors.secondary.background,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius,
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<View
style={{
flexDirection: "row",
marginBottom:
recurringMode === "day" || recurringMode === "year" ? 0 : 12,
alignItems: "center"
}}
>
{Object.keys(RecurringModes).map((mode) => (
<Button
key={mode}
title={strings.recurringModes(
RecurringModes[mode as keyof typeof RecurringModes]
)}
style={{
marginRight: 6,
borderRadius: 100,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
type={
recurringMode ===
RecurringModes[mode as keyof typeof RecurringModes]
? "selected"
: "plain"
}
onPress={() => {
setRecurringMode(
RecurringModes[
mode as keyof typeof RecurringModes
] as Reminder["recurringMode"]
);
setSelectedDays([]);
setRepeatFrequency(1);
}}
/>
))}
</View>
<RNScrollView showsHorizontalScrollIndicator={false} horizontal>
{recurringMode === RecurringModes.Daily ||
recurringMode === RecurringModes.Year
? null
: recurringMode === RecurringModes.Week
? WeekDays.map((item, index) => (
<Button
key={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
title={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
type={
selectedDays.indexOf(index) > -1 ? "selected" : "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index) > -1) {
days.splice(days.indexOf(index), 1);
return [...days];
}
days.push(index);
return [...days];
});
}}
/>
))
: MonthDays.map((item, index) => (
<Button
key={index + "monthday"}
title={index + 1 + ""}
type={
selectedDays.indexOf(index + 1) > -1
? "selected"
: "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index + 1) > -1) {
days.splice(days.indexOf(index + 1), 1);
return [...days];
}
days.push(index + 1);
return [...days];
});
}}
/>
))}
</RNScrollView>
</View>
) : null}
{reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
width: "100%",
flexDirection: "column",
justifyContent: "center",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
alignItems: "center"
}}
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
is24Hour={db.settings.getTimeFormat() === "24-hour"}
date={date || new Date(Date.now())}
/>
<DatePicker
date={date}
maximumDate={dayjs(date).add(3, "months").toDate()}
onDateChange={handleConfirm}
textColor={isDark ? colors.static.white : colors.static.black}
fadeToColor={colors.primary.background}
theme={isDark ? "dark" : "light"}
androidVariant="nativeAndroid"
is24hourSource="locale"
locale={
db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
}
mode={
reminderMode === ReminderModes.Repeat &&
recurringMode !== "year"
? "time"
: "datetime"
}
/>
{reminderMode === ReminderModes.Repeat ? null : (
<Button
style={{
width: "100%"
}}
title={date ? date.toLocaleDateString() : strings.selectDate()}
type={date ? "secondaryAccented" : "secondary"}
icon="calendar"
fontSize={AppFontSize.sm}
onPress={() => {
showDatePicker();
}}
/>
)}
</View>
)}
{reminderMode === ReminderModes.Once ||
reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
borderRadius: defaultBorderRadius,
flexDirection: "row",
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignItems: "center",
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<>
<Paragraph
size={AppFontSize.xxs}
color={colors.secondary.paragraph}
>
{recurringMode === RecurringModes.Daily
? strings.reminderRepeatStrings.day(
dayjs(date).format("hh:mm A")
)
: recurringMode === RecurringModes.Year
? strings.reminderRepeatStrings.year(
dayjs(date).format("dddd, MMMM D, h:mm A")
)
: selectedDays.length === 7 &&
recurringMode === RecurringModes.Week
? strings.reminderRepeatStrings.week.daily(
dayjs(date).format("hh:mm A")
)
: selectedDays.length === 0
? strings.reminderRepeatStrings[
recurringMode as "week" | "month"
].selectDays()
: strings.reminderRepeatStrings.repeats(
repeatFrequency,
recurringMode as string,
getSelectedDaysText(selectedDays),
dayjs(date).format("hh:mm A")
)}
</Paragraph>
</>
</View>
)}
<ReminderTime
reminder={reminder}
style={{
width: "100%",
justifyContent: "flex-start",
borderWidth: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignSelf: "flex-start"
}}
/>
{reminderMode === ReminderModes.Permanent ? null : (
<RNScrollView
style={{
flexDirection: "row",
marginTop: DefaultAppStyles.GAP_VERTICAL,
height: 50
}}
horizontal
>
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={strings.reminderNotificationModes(
mode as keyof typeof ReminderNotificationModes
)}
style={{
marginRight: 12,
borderRadius: 100,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
icon={
mode === "Silent"
? "minus-circle"
: mode === "Vibrate"
? "vibrate"
: "volume-high"
}
height={35}
type={
reminderNotificationMode ===
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "selected"
: "plain"
}
onPress={() => {
const _mode = ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
] as Reminder["priority"];
SettingsService.set({
reminderNotificationMode: _mode
});
setReminderNotificatioMode(_mode);
}}
/>
))}
</RNScrollView>
)}
</ScrollView>
<Button
title={strings.save()}
type="accent"
style={{
paddingHorizontal: DefaultAppStyles.GAP * 2,
marginTop: DefaultAppStyles.GAP_VERTICAL,
width: "100%"
}}
onPress={saveReminder}
/>
</View>
);
}
ReminderSheet.present = (
reminder?: Reminder,
reference?: Note,
isSheet?: boolean
) => {
presentSheet({
context: isSheet ? "local" : undefined,
component: (ref, close, update) => (
<ReminderSheet
actionSheetRef={ref}
close={close}
update={update}
reminder={reminder}
reference={reference}
/>
)
});
};

View File

@@ -40,6 +40,7 @@ import Sync from "../../../services/sync";
import Clipboard from "@react-native-clipboard/clipboard";
import { logoutUser } from "../../../screens/settings/logout";
import { sleep } from "../../../utils/time";
export const UserSheet = () => {
const ref = useSheetRef();
const { colors } = useThemeColors();
@@ -310,10 +311,11 @@ export const UserSheet = () => {
{
icon: "logout",
title: strings.logout(),
onPress: () => {
onPress: async () => {
console.log("logout");
logoutUser();
ref.current?.hide();
await sleep(300);
logoutUser();
},
hidden: !user
}

View File

@@ -41,7 +41,6 @@ import ExportNotesSheet from "../components/sheets/export-notes";
import PublishNoteSheet from "../components/sheets/publish-note";
import { ReferencesList } from "../components/sheets/references";
import { RelationsList } from "../components/sheets/relations-list/index";
import ReminderSheet from "../components/sheets/reminder";
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
import { ButtonProps } from "../components/ui/button";
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
@@ -54,19 +53,20 @@ import {
} from "../services/event-manager";
import Navigation from "../services/navigation";
import Notifications from "../services/notifications";
import SettingsService from "../services/settings";
import { useArchivedStore } from "../stores/use-archived-store";
import { useMenuStore } from "../stores/use-menu-store";
import useNavigationStore from "../stores/use-navigation-store";
import { useRelationStore } from "../stores/use-relation-store";
import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { useTagStore } from "../stores/use-tag-store";
import { useUserStore } from "../stores/use-user-store";
import { eUpdateNoteInEditor } from "../utils/events";
import { deleteItems } from "../utils/functions";
import { convertNoteToText } from "../utils/note-to-text";
import { sleep } from "../utils/time";
import SettingsService from "../services/settings";
import { useSettingStore } from "../stores/use-setting-store";
import { useArchivedStore } from "../stores/use-archived-store";
import AddReminder from "../screens/add-reminder";
export type ActionId =
| "select"
@@ -448,7 +448,8 @@ export const useActions = ({
title: strings.editReminder(),
icon: "pencil",
onPress: async () => {
ReminderSheet.present(item);
AddReminder.present(item);
close();
}
}
);
@@ -908,12 +909,18 @@ export const useActions = ({
referenceType: "reminder",
relationType: "from",
title: strings.dataTypesPluralCamelCase.reminder(),
onAdd: () => ReminderSheet.present(undefined, item, true),
onAdd: () => {
AddReminder.present(undefined, item);
close();
},
button: {
title: strings.add(),
type: "accent",
onPress: () => ReminderSheet.present(undefined, item, true),
icon: "plus"
type: "plain",
onPress: () => {
AddReminder.present(undefined, item);
close();
},
icon: "plus",
iconSize: 20
}
});
}
@@ -957,7 +964,8 @@ export const useActions = ({
title: strings.remindMe(),
icon: "clock-plus-outline",
onPress: () => {
ReminderSheet.present(undefined, item);
close();
AddReminder.present(undefined, item);
}
},
{

View File

@@ -49,7 +49,6 @@ import { MMKV } from "../common/database/mmkv";
import { endProgress, startProgress } from "../components/dialogs/progress";
import Migrate from "../components/sheets/migrate";
import NewFeature from "../components/sheets/new-feature";
import ReminderSheet from "../components/sheets/reminder";
import { Walkthrough } from "../components/walkthroughs";
import {
resetTabStore,
@@ -103,6 +102,7 @@ import { getGithubVersion } from "../utils/github-version";
import { fluidTabsRef } from "../utils/global-refs";
import { NotesnookModule } from "../utils/notesnook-module";
import { sleep } from "../utils/time";
import AddReminder from "../screens/add-reminder";
const onCheckSyncStatus = async (type: SyncStatusEvent) => {
const { disableSync, disableAutoSync } = SettingsService.get();
@@ -182,10 +182,10 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
const id = new URL(url).searchParams.get("id");
if (id) {
const reminder = await db.reminders.reminder(id);
if (reminder) ReminderSheet.present(reminder);
if (reminder) AddReminder.present(reminder);
}
} else if (url.startsWith("https://notesnook.com/new_reminder")) {
ReminderSheet.present();
AddReminder.present();
}
} catch (e) {
console.error(e);

View File

@@ -52,7 +52,14 @@ const useTimer = (initialId?: string) => {
};
}, [seconds, id]);
return { seconds, setId, start };
const reset = () => {
if (id) {
timers[id] = 0;
setSeconds(0);
}
};
return { seconds, setId, start, reset };
};
export default useTimer;

View File

@@ -530,17 +530,11 @@ const onChangeTab = async (event: { i: number; from: number }) => {
activateKeepAwake();
eSendEvent(eOnEnterEditor);
if (!useTabStore.getState().getCurrentNoteId()) {
eSendEvent(eOnLoadNote, {
newNote: true
});
} else {
if (
useTabStore.getState().getTab(useTabStore.getState().currentTab)
?.session?.locked
) {
eSendEvent(eUnlockNote);
}
if (
useTabStore.getState().getTab(useTabStore.getState().currentTab)?.session
?.locked
) {
eSendEvent(eUnlockNote);
}
} else {
if (event.from === 2) {

View File

@@ -21,6 +21,7 @@ import { useThemeColors } from "@notesnook/theme";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import * as React from "react";
import { db } from "../common/database";
import { hideAllTooltips } from "../hooks/use-tooltip";
import SettingsService from "../services/settings";
import useNavigationStore, {
@@ -29,7 +30,6 @@ import useNavigationStore, {
import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { rootNavigatorRef } from "../utils/global-refs";
import { db } from "../common/database";
const RootStack = createNativeStackNavigator();
const AppStack = createNativeStackNavigator();
@@ -248,6 +248,7 @@ let MoveNotebook: any = null;
let MoveNotes: any = null;
let Settings: any = null;
let ManageTags: any = null;
let AddReminder: any = null;
export const RootNavigation = () => {
const introCompleted = useSettingStore(
@@ -336,6 +337,15 @@ export const RootNavigation = () => {
return ManageTags;
}}
/>
<RootStack.Screen
name="AddReminder"
getComponent={() => {
AddReminder =
AddReminder || require("../screens/add-reminder").default;
return AddReminder;
}}
/>
</RootStack.Navigator>
</NavigationContainer>
);

View File

@@ -0,0 +1,608 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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 { Note, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import dayjs from "dayjs";
import React, { useRef, useState } from "react";
import {
KeyboardAvoidingView,
Platform,
ScrollView,
TextInput,
View
} from "react-native";
import DatePicker from "react-native-date-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../../common/database";
import { Dialog } from "../../components/dialog";
import { Header } from "../../components/header";
import { Button } from "../../components/ui/button";
import Input from "../../components/ui/input";
import { ReminderTime } from "../../components/ui/reminder-time";
import Paragraph from "../../components/ui/typography/paragraph";
import { DDS } from "../../services/device-detection";
import { ToastManager } from "../../services/event-manager";
import Navigation, { NavigationProps } from "../../services/navigation";
import Notifications from "../../services/notifications";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
import { useRelationStore } from "../../stores/use-relation-store";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
const ReminderModes =
Platform.OS === "ios"
? {
Once: "once",
Repeat: "repeat"
}
: {
Once: "once",
Repeat: "repeat",
Permanent: "permanent"
};
const RecurringModes = {
Daily: "day",
Week: "week",
Month: "month",
Year: "year"
};
const WeekDays = new Array(7).fill(true);
const MonthDays = new Array(31).fill(true);
const WeekDayNames = {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday"
};
const ReminderNotificationModes = {
Silent: "silent",
Vibrate: "vibrate",
Urgent: "urgent"
};
export default function AddReminder(props: NavigationProps<"AddReminder">) {
const { reminder, reference } = props.route.params;
const { colors, isDark } = useThemeColors();
const [reminderMode, setReminderMode] = useState<Reminder["mode"]>(
reminder?.mode || "once"
);
const [recurringMode, setRecurringMode] = useState<Reminder["recurringMode"]>(
reminder?.recurringMode || "week"
);
const [selectedDays, setSelectedDays] = useState<number[]>(
reminder?.selectedDays || []
);
const [date, setDate] = useState<Date>(
new Date(reminder?.date || Date.now())
);
const [reminderNotificationMode, setReminderNotificatioMode] = useState<
Reminder["priority"]
>(reminder?.priority || SettingsService.get().reminderNotificationMode);
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const [repeatFrequency, setRepeatFrequency] = useState(1);
const referencedItem = reference ? (reference as Note) : null;
const title = useRef<string | undefined>(
!reminder ? referencedItem?.title : reminder?.title
);
const details = useRef<string | undefined>(
!reminder ? referencedItem?.headline : reminder?.description
);
const titleRef = useRef<TextInput>(null);
const timer = useRef<NodeJS.Timeout>();
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = (date: Date) => {
timer.current = setTimeout(() => {
hideDatePicker();
setDate(date);
}, 50);
};
function nth(n: number) {
return (
["st", "nd", "rd"][(((((n < 0 ? -n : n) + 90) % 100) - 10) % 10) - 1] ||
"th"
);
}
function getSelectedDaysText(selectedDays: number[]) {
const text = selectedDays
.sort((a, b) => a - b)
.map((day, index) => {
const isLast = index === selectedDays.length - 1;
const isSecondLast = index === selectedDays.length - 2;
const joinWith = isSecondLast ? " & " : isLast ? "" : ", ";
return recurringMode === RecurringModes.Week
? WeekDayNames[day as keyof typeof WeekDayNames] + joinWith
: `${day}${nth(day)} ${joinWith}`;
})
.join("");
return text;
}
async function saveReminder() {
try {
if (!(await Notifications.checkAndRequestPermissions(true)))
throw new Error(strings.noNotificationPermission());
if (!date && reminderMode !== ReminderModes.Permanent) return;
if (
reminderMode === ReminderModes.Repeat &&
recurringMode !== "day" &&
recurringMode !== "year" &&
selectedDays.length === 0
)
throw new Error(strings.selectDayError());
if (!title.current) throw new Error(strings.setTitleError());
if (date.getTime() < Date.now() && reminderMode === "once") {
titleRef?.current?.focus();
throw new Error(strings.dateError());
}
date.setSeconds(0, 0);
const reminderId = await db.reminders?.add({
id: reminder?.id,
date: date?.getTime(),
priority: reminderNotificationMode,
title: title.current,
description: details.current,
recurringMode: recurringMode,
selectedDays: selectedDays,
mode: reminderMode,
localOnly: reminderMode === "permanent",
snoozeUntil:
date?.getTime() > Date.now() ? undefined : reminder?.snoozeUntil,
disabled: false
});
if (!reminderId) return;
const _reminder = await db.reminders?.reminder(reminderId);
if (reference && _reminder) {
await db.relations?.add(reference, {
id: _reminder?.id as string,
type: _reminder?.type
});
}
Notifications.scheduleNotification(_reminder as Reminder);
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
Navigation.goBack();
} catch (e) {
ToastManager.error(e as Error, undefined);
}
}
const KeyboardViewIOS = Platform.OS === "ios" ? KeyboardAvoidingView : View;
return (
<SafeAreaView
style={{
backgroundColor: colors.primary.background,
flex: 1
}}
>
<KeyboardViewIOS
behavior="padding"
style={{
flex: 1
}}
>
<Header
title={reminder ? strings.editReminder() : strings.newReminder()}
canGoBack
rightButton={{
name: "check",
onPress: saveReminder
}}
/>
<Dialog context="local" />
<ScrollView
style={{
marginBottom: DDS.isTab ? 25 : undefined,
paddingHorizontal: DefaultAppStyles.GAP
}}
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="handled"
>
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
placeholder={strings.remindeMeOf()}
onChangeText={(text) => (title.current = text)}
wrapperStyle={{
marginTop: DefaultAppStyles.GAP_VERTICAL
}}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
placeholder={strings.addShortNote()}
onChangeText={(text) => (details.current = text)}
containerStyle={{
maxHeight: 80
}}
multiline
textAlignVertical="top"
inputStyle={{
minHeight: 80,
paddingVertical: DefaultAppStyles.GAP_VERTICAL
}}
height={80}
wrapperStyle={{
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
/>
<ScrollView
style={{
flexDirection: "row",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
horizontal
>
{Object.keys(ReminderModes).map((mode) => (
<Button
key={mode}
title={strings.reminderModes(
ReminderModes[mode as keyof typeof ReminderModes] as string
)}
style={{
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
marginRight: DefaultAppStyles.GAP_SMALL
}}
proTag={mode === "Repeat"}
height={35}
type={
reminderMode ===
ReminderModes[mode as keyof typeof ReminderModes]
? "selected"
: "plain"
}
onPress={() => {
if (mode === "Repeat" && !PremiumService.get()) return;
setReminderMode(
ReminderModes[
mode as keyof typeof ReminderModes
] as Reminder["mode"]
);
if (mode === "Repeat") {
setSelectedDays((days) => {
if (days.length > 0) return days;
if (days.indexOf(date.getDay()) > -1) {
return days;
}
days.push(date.getDay());
return [...days];
});
}
}}
/>
))}
</ScrollView>
{reminderMode === ReminderModes.Repeat ? (
<View
style={{
backgroundColor: colors.secondary.background,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius,
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<View
style={{
flexDirection: "row",
marginBottom:
recurringMode === "day" || recurringMode === "year"
? 0
: 12,
alignItems: "center"
}}
>
{Object.keys(RecurringModes).map((mode) => (
<Button
key={mode}
title={strings.recurringModes(
RecurringModes[mode as keyof typeof RecurringModes]
)}
style={{
marginRight: 6,
borderRadius: 100,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
type={
recurringMode ===
RecurringModes[mode as keyof typeof RecurringModes]
? "selected"
: "plain"
}
onPress={() => {
setRecurringMode(
RecurringModes[
mode as keyof typeof RecurringModes
] as Reminder["recurringMode"]
);
setSelectedDays([]);
setRepeatFrequency(1);
}}
/>
))}
</View>
<ScrollView showsHorizontalScrollIndicator={false} horizontal>
{recurringMode === RecurringModes.Daily ||
recurringMode === RecurringModes.Year
? null
: recurringMode === RecurringModes.Week
? WeekDays.map((item, index) => (
<Button
key={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
title={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
type={
selectedDays.indexOf(index) > -1
? "selected"
: "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index) > -1) {
days.splice(days.indexOf(index), 1);
return [...days];
}
days.push(index);
return [...days];
});
}}
/>
))
: MonthDays.map((item, index) => (
<Button
key={index + "monthday"}
title={index + 1 + ""}
type={
selectedDays.indexOf(index + 1) > -1
? "selected"
: "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index + 1) > -1) {
days.splice(days.indexOf(index + 1), 1);
return [...days];
}
days.push(index + 1);
return [...days];
});
}}
/>
))}
</ScrollView>
</View>
) : null}
{reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
width: "100%",
flexDirection: "column",
justifyContent: "center",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
alignItems: "center"
}}
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
is24Hour={db.settings.getTimeFormat() === "24-hour"}
date={date || new Date(Date.now())}
/>
<DatePicker
date={date}
maximumDate={dayjs(date).add(3, "months").toDate()}
onDateChange={handleConfirm}
textColor={isDark ? colors.static.white : colors.static.black}
fadeToColor={colors.primary.background}
theme={isDark ? "dark" : "light"}
androidVariant="nativeAndroid"
is24hourSource="locale"
locale={
db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
}
mode={
reminderMode === ReminderModes.Repeat &&
recurringMode !== "year"
? "time"
: "datetime"
}
/>
{reminderMode === ReminderModes.Repeat ? null : (
<Button
style={{
width: "100%"
}}
title={
date ? date.toLocaleDateString() : strings.selectDate()
}
type={date ? "secondaryAccented" : "secondary"}
icon="calendar"
fontSize={AppFontSize.sm}
onPress={() => {
showDatePicker();
}}
/>
)}
</View>
)}
{reminderMode === ReminderModes.Once ||
reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
borderRadius: defaultBorderRadius,
flexDirection: "row",
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignItems: "center",
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<>
<Paragraph
size={AppFontSize.xxs}
color={colors.secondary.paragraph}
>
{recurringMode === RecurringModes.Daily
? strings.reminderRepeatStrings.day(
dayjs(date).format("hh:mm A")
)
: recurringMode === RecurringModes.Year
? strings.reminderRepeatStrings.year(
dayjs(date).format("dddd, MMMM D, h:mm A")
)
: selectedDays.length === 7 &&
recurringMode === RecurringModes.Week
? strings.reminderRepeatStrings.week.daily(
dayjs(date).format("hh:mm A")
)
: selectedDays.length === 0
? strings.reminderRepeatStrings[
recurringMode as "week" | "month"
].selectDays()
: strings.reminderRepeatStrings.repeats(
repeatFrequency,
recurringMode as string,
getSelectedDaysText(selectedDays),
dayjs(date).format("hh:mm A")
)}
</Paragraph>
</>
</View>
)}
<ReminderTime
reminder={reminder}
style={{
width: "100%",
justifyContent: "flex-start",
borderWidth: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignSelf: "flex-start"
}}
/>
{reminderMode === ReminderModes.Permanent ? null : (
<ScrollView
style={{
flexDirection: "row",
marginTop: DefaultAppStyles.GAP_VERTICAL,
height: 50
}}
horizontal
>
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={strings.reminderNotificationModes(
mode as keyof typeof ReminderNotificationModes
)}
style={{
marginRight: 12,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
icon={
mode === "Silent"
? "minus-circle"
: mode === "Vibrate"
? "vibrate"
: "volume-high"
}
fontSize={AppFontSize.xs}
height={35}
type={
reminderNotificationMode ===
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "selected"
: "plain"
}
onPress={() => {
const _mode = ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
] as Reminder["priority"];
SettingsService.set({
reminderNotificationMode: _mode
});
setReminderNotificatioMode(_mode);
}}
/>
))}
</ScrollView>
)}
</ScrollView>
</KeyboardViewIOS>
</SafeAreaView>
);
}
AddReminder.present = (reminder?: Reminder, reference?: Note) => {
Navigation.navigate("AddReminder", {
reminder,
reference
});
};

View File

@@ -38,11 +38,11 @@ import { WebViewMessageEvent } from "react-native-webview";
import { DatabaseLogger, db } from "../../../common/database";
import downloadAttachment from "../../../common/filesystem/download-attachment";
import { AuthMode } from "../../../components/auth/common";
import { Properties } from "../../../components/properties";
import EditorTabs from "../../../components/sheets/editor-tabs";
import { Issue } from "../../../components/sheets/github/issue";
import LinkNote from "../../../components/sheets/link-note";
import { RelationsList } from "../../../components/sheets/relations-list";
import ReminderSheet from "../../../components/sheets/reminder";
import TableOfContents from "../../../components/sheets/toc";
import { DDS } from "../../../services/device-detection";
import {
@@ -73,13 +73,13 @@ import {
} from "../../../utils/events";
import { openLinkInBrowser } from "../../../utils/functions";
import { fluidTabsRef } from "../../../utils/global-refs";
import { sleep } from "../../../utils/time";
import ManageTags from "../../manage-tags";
import { useDragState } from "../../settings/editor/state";
import { EditorMessage, EditorProps, useEditorType } from "./types";
import { useTabStore } from "./use-tab-store";
import { editorState, openInternalLink } from "./utils";
import { Properties } from "../../../components/properties";
import { sleep } from "../../../utils/time";
import ManageTags from "../../manage-tags";
import AddReminder from "../../add-reminder";
const publishNote = async () => {
const user = useUserStore.getState().user;
@@ -438,7 +438,7 @@ export const useEditorEvents = (
referenceType: "reminder",
relationType: "from",
title: strings.dataTypesPluralCamelCase.reminder(),
onAdd: () => ReminderSheet.present(undefined, note, true)
onAdd: () => AddReminder.present(undefined, note)
});
break;
case EditorEvents.newtag:

View File

@@ -755,6 +755,8 @@ export const useEditor = (
? (data as ContentItem).noteId
: data.id;
if (!useTabStore.getState().hasTabForNote(noteId)) return;
const note = data.type === "note" ? data : await db.notes?.note(noteId);
lock.current = true;
// Handle this case where note was locked on another device and synced.

View File

@@ -98,7 +98,7 @@ class TabSessionStorage {
static get(id: string): TabSessionItem | null {
if (!id) return null;
return TabSessionStorage.storage.getMap(id);
return TabSessionStorage.storage.getMap(id) || null;
}
static set(id: string, session: TabSessionItem): void {

View File

@@ -17,19 +17,19 @@ 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 { strings } from "@notesnook/intl";
import React from "react";
import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import ReminderSheet from "../../components/sheets/reminder";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store";
import { useReminders } from "../../stores/use-reminder-store";
import { strings } from "@notesnook/intl";
import AddReminder from "../add-reminder";
export const Reminders = ({
navigation,
@@ -68,7 +68,7 @@ export const Reminders = ({
}}
id={route.name}
onPressDefaultRightButton={() => {
ReminderSheet.present();
AddReminder.present();
}}
/>
@@ -84,7 +84,7 @@ export const Reminders = ({
paragraph: strings.remindersEmpty(),
button: strings.setReminder(),
action: () => {
ReminderSheet.present();
AddReminder.present();
},
loading: strings.loadingReminders()
}}
@@ -92,7 +92,7 @@ export const Reminders = ({
<FloatingButton
onPress={() => {
ReminderSheet.present();
AddReminder.present();
}}
alwaysVisible
/>

View File

@@ -16,35 +16,22 @@ GNU General Public License for more details.
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 React, { RefObject, useRef, useState } from "react";
import React, { useRef, useState } from "react";
import { TextInput, View } from "react-native";
import { ActionSheetRef } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import {
eSendEvent,
presentSheet,
PresentSheetOptions,
ToastManager
} from "../../../services/event-manager";
import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import { eSendEvent, ToastManager } from "../../../services/event-manager";
import { eUserLoggedIn } from "../../../utils/events";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../../utils/styles";
type ChangeEmailProps = {
actionSheetRef: RefObject<ActionSheetRef>;
close?: () => void;
update?: (options: PresentSheetOptions) => void;
};
import Input from "../../../components/ui/input";
import { Button } from "../../../components/ui/button";
enum EmailChangeSteps {
verify,
changeEmail
}
export const ChangeEmail = ({ close }: ChangeEmailProps) => {
export const ChangeEmail = () => {
const [step, setStep] = useState(EmailChangeSteps.verify);
const emailChangeData = useRef<{
email?: string;
@@ -103,10 +90,6 @@ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
return (
<View style={{ paddingHorizontal: DefaultAppStyles.GAP }}>
<DialogHeader
title={strings.changeEmail()}
paragraph={strings.changeEmailDesc()}
/>
<View
style={{
marginTop: DefaultAppStyles.GAP_VERTICAL
@@ -156,23 +139,12 @@ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
: strings.changeEmail()
}
type="accent"
width={250}
style={{
width: "100%"
}}
loading={loading}
onPress={onSubmit}
style={{
borderRadius: 100,
height: 45,
marginTop: 2
}}
/>
</View>
);
};
ChangeEmail.present = () => {
presentSheet({
component: (ref, close, update) => (
<ChangeEmail actionSheetRef={ref} close={close} update={update} />
)
});
};

View File

@@ -43,6 +43,8 @@ import ThemeSelector from "./theme-selector";
import { TitleFormat } from "./title-format";
import { View } from "react-native";
import { DefaultAppStyles } from "../../utils/styles";
import { ChangePassword } from "../../components/auth/change-password";
import { ChangeEmail } from "./change-email";
export const components: { [name: string]: ReactElement } = {
colorpicker: <AccentColorPicker />,
@@ -69,5 +71,7 @@ export const components: { [name: string]: ReactElement } = {
<AttachmentGroupProgress groupId="offline-mode" />
</View>
),
"sidebar-tab-selector": <SidebarTabPicker />
"sidebar-tab-selector": <SidebarTabPicker />,
"change-password": <ChangePassword />,
"change-email": <ChangeEmail />
};

View File

@@ -32,11 +32,9 @@ import { enabled } from "react-native-privacy-snapshot";
import ScreenGuardModule from "react-native-screenguard";
import { db } from "../../common/database";
import filesystem from "../../common/filesystem";
import { ChangePassword } from "../../components/auth/change-password";
import { presentDialog } from "../../components/dialog/functions";
import { AppLockPassword } from "../../components/dialogs/applock-password";
import { endProgress, startProgress } from "../../components/dialogs/progress";
import { ChangeEmail } from "../../components/sheets/change-email";
import ExportNotesSheet from "../../components/sheets/export-notes";
import { Issue } from "../../components/sheets/github/issue";
import { Progress } from "../../components/sheets/progress";
@@ -243,17 +241,15 @@ export const settingsGroups: SettingSection[] = [
{
id: "change-password",
name: strings.changePassword(),
modifer: async () => {
ChangePassword.present();
},
description: strings.changePasswordDesc()
type: "screen",
description: strings.changePasswordDesc(),
component: "change-password"
},
{
id: "change-email",
name: strings.changeEmail(),
modifer: async () => {
ChangeEmail.present();
},
type: "screen",
component: "change-email",
description: strings.changeEmailDesc()
},
{

View File

@@ -113,7 +113,10 @@ async function onBackgroundSyncStarted() {
useUserStore.getState().setSyncing(false);
}
await Notifications.setupReminders();
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(false);
}
Notifications.restorePinnedNotes();
NotePreviewWidget.updateNotes();
deleteDCacheFiles();
DatabaseLogger.info("BACKGROUND SYNC COMPLETE");
@@ -142,6 +145,7 @@ const onBoot = async () => {
Notifications.pinQuickNote(false);
}
Notifications.restorePinnedNotes();
NotePreviewWidget.updateNotes();
DatabaseLogger.info("BOOT TASK COMPLETE");
} catch (e) {
DatabaseLogger.error(e as Error);

View File

@@ -18,12 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Platform } from "react-native";
import { db } from "../common/database";
import ReminderSheet from "../components/sheets/reminder";
import { setAppState } from "../screens/editor/tiptap/utils";
import { eOnLoadNote } from "../utils/events";
import { NotesnookModule } from "../utils/notesnook-module";
import { eSendEvent } from "./event-manager";
import { fluidTabsRef } from "../utils/global-refs";
import AddReminder from "../screens/add-reminder";
const launchIntent = Platform.OS === "ios" ? {} : NotesnookModule.getIntent();
let used = false;
@@ -65,9 +65,9 @@ export const IntentService = {
const reminder = await db.reminders.reminder(
intent["com.streetwriters.notesnook.OpenReminderId"]
);
if (reminder) ReminderSheet.present(reminder);
if (reminder) AddReminder.present(reminder);
} else if (intent["com.streetwriters.notesnook.NewReminder"]) {
ReminderSheet.present();
AddReminder.present();
}
} catch (e) {
/* empty */

View File

@@ -69,7 +69,8 @@ const routeNames = {
LinkNotebooks: "LinkNotebooks",
MoveNotes: "MoveNotes",
Archive: "Archive",
ManageTags: "ManageTags"
ManageTags: "ManageTags",
AddReminder: "AddReminder"
};
export type NavigationProps<T extends RouteName> = NativeStackScreenProps<

View File

@@ -348,7 +348,7 @@ async function scheduleNotification(
title: title,
message: description || "",
ongoing: true,
subtitle: description || "",
// subtitle: description || "",
actions: [strings.unpin()]
});
}
@@ -396,7 +396,7 @@ async function scheduleNotification(
payload: payload || "",
dateModified: reminder.dateModified + ""
},
subtitle: !description ? undefined : description,
// subtitle: !description ? undefined : description,
android: {
channelId: await getChannelId(priority),
smallIcon: "ic_stat_name",

View File

@@ -94,6 +94,10 @@ export interface RouteParams extends ParamListBase {
ManageTags: {
ids?: string[];
};
AddReminder: {
reminder?: Reminder;
reference?: Note;
};
}
export type RouteName = keyof RouteParams;

View File

@@ -124,7 +124,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3057
versionCode 3063
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -11,57 +11,69 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" tools:node="remove" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:node="remove" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
tools:node="remove" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission
android:name="android.permission.USE_FULL_SCREEN_INTENT"
tools:node="remove" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="application/pdf" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="text/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="image/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="application/pdf" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="text/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="image/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="video/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="audio/*" />
</intent>
</queries>
<data android:mimeType="video/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="audio/*" />
</intent>
</queries>
<application
android:name=".MainApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:theme="@style/BootTheme"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config">
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
android:theme="@style/BootTheme">
<receiver
android:name=".NoteWidget"
android:exported="false"
android:label="@string/quick_note">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -71,7 +83,10 @@
android:resource="@xml/new_note_widget_info" />
</receiver>
<receiver android:exported="false" android:label="@string/note" android:name=".NotePreviewWidget">
<receiver
android:name=".NotePreviewWidget"
android:exported="false"
android:label="@string/note">
<intent-filter>"
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -81,7 +96,10 @@
android:resource="@xml/note_widget_info" />
</receiver>
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
<receiver
android:name=".ReminderWidgetProvider"
android:exported="false"
android:label="@string/reminders_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -90,33 +108,37 @@
android:resource="@xml/widget_reminders_info" />
</receiver>
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
<activity
android:name=".NotePreviewConfigureActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:exported="true"
android:label="NotePreviewConfigure"
android:launchMode="singleTask"
android:exported="true"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize" android:name=".NotePreviewConfigureActivity">
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:label="Notesnook">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@@ -127,6 +149,7 @@
<intent-filter android:label="Notesnook">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@@ -134,22 +157,25 @@
</intent-filter>
</activity>
<activity android:exported="false" android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<activity
android:name="com.facebook.react.devsupport.DevSettingsActivity"
android:exported="false" />
<activity
android:name=".ShareActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/title_activity_share"
android:noHistory="true"
android:screenOrientation="unspecified"
android:exported="true"
android:taskAffinity=""
android:excludeFromRecents="true"
android:windowSoftInputMode="adjustResize"
android:theme="@style/AppThemeB">
android:theme="@style/AppThemeB"
android:windowSoftInputMode="adjustResize">
<intent-filter android:label="Make Note">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
@@ -160,6 +186,7 @@
<intent-filter android:label="Make Note">
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
@@ -179,35 +206,41 @@
android:enabled="true"
android:exported="true"
android:stopWithTask="false" />
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
<service android:name="com.streetwriters.notesnook.BootTaskService" android:foregroundServiceType="dataSync" />
<service
android:name="com.streetwriters.notesnook.BootTaskService"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="The service is required by the app to restore notifications of pinned notes, restore notification with reply input for creating notes and restore data in note preview widgets on device reboot." />
</service>
<service
android:name=".NotesnookTileService"
android:exported="true"
android:icon="@drawable/add_note"
android:label="New note"
android:exported="true"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE"/>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<service
android:name=".ReminderViewsService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service
android:name=".ReminderViewsService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_viewer_provider_paths" />
</provider>
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_viewer_provider_paths" />
</provider>
<provider
android:name="com.vinzscam.reactnativefileviewer.FileProvider"
@@ -219,12 +252,14 @@
android:resource="@xml/file_viewer_provider_paths" />
</provider>
<receiver android:exported="true" android:name=".BootRecieverService">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver
android:name=".BootRecieverService"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</application>

View File

@@ -39,7 +39,6 @@ public class BootTaskService extends HeadlessJsTaskService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("com.streetwriters.notesnook",
"Default",
@@ -55,7 +54,7 @@ public class BootTaskService extends HeadlessJsTaskService {
this.startForeground(
1,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
} else {
this.startForeground(
1,

View File

@@ -3,8 +3,16 @@ package com.streetwriters.notesnook;
import com.facebook.react.ReactActivity;
import android.content.Intent;
import android.content.res.Configuration;
import androidx.core.graphics.Insets;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
@@ -22,6 +30,20 @@ public class MainActivity extends ReactActivity {
try {
startService(new Intent(getBaseContext(), OnClearFromRecentService.class));
} catch (Exception ignored) {}
if (Build.VERSION.SDK_INT >= 35) {
final View rootView = findViewById(android.R.id.content);
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
Insets innerPadding = insets.getInsets(WindowInsetsCompat.Type.ime());
rootView.setPadding(
innerPadding.left,
innerPadding.top,
innerPadding.right,
innerPadding.bottom
);
return insets;
});
}
}
/**

View File

@@ -1,5 +1,5 @@
- New and improved search with highlighting
- Fixed app crashing on android 16 devices
- Many bug fixes and improvements
- Fix crash on device boot on android 15 devices
- Fix note not opening in editor randomly
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -124,7 +124,6 @@
6529D2B0257B4A2900B49BC3 /* NotesnookDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NotesnookDebug.entitlements; path = Notesnook/NotesnookDebug.entitlements; sourceTree = "<group>"; };
656835802BB29A8300144BAB /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Italic.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Italic.ttf"; sourceTree = "<group>"; };
6593E4A2281C345400492C50 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Notesnook/AppDelegate.mm; sourceTree = "<group>"; };
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = BetterTrigram.xcframework; sourceTree = "<group>"; };
659BE46625E11A5100E05671 /* notesnook-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "notesnook-text.png"; path = "Notesnook/Images.xcassets/notesnook-text.png"; sourceTree = "<group>"; };
65A7F34F255689AD00699170 /* Notesnook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Notesnook.entitlements; path = Notesnook/Notesnook.entitlements; sourceTree = "<group>"; };
65AA857725E6DDEC00772A01 /* NotesWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotesWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -256,7 +255,6 @@
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */,
6515C42E2580AA2F00E83E39 /* StoreKit.framework */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
@@ -1091,7 +1089,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2135;
CURRENT_PROJECT_VERSION = 2142;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1165,7 +1163,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.1;
MARKETING_VERSION = 3.2.7;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1196,7 +1194,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2135;
CURRENT_PROJECT_VERSION = 2142;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1270,7 +1268,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.1;
MARKETING_VERSION = 3.2.7;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1429,7 +1427,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2135;
CURRENT_PROJECT_VERSION = 2142;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1441,7 +1439,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.1;
MARKETING_VERSION = 3.2.7;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1472,7 +1470,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2135;
CURRENT_PROJECT_VERSION = 2142;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1485,7 +1483,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.1;
MARKETING_VERSION = 3.2.7;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1515,10 +1513,16 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2135;
CURRENT_PROJECT_VERSION = 2142;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal\"",
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built\"",
"\"${PROJECT_DIR}\"",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@@ -1589,7 +1593,8 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.1;
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.7;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1620,11 +1625,17 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2135;
CURRENT_PROJECT_VERSION = 2142;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal\"",
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built\"",
"\"${PROJECT_DIR}\"",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@@ -1695,7 +1706,8 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.1;
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.7;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1325,6 +1325,28 @@ PODS:
- react-native-pdf (6.7.7):
- React-Core
- react-native-quick-sqlite (8.2.7):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.11.18.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- react-native-quick-sqlite/lexbor (= 8.2.7)
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-quick-sqlite/lexbor (8.2.7):
- DoubleConversion
- glog
- hermes-engine
@@ -1352,7 +1374,7 @@ PODS:
- SDWebImage (~> 5.11.1)
- react-native-share-extension (2.9.0):
- React
- react-native-sodium (1.6.3):
- react-native-sodium (1.6.4):
- React
- react-native-theme-switch-animation (0.6.0):
- DoubleConversion
@@ -2409,11 +2431,11 @@ SPEC CHECKSUMS:
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
react-native-pager-view: 5aaf51a9338f7997b8acee0e0febfddd1ade5c0a
react-native-pdf: 6a51a22ccefb23eb93298771e4bf090913e86d70
react-native-quick-sqlite: 6aebe0e1c264c48450059e271e5d2404013cea93
react-native-quick-sqlite: 1bfc7f1e9acbe9a5aa5c4cc81712e9bde3ab7672
react-native-safe-area-context: 9d72abf6d8473da73033b597090a80b709c0b2f1
react-native-screenguard: 82437eeb0086a90b5e5d7e54130bb04fb406373e
react-native-share-extension: bcb7e466390a9e50c742f4b1019d6f181aedd7ad
react-native-sodium: aa26d9f46dcfdebd92d3af793b74fbb234a4d75d
react-native-sodium: 5b55f74705005612e12838ffd583927229a65e6a
react-native-theme-switch-animation: d90fe2de0d9e87a63cd6235d98cba6e7054e9a10
react-native-webview: 079eca50edf657503318b66687dadfb903731aa8
React-nativeconfig: ecf4dc92c40b97e2b3f0c619938f78bfd6507b08

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.2.0",
"version": "3.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.2.0",
"version": "3.2.2",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.2.1",
"version": "3.2.7",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -36,3 +36,37 @@ index 3dfe1dc..70ef9da 100644
+ implementation 'io.legere:pdfiumandroid:1.0.32'
implementation 'com.google.code.gson:gson:2.8.5'
}
diff --git a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
index 7b7a125..361924b 100644
--- a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
+++ b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
@@ -12,6 +12,8 @@ import java.io.File;
import android.content.ContentResolver;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import android.util.SizeF;
import android.view.View;
import android.view.ViewGroup;
@@ -105,7 +107,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
TopChangeEvent tce = new TopChangeEvent(surfaceId, getId(), event);
if (dispatcher != null) {
- dispatcher.dispatchEvent(tce);
+ new Handler(Looper.getMainLooper()).postDelayed(() -> dispatcher.dispatchEvent(tce), 10);
}
// ReactContext reactContext = (ReactContext)this.getContext();
diff --git a/node_modules/react-native-pdf/index.js b/node_modules/react-native-pdf/index.js
index 56df005..dd14412 100644
--- a/node_modules/react-native-pdf/index.js
+++ b/node_modules/react-native-pdf/index.js
@@ -364,7 +364,6 @@ export default class Pdf extends Component {
}
_onChange = (event) => {
-
let message = event.nativeEvent.message.split('|');
//__DEV__ && console.log("onChange: " + message);
if (message.length > 0) {

File diff suppressed because it is too large Load Diff

View File

@@ -107,10 +107,12 @@ function generateTableOfContents() {
export const MonographPage = ({
monograph,
encodedKey
encodedKey,
apiHost
}: {
monograph: Monograph;
encodedKey?: string;
apiHost: string;
}) => {
const [reportDialogVisible, setReportDialogVisible] = useState(false);
const [tableOfContents, setTableOfContents] = useState<TableOfContent[]>([]);
@@ -227,7 +229,7 @@ export const MonographPage = ({
)}
<Image
sx={{ display: "none" }}
src={`https://api.notesnook.com/monographs/${monograph.id}/view`}
src={`${apiHost}/monographs/${monograph.id}/view`}
/>
</Flex>
</Box>

View File

@@ -85,7 +85,8 @@ export async function loader({ params }: LoaderFunctionArgs) {
const metadata = getMonographMetadata(monograph);
return {
monograph,
metadata
metadata,
apiHost: API_HOST
};
} catch (e) {
// console.error(e);
@@ -96,19 +97,24 @@ export async function loader({ params }: LoaderFunctionArgs) {
fullDescription: "This monograph does not exist.",
shortDescription: "This monograph does not exist.",
datePublished: ""
}
},
apiHost: API_HOST
};
}
}
export default function MonographPost() {
const { monograph } = useLoaderData<typeof loader>();
const { monograph, apiHost } = useLoaderData<typeof loader>();
const [_, hashParams] = useHashLocation();
return (
<>
{monograph ? (
<MonographPage monograph={monograph} encodedKey={hashParams.key} />
<MonographPage
monograph={monograph}
encodedKey={hashParams.key}
apiHost={apiHost}
/>
) : (
<>
<Header />

View File

@@ -0,0 +1,40 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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 { test, expect } from "@playwright/test";
import { AppModel } from "./models/app.model";
import { USER } from "./utils";
test("don't show monographs list to unauthenticated user", async ({ page }) => {
const app = new AppModel(page);
await app.goto();
await expect(
async () => await app.navigateTo("Monographs")
).rejects.toThrowError();
});
test("show monographs list to authenticated user", async ({ page }) => {
const app = new AppModel(page);
await app.auth.goto();
await app.auth.login(USER.CURRENT);
await app.navigateTo("Monographs");
});

View File

@@ -28,7 +28,6 @@ function createRoute(key: string, header: string) {
const routes = [
createRoute("notes", "Notes"),
createRoute("favorites", "Favorites"),
createRoute("monographs", "Monographs"),
createRoute("reminders", "Reminders"),
createRoute("trash", "Trash")
];

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "3.2.1",
"version": "3.2.3",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { store as noteStore } from "../stores/note-store";
import { store as trashStore } from "../stores/trash-store";
import { store as notebookStore } from "../stores/notebook-store";
import { store as attachmentStore } from "../stores/attachment-store";
import { store as reminderStore } from "../stores/reminder-store";
@@ -27,7 +28,11 @@ import { db } from "./db";
import { showToast } from "../utils/toast";
import Vault from "./vault";
import { TaskManager } from "./task-manager";
import { ConfirmDialog, showMultiDeleteConfirmation } from "../dialogs/confirm";
import {
ConfirmDialog,
showMultiDeleteConfirmation,
showMultiPermanentDeleteConfirmation
} from "../dialogs/confirm";
import { strings } from "@notesnook/intl";
async function moveNotesToTrash(ids: string[], confirm = true) {
@@ -77,14 +82,7 @@ async function moveNotebooksToTrash(ids: string[]) {
if (!result) return;
if (result.deleteContainingNotes) {
await Multiselect.moveNotesToTrash(
Array.from(
new Set(
(await Promise.all(ids.map((id) => db.notebooks.notes(id)))).flat()
)
),
false
);
await Multiselect.moveNotesToTrash(await db.notebooks.notes(...ids), false);
}
await TaskManager.startTask({
@@ -182,10 +180,50 @@ async function deleteTags(ids: string[]) {
showToast("success", strings.actions.deleted.tag(ids.length));
}
async function restoreItemsFromTrash(ids: string[]) {
if (!ids.length) return;
await TaskManager.startTask({
type: "status",
id: "restoreItems",
title: strings.inProgressActions.restoring.item(ids.length),
action: async (report) => {
report({
text: strings.inProgressActions.restoring.item(ids.length)
});
await trashStore.restore(...ids);
}
});
showToast("success", strings.actions.restored.item(ids.length));
}
async function deleteItemsFromTrash(ids: string[]) {
if (!ids.length) return;
if (!(await showMultiPermanentDeleteConfirmation(ids.length))) return;
await TaskManager.startTask({
type: "status",
id: "restoreItems",
title: strings.inProgressActions.permanentlyDeleting.item(ids.length),
action: async (report) => {
report({
text: strings.inProgressActions.permanentlyDeleting.item(ids.length)
});
await trashStore.delete(...ids);
}
});
showToast("success", strings.actions.permanentlyDeleted.item(ids.length));
}
export const Multiselect = {
moveRemindersToTrash,
moveNotebooksToTrash,
moveNotesToTrash,
deleteAttachments,
deleteTags
deleteTags,
restoreItemsFromTrash,
deleteItemsFromTrash
};

View File

@@ -561,11 +561,16 @@ export function Factory(Module) {
databases.add(db);
Module._sqlite3_free(zVfs);
check(fname, result);
return db;
};
})();
sqlite3.register_extensions = (function () {
return async function (db) {
Module.ccall("RegisterExtensionFunctions", "void", ["number"], [db]);
Module.ccall("sqlite3Fts5BetterTrigramInit", "void", ["number"], [db]);
Module.ccall("sqlite3Fts5HtmlInit", "void", ["number"], [db]);
check(fname, result);
return db;
};
})();

View File

@@ -724,6 +724,12 @@ export interface SQLiteAPI {
*/
open_v2(zFilename: string, iFlags?: number, zVfs?: string): Promise<number>;
/**
* Register and init extensions.
* @param db database pointer
*/
register_extensions(db: number): Promise<void>;
/**
* Compile an SQL statement
*

View File

@@ -227,6 +227,9 @@ class _SQLiteWorker {
}
async initialize() {
if (typeof this.db === "number")
await this.sqlite.register_extensions(this.db);
self.dispatchEvent(
new MessageEvent("message", {
data: { type: "databaseInitialized", dbName: this.name }

View File

@@ -88,7 +88,7 @@ typeof U||!(H instanceof O))throw H;return H.Pf}},i:function(b,c,d,e,f){try{c=d+
c.node.Vf;e.type.Tg?e.type.Tg(e,!1,function(f){f?d(function(){return 29}):d(0)}):d(0)})}catch(d){if("undefined"==typeof U||!(d instanceof O))throw d;return d.Pf}},s:function(b,c,d,e){try{a:{var f=V(b);b=c;for(var g,h=c=0;h<d;h++){var n=E[b>>2],k=E[b+4>>2];b+=8;var l=f,p=n,q=k,r=g,w=A;if(0>q||0>r)throw new O(28);if(null===l.Xf)throw new O(8);if(0===(l.flags&2097155))throw new O(8);if(16384===(l.node.mode&61440))throw new O(31);if(!l.Nf.write)throw new O(28);l.seekable&&l.flags&1024&&Db(l,0,2);var z=
"undefined"!=typeof r;if(!z)r=l.position;else if(!l.seekable)throw new O(70);var G=l.Nf.write(l,w,p,q,r,void 0);z||(l.position+=G);var D=G;if(0>D){var J=-1;break a}c+=D;"undefined"!==typeof g&&(g+=D)}J=c}E[e>>2]=J;return 0}catch(H){if("undefined"==typeof U||!(H instanceof O))throw H;return H.Pf}},sa:Vb,_:Wb,x:Xb,P:Yb,ca:Zb,I:$b,ka:ac,m:bc,ra:cc,na:dc,ia:ec,ea:fc,fa:gc,g:hc,h:ic,oa:jc,qa:kc,pa:lc,da:mc,ga:nc,ha:oc,ma:pc,c:qc,ja:rc,la:sc,aa:tc,V:uc,$:vc,ba:wc,S:xc,U:yc,Y:zc,X:Ac,R:Bc,n:Cc,T:Dc,Z:Ec,
k:Fc,W:Gc,l:Hc};
(function(){function b(g){g=g.exports;g=Kc(g);a.asm=g;ma=a.asm.ua;va();wa=a.asm.Df;ya.unshift(a.asm.va);Ca--;a.monitorRunDependencies&&a.monitorRunDependencies(Ca);0==Ca&&(null!==Da&&(clearInterval(Da),Da=null),Ea&&(g=Ea,Ea=null,g()))}function c(g){b(g.instance)}function d(g){return Ia().then(function(h){return WebAssembly.instantiate(h,e)}).then(function(h){return h}).then(g,function(h){t("failed to asynchronously prepare wasm: "+h);u(h)})}var e={a:ld};Ca++;a.monitorRunDependencies&&a.monitorRunDependencies(Ca);
(function(){function b(g){g=g.exports;g=Kc(g);a.asm=g;ma=a.asm.ua;va();wa=a.asm.Cf;ya.unshift(a.asm.va);Ca--;a.monitorRunDependencies&&a.monitorRunDependencies(Ca);0==Ca&&(null!==Da&&(clearInterval(Da),Da=null),Ea&&(g=Ea,Ea=null,g()))}function c(g){b(g.instance)}function d(g){return Ia().then(function(h){return WebAssembly.instantiate(h,e)}).then(function(h){return h}).then(g,function(h){t("failed to asynchronously prepare wasm: "+h);u(h)})}var e={a:ld};Ca++;a.monitorRunDependencies&&a.monitorRunDependencies(Ca);
if(a.instantiateWasm)try{var f=a.instantiateWasm(e,b);return f=Kc(f)}catch(g){t("Module.instantiateWasm callback failed with error: "+g),ba(g)}(function(){return la||"function"!=typeof WebAssembly.instantiateStreaming||Fa()||"function"!=typeof fetch?d(c):fetch(F,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,e).then(c,function(h){t("wasm streaming compile failed: "+h);t("falling back to ArrayBuffer instantiation");return d(c)})})})().catch(ba);return{}})();
a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.va).apply(null,arguments)};a._sqlite3_status64=function(){return(a._sqlite3_status64=a.asm.wa).apply(null,arguments)};a._sqlite3_status=function(){return(a._sqlite3_status=a.asm.xa).apply(null,arguments)};a._sqlite3_db_status=function(){return(a._sqlite3_db_status=a.asm.ya).apply(null,arguments)};a._sqlite3_msize=function(){return(a._sqlite3_msize=a.asm.za).apply(null,arguments)};
a._sqlite3_vfs_find=function(){return(a._sqlite3_vfs_find=a.asm.Aa).apply(null,arguments)};a._sqlite3_vfs_register=function(){return(a._sqlite3_vfs_register=a.asm.Ba).apply(null,arguments)};a._sqlite3_vfs_unregister=function(){return(a._sqlite3_vfs_unregister=a.asm.Ca).apply(null,arguments)};a._sqlite3_release_memory=function(){return(a._sqlite3_release_memory=a.asm.Da).apply(null,arguments)};a._sqlite3_soft_heap_limit64=function(){return(a._sqlite3_soft_heap_limit64=a.asm.Ea).apply(null,arguments)};
@@ -142,14 +142,14 @@ a._sqlite3_table_column_metadata=function(){return(a._sqlite3_table_column_metad
a._sqlite3_test_control=function(){return(a._sqlite3_test_control=a.asm.Oe).apply(null,arguments)};a._sqlite3_create_filename=function(){return(a._sqlite3_create_filename=a.asm.Pe).apply(null,arguments)};a._sqlite3_free_filename=function(){return(a._sqlite3_free_filename=a.asm.Qe).apply(null,arguments)};a._sqlite3_uri_parameter=function(){return(a._sqlite3_uri_parameter=a.asm.Re).apply(null,arguments)};a._sqlite3_uri_key=function(){return(a._sqlite3_uri_key=a.asm.Se).apply(null,arguments)};
a._sqlite3_uri_boolean=function(){return(a._sqlite3_uri_boolean=a.asm.Te).apply(null,arguments)};a._sqlite3_uri_int64=function(){return(a._sqlite3_uri_int64=a.asm.Ue).apply(null,arguments)};a._sqlite3_filename_database=function(){return(a._sqlite3_filename_database=a.asm.Ve).apply(null,arguments)};a._sqlite3_filename_journal=function(){return(a._sqlite3_filename_journal=a.asm.We).apply(null,arguments)};a._sqlite3_filename_wal=function(){return(a._sqlite3_filename_wal=a.asm.Xe).apply(null,arguments)};
a._sqlite3_db_name=function(){return(a._sqlite3_db_name=a.asm.Ye).apply(null,arguments)};a._sqlite3_db_filename=function(){return(a._sqlite3_db_filename=a.asm.Ze).apply(null,arguments)};a._sqlite3_db_readonly=function(){return(a._sqlite3_db_readonly=a.asm._e).apply(null,arguments)};a._sqlite3_compileoption_used=function(){return(a._sqlite3_compileoption_used=a.asm.$e).apply(null,arguments)};a._sqlite3_compileoption_get=function(){return(a._sqlite3_compileoption_get=a.asm.af).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.cf).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.gf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.hf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.jf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.kf).apply(null,arguments)};a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.mf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.nf).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.of).apply(null,arguments)};
var kd=a.___errno_location=function(){return(kd=a.___errno_location=a.asm.pf).apply(null,arguments)},Rb=a._malloc=function(){return(Rb=a._malloc=a.asm.qf).apply(null,arguments)},$c=a._free=function(){return($c=a._free=a.asm.rf).apply(null,arguments)};a._RegisterExtensionFunctions=function(){return(a._RegisterExtensionFunctions=a.asm.sf).apply(null,arguments)};a._sqlite3Fts5BetterTrigramInit=function(){return(a._sqlite3Fts5BetterTrigramInit=a.asm.tf).apply(null,arguments)};
a._sqlite3Fts5HtmlInit=function(){return(a._sqlite3Fts5HtmlInit=a.asm.uf).apply(null,arguments)};a._set_authorizer=function(){return(a._set_authorizer=a.asm.vf).apply(null,arguments)};a._create_function=function(){return(a._create_function=a.asm.wf).apply(null,arguments)};a._create_module=function(){return(a._create_module=a.asm.xf).apply(null,arguments)};a._progress_handler=function(){return(a._progress_handler=a.asm.yf).apply(null,arguments)};
a._register_vfs=function(){return(a._register_vfs=a.asm.zf).apply(null,arguments)};a._getSqliteFree=function(){return(a._getSqliteFree=a.asm.Af).apply(null,arguments)};a._main=function(){return(a._main=a.asm.Bf).apply(null,arguments)};
var ab=a._emscripten_builtin_memalign=function(){return(ab=a._emscripten_builtin_memalign=a.asm.Cf).apply(null,arguments)},md=a.getTempRet0=function(){return(md=a.getTempRet0=a.asm.Ef).apply(null,arguments)},hd=a.stackSave=function(){return(hd=a.stackSave=a.asm.Ff).apply(null,arguments)},fd=a.stackRestore=function(){return(fd=a.stackRestore=a.asm.Gf).apply(null,arguments)},gd=a.stackAlloc=function(){return(gd=a.stackAlloc=a.asm.Hf).apply(null,arguments)},Yc=a._asyncify_start_unwind=function(){return(Yc=
a._asyncify_start_unwind=a.asm.If).apply(null,arguments)},Mc=a._asyncify_stop_unwind=function(){return(Mc=a._asyncify_stop_unwind=a.asm.Jf).apply(null,arguments)},Wc=a._asyncify_start_rewind=function(){return(Wc=a._asyncify_start_rewind=a.asm.Kf).apply(null,arguments)},Zc=a._asyncify_stop_rewind=function(){return(Zc=a._asyncify_stop_rewind=a.asm.Lf).apply(null,arguments)};a._sqlite3_version=3232;a.UTF8ToString=x;a.stringToUTF8=qa;a.lengthBytesUTF8=ra;a.getTempRet0=md;a.ccall=Z;
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};var kd=a.___errno_location=function(){return(kd=a.___errno_location=a.asm.cf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.gf).apply(null,arguments)};a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.hf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.jf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.kf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.mf).apply(null,arguments)};a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.nf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.of).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.pf).apply(null,arguments)};
var Rb=a._malloc=function(){return(Rb=a._malloc=a.asm.qf).apply(null,arguments)},$c=a._free=function(){return($c=a._free=a.asm.rf).apply(null,arguments)};a._RegisterExtensionFunctions=function(){return(a._RegisterExtensionFunctions=a.asm.sf).apply(null,arguments)};a._sqlite3Fts5BetterTrigramInit=function(){return(a._sqlite3Fts5BetterTrigramInit=a.asm.tf).apply(null,arguments)};a._sqlite3Fts5HtmlInit=function(){return(a._sqlite3Fts5HtmlInit=a.asm.uf).apply(null,arguments)};
a._set_authorizer=function(){return(a._set_authorizer=a.asm.vf).apply(null,arguments)};a._create_function=function(){return(a._create_function=a.asm.wf).apply(null,arguments)};a._create_module=function(){return(a._create_module=a.asm.xf).apply(null,arguments)};a._progress_handler=function(){return(a._progress_handler=a.asm.yf).apply(null,arguments)};a._register_vfs=function(){return(a._register_vfs=a.asm.zf).apply(null,arguments)};
a._getSqliteFree=function(){return(a._getSqliteFree=a.asm.Af).apply(null,arguments)};a._main=function(){return(a._main=a.asm.Bf).apply(null,arguments)};
var ab=a._emscripten_builtin_memalign=function(){return(ab=a._emscripten_builtin_memalign=a.asm.Df).apply(null,arguments)},md=a.getTempRet0=function(){return(md=a.getTempRet0=a.asm.Ef).apply(null,arguments)},hd=a.stackSave=function(){return(hd=a.stackSave=a.asm.Ff).apply(null,arguments)},fd=a.stackRestore=function(){return(fd=a.stackRestore=a.asm.Gf).apply(null,arguments)},gd=a.stackAlloc=function(){return(gd=a.stackAlloc=a.asm.Hf).apply(null,arguments)},Yc=a._asyncify_start_unwind=function(){return(Yc=
a._asyncify_start_unwind=a.asm.If).apply(null,arguments)},Mc=a._asyncify_stop_unwind=function(){return(Mc=a._asyncify_stop_unwind=a.asm.Jf).apply(null,arguments)},Wc=a._asyncify_start_rewind=function(){return(Wc=a._asyncify_start_rewind=a.asm.Kf).apply(null,arguments)},Zc=a._asyncify_stop_rewind=function(){return(Zc=a._asyncify_stop_rewind=a.asm.Lf).apply(null,arguments)};a._sqlite3_version=60912;a.UTF8ToString=x;a.stringToUTF8=qa;a.lengthBytesUTF8=ra;a.getTempRet0=md;a.ccall=Z;
a.cwrap=function(b,c,d,e){d=d||[];var f=d.every(g=>"number"===g||"boolean"===g);return"string"!==c&&f&&!e?a["_"+b]:function(){return Z(b,c,d,arguments,e)}};
a.addFunction=function(b,c){if(!cd){cd=new WeakMap;var d=wa.length;if(cd)for(var e=0;e<0+d;e++){var f=wa.get(e);f&&cd.set(f,e)}}if(cd.has(b))return cd.get(b);if(dd.length)d=dd.pop();else{try{wa.grow(1)}catch(n){if(!(n instanceof RangeError))throw n;throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.";}d=wa.length-1}try{wa.set(d,b)}catch(n){if(!(n instanceof TypeError))throw n;if("function"==typeof WebAssembly.Function){e=WebAssembly.Function;f={i:"i32",j:"i32",f:"f32",d:"f64",p:"i32"};for(var g=
{parameters:[],results:"v"==c[0]?[]:[f[c[0]]]},h=1;h<c.length;++h)g.parameters.push(f[c[h]]),"j"===c[h]&&g.parameters.push("i32");c=new e(g,b)}else{e=[1];f=c.slice(0,1);c=c.slice(1);g={i:127,p:127,j:126,f:125,d:124};e.push(96);h=c.length;128>h?e.push(h):e.push(h%128|128,h>>7);for(h=0;h<c.length;++h)e.push(g[c[h]]);"v"==f?e.push(0):e.push(1,g[f]);c=[0,97,115,109,1,0,0,0,1];f=e.length;128>f?c.push(f):c.push(f%128|128,f>>7);c.push.apply(c,e);c.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);c=new WebAssembly.Module(new Uint8Array(c));

View File

@@ -83,7 +83,7 @@ h=c=0;h<d;h++){var p=C[b>>2],l=C[b+4>>2];b+=8;var k=f,n=p,q=l,r=g,w=y;if(0>q||0>
typeof X||!(H instanceof P))throw H;return H.Lf}},i:function(b,c,d,e,f){try{c=d+2097152>>>0<4194305-!!c?(c>>>0)+4294967296*d:NaN;if(isNaN(c))return 61;var g=Y(b);Bb(g,c,e);L=[g.position>>>0,(K=g.position,1<=+Math.abs(K)?0<K?(Math.min(+Math.floor(K/4294967296),4294967295)|0)>>>0:~~+Math.ceil((K-+(~~K>>>0))/4294967296)>>>0:0)];B[f>>2]=L[0];B[f+4>>2]=L[1];g.lg&&0===c&&0===e&&(g.lg=null);return 0}catch(h){if("undefined"==typeof X||!(h instanceof P))throw h;return h.Lf}},E:function(b){try{var c=Y(b);return c.Jf&&
c.Jf.Xf?c.Jf.Xf(c):0}catch(d){if("undefined"==typeof X||!(d instanceof P))throw d;return d.Lf}},s:function(b,c,d,e){try{a:{var f=Y(b);b=c;for(var g,h=c=0;h<d;h++){var p=C[b>>2],l=C[b+4>>2];b+=8;var k=f,n=p,q=l,r=g,w=y;if(0>q||0>r)throw new P(28);if(null===k.Tf)throw new P(8);if(0===(k.flags&2097155))throw new P(8);if(16384===(k.node.mode&61440))throw new P(31);if(!k.Jf.write)throw new P(28);k.seekable&&k.flags&1024&&Bb(k,0,2);var z="undefined"!=typeof r;if(!z)r=k.position;else if(!k.seekable)throw new P(70);
var G=k.Jf.write(k,w,n,q,r,void 0);z||(k.position+=G);var D=G;if(0>D){var J=-1;break a}c+=D;"undefined"!==typeof g&&(g+=D)}J=c}C[e>>2]=J;return 0}catch(H){if("undefined"==typeof X||!(H instanceof P))throw H;return H.Lf}},sa:Tb,_:Ub,x:Vb,P:Wb,ca:Xb,I:Yb,ka:Zb,m:$b,ra:ac,na:bc,ia:cc,ea:dc,fa:ec,g:fc,h:gc,oa:hc,qa:ic,pa:jc,da:kc,ga:lc,ha:mc,ma:nc,c:oc,ja:pc,la:qc,aa:rc,V:sc,$:tc,ba:uc,S:vc,U:wc,Y:xc,X:yc,R:zc,n:Ac,T:Bc,Z:Cc,k:Dc,W:Ec,l:Fc};
(function(){function b(f){a.asm=f.exports;ma=a.asm.ua;wa();E=a.asm.Df;ya.unshift(a.asm.va);F--;a.monitorRunDependencies&&a.monitorRunDependencies(F);0==F&&(null!==Ca&&(clearInterval(Ca),Ca=null),Da&&(f=Da,Da=null,f()))}function c(f){b(f.instance)}function d(f){return Ha().then(function(g){return WebAssembly.instantiate(g,e)}).then(function(g){return g}).then(f,function(g){t("failed to asynchronously prepare wasm: "+g);u(g)})}var e={a:Oc};F++;a.monitorRunDependencies&&a.monitorRunDependencies(F);if(a.instantiateWasm)try{return a.instantiateWasm(e,
(function(){function b(f){a.asm=f.exports;ma=a.asm.ua;wa();E=a.asm.Cf;ya.unshift(a.asm.va);F--;a.monitorRunDependencies&&a.monitorRunDependencies(F);0==F&&(null!==Ca&&(clearInterval(Ca),Ca=null),Da&&(f=Da,Da=null,f()))}function c(f){b(f.instance)}function d(f){return Ha().then(function(g){return WebAssembly.instantiate(g,e)}).then(function(g){return g}).then(f,function(g){t("failed to asynchronously prepare wasm: "+g);u(g)})}var e={a:Oc};F++;a.monitorRunDependencies&&a.monitorRunDependencies(F);if(a.instantiateWasm)try{return a.instantiateWasm(e,
b)}catch(f){t("Module.instantiateWasm callback failed with error: "+f),ba(f)}(function(){return la||"function"!=typeof WebAssembly.instantiateStreaming||Ea()||"function"!=typeof fetch?d(c):fetch(I,{credentials:"same-origin"}).then(function(f){return WebAssembly.instantiateStreaming(f,e).then(c,function(g){t("wasm streaming compile failed: "+g);t("falling back to ArrayBuffer instantiation");return d(c)})})})().catch(ba);return{}})();
a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.va).apply(null,arguments)};a._sqlite3_status64=function(){return(a._sqlite3_status64=a.asm.wa).apply(null,arguments)};a._sqlite3_status=function(){return(a._sqlite3_status=a.asm.xa).apply(null,arguments)};a._sqlite3_db_status=function(){return(a._sqlite3_db_status=a.asm.ya).apply(null,arguments)};a._sqlite3_msize=function(){return(a._sqlite3_msize=a.asm.za).apply(null,arguments)};
a._sqlite3_vfs_find=function(){return(a._sqlite3_vfs_find=a.asm.Aa).apply(null,arguments)};a._sqlite3_vfs_register=function(){return(a._sqlite3_vfs_register=a.asm.Ba).apply(null,arguments)};a._sqlite3_vfs_unregister=function(){return(a._sqlite3_vfs_unregister=a.asm.Ca).apply(null,arguments)};a._sqlite3_release_memory=function(){return(a._sqlite3_release_memory=a.asm.Da).apply(null,arguments)};a._sqlite3_soft_heap_limit64=function(){return(a._sqlite3_soft_heap_limit64=a.asm.Ea).apply(null,arguments)};
@@ -137,12 +137,12 @@ a._sqlite3_table_column_metadata=function(){return(a._sqlite3_table_column_metad
a._sqlite3_test_control=function(){return(a._sqlite3_test_control=a.asm.Oe).apply(null,arguments)};a._sqlite3_create_filename=function(){return(a._sqlite3_create_filename=a.asm.Pe).apply(null,arguments)};a._sqlite3_free_filename=function(){return(a._sqlite3_free_filename=a.asm.Qe).apply(null,arguments)};a._sqlite3_uri_parameter=function(){return(a._sqlite3_uri_parameter=a.asm.Re).apply(null,arguments)};a._sqlite3_uri_key=function(){return(a._sqlite3_uri_key=a.asm.Se).apply(null,arguments)};
a._sqlite3_uri_boolean=function(){return(a._sqlite3_uri_boolean=a.asm.Te).apply(null,arguments)};a._sqlite3_uri_int64=function(){return(a._sqlite3_uri_int64=a.asm.Ue).apply(null,arguments)};a._sqlite3_filename_database=function(){return(a._sqlite3_filename_database=a.asm.Ve).apply(null,arguments)};a._sqlite3_filename_journal=function(){return(a._sqlite3_filename_journal=a.asm.We).apply(null,arguments)};a._sqlite3_filename_wal=function(){return(a._sqlite3_filename_wal=a.asm.Xe).apply(null,arguments)};
a._sqlite3_db_name=function(){return(a._sqlite3_db_name=a.asm.Ye).apply(null,arguments)};a._sqlite3_db_filename=function(){return(a._sqlite3_db_filename=a.asm.Ze).apply(null,arguments)};a._sqlite3_db_readonly=function(){return(a._sqlite3_db_readonly=a.asm._e).apply(null,arguments)};a._sqlite3_compileoption_used=function(){return(a._sqlite3_compileoption_used=a.asm.$e).apply(null,arguments)};a._sqlite3_compileoption_get=function(){return(a._sqlite3_compileoption_get=a.asm.af).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.cf).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.gf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.hf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.jf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.kf).apply(null,arguments)};a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.mf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.nf).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.of).apply(null,arguments)};var Nc=a.___errno_location=function(){return(Nc=a.___errno_location=a.asm.pf).apply(null,arguments)},Pb=a._malloc=function(){return(Pb=a._malloc=a.asm.qf).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};var Nc=a.___errno_location=function(){return(Nc=a.___errno_location=a.asm.cf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.gf).apply(null,arguments)};a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.hf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.jf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.kf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.mf).apply(null,arguments)};a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.nf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.of).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.pf).apply(null,arguments)};var Pb=a._malloc=function(){return(Pb=a._malloc=a.asm.qf).apply(null,arguments)};
a._free=function(){return(a._free=a.asm.rf).apply(null,arguments)};a._RegisterExtensionFunctions=function(){return(a._RegisterExtensionFunctions=a.asm.sf).apply(null,arguments)};a._sqlite3Fts5BetterTrigramInit=function(){return(a._sqlite3Fts5BetterTrigramInit=a.asm.tf).apply(null,arguments)};a._sqlite3Fts5HtmlInit=function(){return(a._sqlite3Fts5HtmlInit=a.asm.uf).apply(null,arguments)};a._set_authorizer=function(){return(a._set_authorizer=a.asm.vf).apply(null,arguments)};
a._create_function=function(){return(a._create_function=a.asm.wf).apply(null,arguments)};a._create_module=function(){return(a._create_module=a.asm.xf).apply(null,arguments)};a._progress_handler=function(){return(a._progress_handler=a.asm.yf).apply(null,arguments)};a._register_vfs=function(){return(a._register_vfs=a.asm.zf).apply(null,arguments)};a._getSqliteFree=function(){return(a._getSqliteFree=a.asm.Af).apply(null,arguments)};a._main=function(){return(a._main=a.asm.Bf).apply(null,arguments)};
var Za=a._emscripten_builtin_memalign=function(){return(Za=a._emscripten_builtin_memalign=a.asm.Cf).apply(null,arguments)},Pc=a.getTempRet0=function(){return(Pc=a.getTempRet0=a.asm.Ef).apply(null,arguments)},Kc=a.stackSave=function(){return(Kc=a.stackSave=a.asm.Ff).apply(null,arguments)},Lc=a.stackRestore=function(){return(Lc=a.stackRestore=a.asm.Gf).apply(null,arguments)},Jc=a.stackAlloc=function(){return(Jc=a.stackAlloc=a.asm.Hf).apply(null,arguments)};a._sqlite3_version=3232;a.UTF8ToString=v;
var Za=a._emscripten_builtin_memalign=function(){return(Za=a._emscripten_builtin_memalign=a.asm.Df).apply(null,arguments)},Pc=a.getTempRet0=function(){return(Pc=a.getTempRet0=a.asm.Ef).apply(null,arguments)},Kc=a.stackSave=function(){return(Kc=a.stackSave=a.asm.Ff).apply(null,arguments)},Lc=a.stackRestore=function(){return(Lc=a.stackRestore=a.asm.Gf).apply(null,arguments)},Jc=a.stackAlloc=function(){return(Jc=a.stackAlloc=a.asm.Hf).apply(null,arguments)};a._sqlite3_version=60912;a.UTF8ToString=v;
a.stringToUTF8=ra;a.lengthBytesUTF8=sa;a.getTempRet0=Pc;a.ccall=Z;a.cwrap=function(b,c,d,e){d=d||[];var f=d.every(g=>"number"===g||"boolean"===g);return"string"!==c&&f&&!e?a["_"+b]:function(){return Z(b,c,d,arguments,e)}};
a.addFunction=function(b,c){if(!Gc){Gc=new WeakMap;var d=E.length;if(Gc)for(var e=0;e<0+d;e++){var f=E.get(e);f&&Gc.set(f,e)}}if(Gc.has(b))return Gc.get(b);if(Hc.length)d=Hc.pop();else{try{E.grow(1)}catch(p){if(!(p instanceof RangeError))throw p;throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.";}d=E.length-1}try{E.set(d,b)}catch(p){if(!(p instanceof TypeError))throw p;if("function"==typeof WebAssembly.Function){e=WebAssembly.Function;f={i:"i32",j:"i32",f:"f32",d:"f64",p:"i32"};for(var g={parameters:[],
results:"v"==c[0]?[]:[f[c[0]]]},h=1;h<c.length;++h)g.parameters.push(f[c[h]]),"j"===c[h]&&g.parameters.push("i32");c=new e(g,b)}else{e=[1];f=c.slice(0,1);c=c.slice(1);g={i:127,p:127,j:126,f:125,d:124};e.push(96);h=c.length;128>h?e.push(h):e.push(h%128|128,h>>7);for(h=0;h<c.length;++h)e.push(g[c[h]]);"v"==f?e.push(0):e.push(1,g[f]);c=[0,97,115,109,1,0,0,0,1];f=e.length;128>f?c.push(f):c.push(f%128|128,f>>7);c.push.apply(c,e);c.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);c=new WebAssembly.Module(new Uint8Array(c));

View File

@@ -378,7 +378,7 @@ function TipTap(props: TipTapProps) {
<ScrollContainer
className="toolbarScroll"
suppressScrollY
style={{ display: "flex" }}
style={{ display: "flex", overscrollBehavior: "contain" }}
trackStyle={() => ({
backgroundColor: "transparent",
"--ms-track-size": "6px"

View File

@@ -39,9 +39,10 @@ function TitleBox(props: TitleBoxProps) {
const inputRef = useRef<HTMLTextAreaElement>(null);
const pendingChanges = useRef(false);
const sessionType = useEditorStore((store) => store.getSession(id)?.type);
const sessionTitle = useEditorStore(
(store) => store.getSession(id, ["default"])?.note.title
);
const sessionTitle = useEditorStore((store) => {
const session = store.getSession(id, ["default"]);
return session?.note.isGeneratedTitle ? session.note.title : null;
});
const { editorConfig } = useEditorConfig();
const dateFormat = useSettingsStore((store) => store.dateFormat);
const timeFormat = useSettingsStore((store) => store.timeFormat);

View File

@@ -113,6 +113,7 @@ type Route = {
path: string;
icon: Icon;
tag?: string;
loginRequired?: boolean;
};
const routes: Route[] = [
@@ -133,7 +134,8 @@ const routes: Route[] = [
id: "monographs",
title: strings.routes.Monographs(),
path: "/monographs",
icon: Monographs
icon: Monographs,
loginRequired: true
},
{ id: "trash", title: strings.routes.Trash(), path: "/trash", icon: Trash },
{
@@ -449,9 +451,12 @@ function Routes({
collapse: () => void;
}) {
const hiddenRoutes = useAppStore((store) => store.hiddenRoutes);
const isLoggedIn = useUserStore((store) => store.isLoggedIn);
return (
<ReorderableList
items={routes.filter((r) => !hiddenRoutes.includes(r.id))}
items={routes
.filter((r) => !hiddenRoutes.includes(r.id))
.filter((r) => (r.loginRequired ? isLoggedIn : true))}
orderKey={`sidebarOrder:routes`}
order={() => db.settings.getSideBarOrder("routes")}
onOrderChanged={(order) => db.settings.setSideBarOrder("routes", order)}

View File

@@ -19,17 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import ListItem from "../list-item";
import { Restore, DeleteForver } from "../icons";
import { store } from "../../stores/trash-store";
import { Flex, Text } from "@theme-ui/components";
import TimeAgo from "../time-ago";
import { pluralize, toTitleCase } from "@notesnook/common";
import { showToast } from "../../utils/toast";
import { toTitleCase } from "@notesnook/common";
import { MenuItem } from "@notesnook/ui";
import { TrashItem as TrashItemType } from "@notesnook/core";
import { useEditorStore } from "../../stores/editor-store";
import { showMultiPermanentDeleteConfirmation } from "../../dialogs/confirm";
import { useStore as useSelectionStore } from "../../stores/selection-store";
import { strings } from "@notesnook/intl";
import { Multiselect } from "../../common/multi-select";
type TrashItemProps = { item: TrashItemType; date: number };
function TrashItem(props: TrashItemProps) {
@@ -44,7 +42,9 @@ function TrashItem(props: TrashItemProps) {
body={item.itemType === "note" ? item.headline : item.description}
onKeyPress={async (e) => {
if (e.key === "Delete") {
await deleteTrash(useSelectionStore.getState().selectedItems);
await Multiselect.deleteItemsFromTrash(
useSelectionStore.getState().selectedItems
);
}
}}
footer={
@@ -81,9 +81,7 @@ export const trashMenuItems: (
key: "restore",
title: strings.restore(),
icon: Restore.path,
onClick: async () => {
await store.restore(...ids);
},
onClick: () => Multiselect.restoreItemsFromTrash(ids),
multiSelect: true
},
{
@@ -92,16 +90,8 @@ export const trashMenuItems: (
title: strings.delete(),
icon: DeleteForver.path,
variant: "dangerous",
onClick: async () => {
await deleteTrash(ids);
},
onClick: () => Multiselect.deleteItemsFromTrash(ids),
multiSelect: true
}
];
};
export async function deleteTrash(ids: string[]) {
if (!(await showMultiPermanentDeleteConfirmation(ids.length))) return;
await store.delete(...ids);
showToast("success", `${pluralize(ids.length, "item")} permanently deleted`);
}

View File

@@ -1,5 +1,5 @@
- You can now share multiple files to Notesnook
- Fix file and image sharing not working
- Many other bug fixes and small improvements
- New and improved search with highlighting
- Fixed app crashing on android 16 devices
- Many bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,5 @@
- New and improved search with highlighting
- Fixed app crashing on android 16 devices
- Many bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,4 @@
- Fix toolbar hidden behing keyboard on android 15 and 16
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,5 @@
- Fix toolbar hidden behing keyboard on android 15 and 16
- Fix sync performance issues
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,4 @@
- Fix PDF files get stuck on first page
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,4 @@
- Fix crash on device boot on android 15 devices
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,5 @@
- Fix crash on device boot on android 15 devices
- Fix note not opening in editor randomly
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -85,7 +85,7 @@
"refractor": "^4.8.1",
"sqlite-better-trigram": "^0.0.3",
"sqlite-regex": "^0.2.4-alpha.1",
"sqlite3-fts5-html": "^0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"vitest": "2.1.8",
"vitest-fetch-mock": "^0.2.2",
"ws": "^8.13.0"

View File

@@ -48,11 +48,12 @@ export async function resolveItems(ids: string[], items: Item[]) {
const { type } = items[0];
if (type === "note") return resolveNotes(ids);
else if (type === "notebook") {
return Promise.all(ids.map((id) => db.notebooks.totalNotes(id)));
return await db.notebooks.totalNotes(...ids);
} else if (type === "tag") {
return Promise.all(
ids.map((id) => db.relations.from({ id, type: "tag" }, "note").count())
);
const relations = await db.relations
.from({ type: "tag", ids }, "note")
.get();
return ids.map((id) => relations.filter((r) => r.fromId === id).length);
}
return [];
}

View File

@@ -99,8 +99,8 @@ test("delete note", () =>
await db.notes.moveToTrash(id);
expect(await db.notes.note(id)).toBeUndefined();
expect(await db.notebooks.totalNotes(notebookId)).toBe(0);
expect(await db.notebooks.totalNotes(subNotebookId)).toBe(0);
expect(await db.notebooks.totalNotes(notebookId)).toStrictEqual([0]);
expect(await db.notebooks.totalNotes(subNotebookId)).toStrictEqual([0]);
}));
test("get all notes", () =>
@@ -323,8 +323,8 @@ test("add note to subnotebook", () =>
.from({ type: "notebook", id: notebookId }, "notebook")
.count()
).toBe(1);
expect(await db.notebooks.totalNotes(subNotebookId)).toBe(1);
expect(await db.notebooks.totalNotes(notebookId)).toBe(1);
expect(await db.notebooks.totalNotes(subNotebookId)).toStrictEqual([1]);
expect(await db.notebooks.totalNotes(notebookId)).toStrictEqual([1]);
}));
test("duplicate note to topic should not be added", () =>
@@ -333,7 +333,7 @@ test("duplicate note to topic should not be added", () =>
notebookTitle: "Hello",
subNotebookTitle: "Home"
});
expect(await db.notebooks.totalNotes(subNotebookId)).toBe(1);
expect(await db.notebooks.totalNotes(subNotebookId)).toStrictEqual([1]);
}));
test("add the same note to 2 notebooks", () =>

View File

@@ -62,7 +62,7 @@
"refractor": "^4.8.1",
"sqlite-better-trigram": "^0.0.3",
"sqlite-regex": "^0.2.4-alpha.1",
"sqlite3-fts5-html": "^0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"vitest": "2.1.8",
"vitest-fetch-mock": "^0.2.2",
"ws": "^8.13.0"
@@ -3445,24 +3445,24 @@
]
},
"node_modules/sqlite3-fts5-html": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html/-/sqlite3-fts5-html-0.0.3.tgz",
"integrity": "sha512-omJELqavUID4rikHgSnprzR8KoD+k34Gr//Z6Ju5ZJLI9d2MTv+63gJ/9iCWCmvt5dLODDBL25RzmXsChltjkQ==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html/-/sqlite3-fts5-html-0.0.4.tgz",
"integrity": "sha512-2d7G3QAgNtndTYRFDXVzzsjlDkjXLo9YB4xxVFIxHIotKnVTNmd0/iSQMMjOMFY5XEpW8yemFkD0EyuN0wFuSQ==",
"dev": true,
"license": "Public Domain",
"optionalDependencies": {
"sqlite3-fts5-html-darwin-arm64": "0.0.3",
"sqlite3-fts5-html-darwin-x64": "0.0.3",
"sqlite3-fts5-html-linux-arm64": "0.0.3",
"sqlite3-fts5-html-linux-x64": "0.0.3",
"sqlite3-fts5-html-windows-arm64": "0.0.3",
"sqlite3-fts5-html-windows-x64": "0.0.3"
"sqlite3-fts5-html-darwin-arm64": "0.0.4",
"sqlite3-fts5-html-darwin-x64": "0.0.4",
"sqlite3-fts5-html-linux-arm64": "0.0.4",
"sqlite3-fts5-html-linux-x64": "0.0.4",
"sqlite3-fts5-html-windows-arm64": "0.0.4",
"sqlite3-fts5-html-windows-x64": "0.0.4"
}
},
"node_modules/sqlite3-fts5-html-darwin-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-arm64/-/sqlite3-fts5-html-darwin-arm64-0.0.3.tgz",
"integrity": "sha512-jLmBOslggCYG+N8CVbjN/ok6kP9kCkWVYiXVF+ms4NylBPevs6+QBgY4GXNcVuSoRs2T8PT/gxI3yTnjdv/uDg==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-arm64/-/sqlite3-fts5-html-darwin-arm64-0.0.4.tgz",
"integrity": "sha512-zkg+LxOtq7ONz82C4+1mxsmasOIt9tbUReEMlmu29pJosfgDsMftp9k1Jy46FEk3VDFODlg9uDX7f0O0rspayg==",
"cpu": [
"arm64"
],
@@ -3474,9 +3474,9 @@
]
},
"node_modules/sqlite3-fts5-html-darwin-x64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-x64/-/sqlite3-fts5-html-darwin-x64-0.0.3.tgz",
"integrity": "sha512-zhfkryrXf9UOS/vG9LEUs3sLZTyfgkkX316OAuSVhB0At9oa7rYc3FDiypNh6n3PMr+B3uySC6f9mipBGcsOiQ==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-darwin-x64/-/sqlite3-fts5-html-darwin-x64-0.0.4.tgz",
"integrity": "sha512-LtBRTVfB3eNkupKJEobZVOyf4TbmWw4XgdBGvi4wFHHcv96gqhVlHdUGlavul561AOpab+T7DOgaPgph1y5M5A==",
"cpu": [
"x64"
],
@@ -3488,9 +3488,9 @@
]
},
"node_modules/sqlite3-fts5-html-linux-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-arm64/-/sqlite3-fts5-html-linux-arm64-0.0.3.tgz",
"integrity": "sha512-FoEwY8I7KHQ0iOtobskKFCqIgFbi+X97h733kDgAN5B9ADK6VHL6/jpLhJLzLtlSVgPedPLTTXnhgP2abQR0eg==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-arm64/-/sqlite3-fts5-html-linux-arm64-0.0.4.tgz",
"integrity": "sha512-+xgRPpD7fdXNJPKaggHE5BkSF00aI8rlHHZ+pXZR2GmFyRr++jt34MA5avO4G6oI4mrZ82wUV+sWUqd8FErRBw==",
"cpu": [
"arm64"
],
@@ -3502,9 +3502,9 @@
]
},
"node_modules/sqlite3-fts5-html-linux-x64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-x64/-/sqlite3-fts5-html-linux-x64-0.0.3.tgz",
"integrity": "sha512-wJ2ONYC8Rs/HSWHVI2Y3j7vMh+6QzcA3SYCWAFJEFPM3aCyvf7lFhQgLSHZWkS+OUKduP+9/e/Hc7g3UwHdtig==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-linux-x64/-/sqlite3-fts5-html-linux-x64-0.0.4.tgz",
"integrity": "sha512-4ah7IiDUH9kpCPoqT4lW6FcWcNiVF0wtpywW82UVfkawbmgLFPGqSQFqHyChmp1b2dsrkO5X/bhq9E2BR0/xmQ==",
"cpu": [
"x64"
],
@@ -3516,9 +3516,9 @@
]
},
"node_modules/sqlite3-fts5-html-windows-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-arm64/-/sqlite3-fts5-html-windows-arm64-0.0.3.tgz",
"integrity": "sha512-58Qi1NTe/Po+nWz2z24ziSvghQcrMC94/mxmErNeC4SFPZMN7JLx56W2eybJB436CiUUUN9MT4a6BD1bn7RpQg==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-arm64/-/sqlite3-fts5-html-windows-arm64-0.0.4.tgz",
"integrity": "sha512-rQKLrX90TKIMgD7zSrOk/U6q0KEBiTaB2nXDONcK4d/CYpnG9g/N/i6Ady98j5+v/PgqymchgtRwvgBHOZzCfw==",
"cpu": [
"arm64"
],
@@ -3530,9 +3530,9 @@
]
},
"node_modules/sqlite3-fts5-html-windows-x64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-x64/-/sqlite3-fts5-html-windows-x64-0.0.3.tgz",
"integrity": "sha512-LYilVGRYXpN0nra3LL7CXlfaLbPmBK78rtaG9Aivufqexqjc2sB+v+xMGT8vxLXOjIAtkBLko2ELuYe8hRLQMw==",
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/sqlite3-fts5-html-windows-x64/-/sqlite3-fts5-html-windows-x64-0.0.4.tgz",
"integrity": "sha512-FxLloBxWtsIGUMxbvOLNY9+FXJYEcku1xVqJarLfGKcXX7zvS3yXMP0dYeld2lQ/evNiLgvLMNLhUFwhEln1vA==",
"cpu": [
"x64"
],

View File

@@ -48,7 +48,7 @@
"refractor": "^4.8.1",
"sqlite-better-trigram": "^0.0.3",
"sqlite-regex": "^0.2.4-alpha.1",
"sqlite3-fts5-html": "^0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"vitest": "2.1.8",
"vitest-fetch-mock": "^0.2.2",
"ws": "^8.13.0"

View File

@@ -22,8 +22,8 @@ import Database from "../api/index.js";
import { Notebook, TrashOrItem, isTrashItem } from "../types.js";
import { ICollection } from "./collection.js";
import { SQLCollection } from "../database/sql-collection.js";
import { isFalse } from "../database/index.js";
import { sql } from "@streetwriters/kysely";
import { DatabaseSchema, isFalse } from "../database/index.js";
import { Kysely, sql, Transaction } from "@streetwriters/kysely";
import { deleteItems } from "../utils/array.js";
import {
CHECK_IDS,
@@ -125,65 +125,45 @@ export class Notebooks implements ICollection {
await this.collection.update(ids, { pinned: state });
}
async totalNotes(id: string) {
const result = await this.db
.sql()
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectNoFrom((eb) => eb.val(id).as("id"))
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.db.trash.cache.notebooks)
.$narrowType<{ id: string }>()
)
)
async totalNotes(...ids: string[]) {
const result = await withSubNotebooks(
this.db.sql(),
ids,
this.db.trash.cache.notebooks
)
.selectFrom("relations")
.innerJoin("subNotebooks", "subNotebooks.id", "relations.fromId")
.where("toType", "==", "note")
.where("fromType", "==", "notebook")
.where("fromId", "in", (eb) =>
eb.selectFrom("subNotebooks").select("subNotebooks.id")
)
.where("toId", "not in", this.db.trash.cache.notes)
.select((eb) => eb.fn.count<number>("relations.toId").as("totalNotes"))
.executeTakeFirst();
if (!result) return 0;
return result.totalNotes;
}
async notes(id: string) {
const result = await this.db
.sql()
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectNoFrom((eb) => eb.val(id).as("id"))
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.db.trash.cache.notebooks)
.$narrowType<{ id: string }>()
)
)
.selectFrom("relations")
.where("toType", "==", "note")
.where("fromType", "==", "notebook")
.where("fromId", "in", (eb) =>
eb.selectFrom("subNotebooks").select("subNotebooks.id")
)
.where("toId", "not in", this.db.trash.cache.notes)
.select("relations.toId as id")
.$narrowType<{ id: string }>()
.select((eb) => [
"subNotebooks.rootId as id",
eb.fn.count<number>("relations.toId").distinct().as("totalNotes")
])
.groupBy("subNotebooks.rootId")
.execute();
return ids.map((id) => {
const item = result.find((i) => i.id === id);
return item ? item.totalNotes : 0;
});
}
async notes(...ids: string[]) {
const result = await withSubNotebooks(
this.db.sql(),
ids,
this.db.trash.cache.notebooks
)
.selectFrom("relations")
.innerJoin("subNotebooks", "subNotebooks.id", "relations.fromId")
.where("toType", "==", "note")
.where("fromType", "==", "notebook")
.where("toId", "not in", this.db.trash.cache.notes)
.select("relations.toId as id")
.distinct()
.$narrowType<{ id: string }>()
.execute();
return result.map((i) => i.id);
}
@@ -254,26 +234,7 @@ export class Notebooks implements ICollection {
async moveToTrash(...ids: string[]) {
await this.db.transaction(async (tr) => {
const query = tr
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectFrom(() =>
sql<{ id: string }>`(VALUES ${sql.join(
ids.map((id) => sql.raw(`('${id}')`))
)})`.as("roots")
)
.selectAll()
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.db.trash.cache.notebooks)
.$narrowType<{ id: string }>()
)
)
const query = withSubNotebooks(tr, ids, this.db.trash.cache.notebooks)
.selectFrom("subNotebooks")
.select("id");
@@ -305,3 +266,45 @@ export class Notebooks implements ICollection {
return relation[0]?.fromId;
}
}
export function withSubNotebooks(
db: Kysely<DatabaseSchema> | Transaction<DatabaseSchema>,
ids: string[],
excluded: string[]
) {
return db.withRecursive(`subNotebooks(id, path, rootId)`, (eb) =>
eb
.selectFrom(() =>
sql<{
id: string;
path: string;
rootId: string;
}>`(VALUES ${sql.join(
ids.map((id) => sql.raw(`('${id}', '${id}', '${id}')`))
)})`.as("roots")
)
.selectAll()
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select([
"relations.toId as id",
// Concatenate parent path with current id
sql<string>`subNotebooks.path || '/' || relations.toId`.as("path"),
// Preserve original root
"subNotebooks.rootId as rootId"
])
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", excluded)
// Use path to prevent cycles
.where(
"subNotebooks.path",
"not like",
sql`'%' || relations.toId || '%'`
)
.$narrowType<{ id: string; path: string; rootId: string }>()
)
);
}

View File

@@ -34,6 +34,7 @@ import {
checkIsUserPremium,
FREE_NOTEBOOKS_LIMIT
} from "../common.js";
import { withSubNotebooks } from "./notebooks.js";
export default class Trash {
collections = ["notes", "notebooks"] as const;
@@ -324,27 +325,11 @@ export default class Trash {
}
private async subNotebooks(notebookIds: string[]) {
const ids = await this.db
.sql()
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectFrom((eb) =>
sql<{ id: string }>`(VALUES ${sql.join(
notebookIds.map((id) => eb.parens(sql`${id}`))
)})`.as("notebookIds")
)
.selectAll()
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.userDeletedCache.notebooks)
.$narrowType<{ id: string }>()
)
)
const ids = await withSubNotebooks(
this.db.sql(),
notebookIds,
this.userDeletedCache.notebooks
)
.selectFrom("subNotebooks")
.select("id")
.where("id", "not in", notebookIds)

View File

@@ -44,10 +44,12 @@ export default function TiptapEditorWrapper(props: {
display: props.settings.noToolbar ? "none" : "flex",
overflowY: "hidden",
minHeight: "50px",
position: "absolute",
bottom: 0,
left: 0,
right: 0
...(globalThis.__PLATFORM__ === "ios" && {
position: "absolute",
bottom: 0,
left: 0,
right: 0
})
}}
editor={editor}
location="bottom"

View File

@@ -111,13 +111,7 @@ export function ClearFormatting(props: ToolProps) {
{...props}
toggled={false}
onClick={() =>
editor
?.chain()
.focus()
.clearNodes()
.unsetAllMarks()
.unsetMark("link")
.run()
editor?.chain().focus().unsetAllMarks().unsetMark("link").run()
}
/>
);

View File

@@ -1,17 +1,11 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-06-05 11:43+0500\n"
"POT-Creation-Date: 2025-06-21 11:05+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/strings.ts:2410
msgid " \"Notebook > Notes\""
@@ -157,7 +151,7 @@ msgstr "{count, plural, one {Attachment deleted} other {# attachments deleted}}"
msgid "{count, plural, one {Attachment downloaded at {path}} other {#/{total} attachments downloaded as a zip file at {path}}}"
msgstr "{count, plural, one {Attachment downloaded at {path}} other {#/{total} attachments downloaded as a zip file at {path}}}"
#: generated/actions.ts:210
#: generated/actions.ts:216
msgid "{count, plural, one {Color renamed} other {# colors renamed}}"
msgstr "{count, plural, one {Color renamed} other {# colors renamed}}"
@@ -250,7 +244,7 @@ msgstr "{count, plural, one {Item could not be published} other {# items could n
msgid "{count, plural, one {Item could not be unpublished} other {# items could not be unpublished}}"
msgstr "{count, plural, one {Item could not be unpublished} other {# items could not be unpublished}}"
#: generated/actions.ts:202
#: generated/actions.ts:208
msgid "{count, plural, one {Item created} other {# items created}}"
msgstr "{count, plural, one {Item created} other {# items created}}"
@@ -258,7 +252,7 @@ msgstr "{count, plural, one {Item created} other {# items created}}"
msgid "{count, plural, one {Item deleted} other {# items deleted}}"
msgstr "{count, plural, one {Item deleted} other {# items deleted}}"
#: generated/actions.ts:173
#: generated/actions.ts:179
msgid "{count, plural, one {Item edited} other {# items edited}}"
msgstr "{count, plural, one {Item edited} other {# items edited}}"
@@ -266,24 +260,25 @@ msgstr "{count, plural, one {Item edited} other {# items edited}}"
msgid "{count, plural, one {Item moved to trash} other {# items moved to trash}}"
msgstr "{count, plural, one {Item moved to trash} other {# items moved to trash}}"
#: generated/actions.ts:87
#: generated/actions.ts:80
#: generated/actions.ts:93
msgid "{count, plural, one {Item permanently deleted} other {# items permanently deleted}}"
msgstr "{count, plural, one {Item permanently deleted} other {# items permanently deleted}}"
#: generated/actions.ts:104
#: generated/actions.ts:110
msgid "{count, plural, one {Item published} other {# items published}}"
msgstr "{count, plural, one {Item published} other {# items published}}"
#: generated/actions.ts:219
#: generated/actions.ts:225
msgid "{count, plural, one {Item renamed} other {# items renamed}}"
msgstr "{count, plural, one {Item renamed} other {# items renamed}}"
#: generated/actions.ts:137
#: generated/actions.ts:150
#: generated/actions.ts:143
#: generated/actions.ts:156
msgid "{count, plural, one {Item restored} other {# items restored}}"
msgstr "{count, plural, one {Item restored} other {# items restored}}"
#: generated/actions.ts:121
#: generated/actions.ts:127
msgid "{count, plural, one {Item unpublished} other {# items unpublished}}"
msgstr "{count, plural, one {Item unpublished} other {# items unpublished}}"
@@ -323,15 +318,15 @@ msgstr "{count, plural, one {Note moved to trash} other {# notes moved to trash}
msgid "{count, plural, one {Note permanently deleted} other {# notes permanently deleted}}"
msgstr "{count, plural, one {Note permanently deleted} other {# notes permanently deleted}}"
#: generated/actions.ts:95
#: generated/actions.ts:101
msgid "{count, plural, one {Note published} other {# notes published}}"
msgstr "{count, plural, one {Note published} other {# notes published}}"
#: generated/actions.ts:129
#: generated/actions.ts:135
msgid "{count, plural, one {Note restored} other {# notes restored}}"
msgstr "{count, plural, one {Note restored} other {# notes restored}}"
#: generated/actions.ts:112
#: generated/actions.ts:118
msgid "{count, plural, one {Note unpublished} other {# notes unpublished}}"
msgstr "{count, plural, one {Note unpublished} other {# notes unpublished}}"
@@ -339,7 +334,7 @@ msgstr "{count, plural, one {Note unpublished} other {# notes unpublished}}"
msgid "{count, plural, one {Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?} other {# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?}}"
msgstr "{count, plural, one {Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?} other {# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?}}"
#: generated/actions.ts:181
#: generated/actions.ts:187
msgid "{count, plural, one {Notebook created} other {# notebooks created}}"
msgstr "{count, plural, one {Notebook created} other {# notebooks created}}"
@@ -347,7 +342,7 @@ msgstr "{count, plural, one {Notebook created} other {# notebooks created}}"
msgid "{count, plural, one {Notebook deleted} other {# notebooks deleted}}"
msgstr "{count, plural, one {Notebook deleted} other {# notebooks deleted}}"
#: generated/actions.ts:162
#: generated/actions.ts:168
msgid "{count, plural, one {Notebook edited} other {# notebooks edited}}"
msgstr "{count, plural, one {Notebook edited} other {# notebooks edited}}"
@@ -359,7 +354,7 @@ msgstr "{count, plural, one {Notebook moved to trash} other {# notebooks moved t
msgid "{count, plural, one {Notebook permanently deleted} other {# notebooks permanently deleted}}"
msgstr "{count, plural, one {Notebook permanently deleted} other {# notebooks permanently deleted}}"
#: generated/actions.ts:133
#: generated/actions.ts:139
msgid "{count, plural, one {Notebook restored} other {# notebooks restored}}"
msgstr "{count, plural, one {Notebook restored} other {# notebooks restored}}"
@@ -372,6 +367,11 @@ msgstr "{count, plural, one {Permanently delete attachment} other {Permanently d
msgid "{count, plural, one {Permanently delete item} other {Permanently delete # items}}"
msgstr "{count, plural, one {Permanently delete item} other {Permanently delete # items}}"
#: generated/in-progress-actions.ts:66
#: generated/in-progress-actions.ts:75
msgid "{count, plural, one {permanently deleting item...} other {permanently deleting # items...}}"
msgstr "{count, plural, one {permanently deleting item...} other {permanently deleting # items...}}"
#: generated/do-actions.ts:99
msgid "{count, plural, one {Pin item} other {Pin # items}}"
msgstr "{count, plural, one {Pin item} other {Pin # items}}"
@@ -444,11 +444,16 @@ msgstr "{count, plural, one {Restore note} other {Restore # notes}}"
msgid "{count, plural, one {Restore notebook} other {Restore # notebooks}}"
msgstr "{count, plural, one {Restore notebook} other {Restore # notebooks}}"
#: generated/actions.ts:189
#: generated/in-progress-actions.ts:49
#: generated/in-progress-actions.ts:58
msgid "{count, plural, one {Restoring item...} other {Restoring # items...}}"
msgstr "{count, plural, one {Restoring item...} other {Restoring # items...}}"
#: generated/actions.ts:195
msgid "{count, plural, one {Shortcut created} other {# shortcuts created}}"
msgstr "{count, plural, one {Shortcut created} other {# shortcuts created}}"
#: generated/actions.ts:185
#: generated/actions.ts:191
msgid "{count, plural, one {Tag created} other {# tags created}}"
msgstr "{count, plural, one {Tag created} other {# tags created}}"
@@ -456,7 +461,7 @@ msgstr "{count, plural, one {Tag created} other {# tags created}}"
msgid "{count, plural, one {Tag deleted} other {# tags deleted}}"
msgstr "{count, plural, one {Tag deleted} other {# tags deleted}}"
#: generated/actions.ts:158
#: generated/actions.ts:164
msgid "{count, plural, one {Tag edited} other {# tags edited}}"
msgstr "{count, plural, one {Tag edited} other {# tags edited}}"
@@ -613,8 +618,8 @@ msgid "A to Z"
msgstr "A to Z"
#: src/strings.ts:447
msgid "A vault stores your notes in a encrypted storage."
msgstr "A vault stores your notes in a encrypted storage."
msgid "A vault stores your notes in an encrypted storage."
msgstr "A vault stores your notes in an encrypted storage."
#: src/strings.ts:661
msgid "Abc"

View File

@@ -1,17 +1,11 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-06-05 11:43+0500\n"
"POT-Creation-Date: 2025-06-21 11:05+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pseudo-LOCALE\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/strings.ts:2410
msgid " \"Notebook > Notes\""
@@ -157,7 +151,7 @@ msgstr ""
msgid "{count, plural, one {Attachment downloaded at {path}} other {#/{total} attachments downloaded as a zip file at {path}}}"
msgstr ""
#: generated/actions.ts:210
#: generated/actions.ts:216
msgid "{count, plural, one {Color renamed} other {# colors renamed}}"
msgstr ""
@@ -250,7 +244,7 @@ msgstr ""
msgid "{count, plural, one {Item could not be unpublished} other {# items could not be unpublished}}"
msgstr ""
#: generated/actions.ts:202
#: generated/actions.ts:208
msgid "{count, plural, one {Item created} other {# items created}}"
msgstr ""
@@ -258,7 +252,7 @@ msgstr ""
msgid "{count, plural, one {Item deleted} other {# items deleted}}"
msgstr ""
#: generated/actions.ts:173
#: generated/actions.ts:179
msgid "{count, plural, one {Item edited} other {# items edited}}"
msgstr ""
@@ -266,24 +260,25 @@ msgstr ""
msgid "{count, plural, one {Item moved to trash} other {# items moved to trash}}"
msgstr ""
#: generated/actions.ts:87
#: generated/actions.ts:80
#: generated/actions.ts:93
msgid "{count, plural, one {Item permanently deleted} other {# items permanently deleted}}"
msgstr ""
#: generated/actions.ts:104
#: generated/actions.ts:110
msgid "{count, plural, one {Item published} other {# items published}}"
msgstr ""
#: generated/actions.ts:219
#: generated/actions.ts:225
msgid "{count, plural, one {Item renamed} other {# items renamed}}"
msgstr ""
#: generated/actions.ts:137
#: generated/actions.ts:150
#: generated/actions.ts:143
#: generated/actions.ts:156
msgid "{count, plural, one {Item restored} other {# items restored}}"
msgstr ""
#: generated/actions.ts:121
#: generated/actions.ts:127
msgid "{count, plural, one {Item unpublished} other {# items unpublished}}"
msgstr ""
@@ -323,15 +318,15 @@ msgstr ""
msgid "{count, plural, one {Note permanently deleted} other {# notes permanently deleted}}"
msgstr ""
#: generated/actions.ts:95
#: generated/actions.ts:101
msgid "{count, plural, one {Note published} other {# notes published}}"
msgstr ""
#: generated/actions.ts:129
#: generated/actions.ts:135
msgid "{count, plural, one {Note restored} other {# notes restored}}"
msgstr ""
#: generated/actions.ts:112
#: generated/actions.ts:118
msgid "{count, plural, one {Note unpublished} other {# notes unpublished}}"
msgstr ""
@@ -339,7 +334,7 @@ msgstr ""
msgid "{count, plural, one {Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?} other {# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?}}"
msgstr ""
#: generated/actions.ts:181
#: generated/actions.ts:187
msgid "{count, plural, one {Notebook created} other {# notebooks created}}"
msgstr ""
@@ -347,7 +342,7 @@ msgstr ""
msgid "{count, plural, one {Notebook deleted} other {# notebooks deleted}}"
msgstr ""
#: generated/actions.ts:162
#: generated/actions.ts:168
msgid "{count, plural, one {Notebook edited} other {# notebooks edited}}"
msgstr ""
@@ -359,7 +354,7 @@ msgstr ""
msgid "{count, plural, one {Notebook permanently deleted} other {# notebooks permanently deleted}}"
msgstr ""
#: generated/actions.ts:133
#: generated/actions.ts:139
msgid "{count, plural, one {Notebook restored} other {# notebooks restored}}"
msgstr ""
@@ -372,6 +367,11 @@ msgstr ""
msgid "{count, plural, one {Permanently delete item} other {Permanently delete # items}}"
msgstr ""
#: generated/in-progress-actions.ts:66
#: generated/in-progress-actions.ts:75
msgid "{count, plural, one {permanently deleting item...} other {permanently deleting # items...}}"
msgstr ""
#: generated/do-actions.ts:99
msgid "{count, plural, one {Pin item} other {Pin # items}}"
msgstr ""
@@ -444,11 +444,16 @@ msgstr ""
msgid "{count, plural, one {Restore notebook} other {Restore # notebooks}}"
msgstr ""
#: generated/actions.ts:189
#: generated/in-progress-actions.ts:49
#: generated/in-progress-actions.ts:58
msgid "{count, plural, one {Restoring item...} other {Restoring # items...}}"
msgstr ""
#: generated/actions.ts:195
msgid "{count, plural, one {Shortcut created} other {# shortcuts created}}"
msgstr ""
#: generated/actions.ts:185
#: generated/actions.ts:191
msgid "{count, plural, one {Tag created} other {# tags created}}"
msgstr ""
@@ -456,7 +461,7 @@ msgstr ""
msgid "{count, plural, one {Tag deleted} other {# tags deleted}}"
msgstr ""
#: generated/actions.ts:158
#: generated/actions.ts:164
msgid "{count, plural, one {Tag edited} other {# tags edited}}"
msgstr ""
@@ -613,7 +618,7 @@ msgid "A to Z"
msgstr ""
#: src/strings.ts:447
msgid "A vault stores your notes in a encrypted storage."
msgid "A vault stores your notes in an encrypted storage."
msgstr ""
#: src/strings.ts:661

View File

@@ -83,7 +83,7 @@ const ACTIONS = [
{
action: "permanentlyDeleted",
label: "permanently deleted",
dataTypes: ["note", "notebook"]
dataTypes: ["note", "notebook", "item"]
},
{ action: "published", label: "published", dataTypes: ["note"] },
{ action: "unpublished", label: "unpublished", dataTypes: ["note"] },
@@ -121,7 +121,7 @@ const ACTION_CONFIRMATIONS = [
"tag",
"color",
"attachment"
]
],
},
{
action: "permanentlyDelete",
@@ -148,6 +148,16 @@ const IN_PROGRESS_ACTIONS = [
action: "deleting",
label: "Deleting",
dataTypes: ["note", "notebook", "attachment", "tag", "reminder"]
},
{
action: "restoring",
label: "Restoring",
dataTypes: ["item"]
},
{
action: "permanentlyDeleting",
label: "permanently deleting",
dataTypes: ["item"]
}
];