Compare commits

...

53 Commits

Author SHA1 Message Date
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
Ammar Ahmed
0078d509c5 mobile: release v3.2.1 2025-06-18 11:25:33 +05:00
Ammar Ahmed
0c2d57716d mobile: fix app crash on android 15 & 16 2025-06-18 11:16:07 +05:00
Abdullah Atta
678aeb98c3 ci: release snap to stable track (instead of release track) 2025-06-18 10:35:56 +05:00
Abdullah Atta
5f422f4eb7 web: bump version to v3.2.1 2025-06-18 10:27:50 +05:00
Abdullah Atta
2b26524fcd web: bump version to v3.2.1-beta.0 2025-06-18 10:27:13 +05:00
Abdullah Atta
7326533cc1 ci: publish snap beta 2025-06-18 10:27:13 +05:00
Abdullah Atta
c8aae5be61 desktop: change snap base to core22 2025-06-18 10:27:13 +05:00
Abdullah Atta
be1a8bf849 docs: remove mention of importer.notesnook.com (#8218) 2025-06-17 13:59:38 +05:00
114 changed files with 630492 additions and 3265 deletions

View File

@@ -251,7 +251,7 @@ jobs:
run: echo "SNAPCRAFT_STORE_CREDENTIALS=${{ secrets.snapcraft_token }}" >> $GITHUB_ENV
- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v1
uses: samuelmeuli/action-snapcraft@v3
if: inputs.publish-snap
- name: Download build
@@ -294,7 +294,11 @@ jobs:
- name: Publish on Snapcraft
if: inputs.publish-snap
run: |
snapcraft upload --release=stable ./output/notesnook_linux_amd64.snap
if [ ${{ inputs.release-track }} == 'beta' ]; then
snapcraft upload --release=beta ./output/notesnook_linux_amd64.snap
else
snapcraft upload --release=stable ./output/notesnook_linux_amd64.snap
fi
working-directory: ./apps/desktop
build-linux-arm64:

View File

@@ -66,7 +66,7 @@ We take all queries, issues and bug reports that you might have. Feel free to as
## Additional Resources
- [Migrating & Importing your data from other apps — Importer](https://importer.notesnook.com/)
- [Migrating & Importing your data from other apps — Importer](https://help.notesnook.com/importing-notes)
- [Privacy policy](https://notesnook.com/privacy) & [Terms of service](https://notesnook.com/terms)
- [Verify Notesnook encryption claims yourself — Vericrypt](https://vericrypt.notesnook.com/)
- [Why Notesnook requires an email address?](https://blog.notesnook.com/why-notesnook-requires-an-email-address/)

View File

@@ -38,6 +38,9 @@ const linuxExecutableName = process.env.NN_PRODUCT_NAME
const year = new Date().getFullYear();
const isBeta = pkg.version.includes("-beta");
/**
* @type {import("app-builder-lib").Configuration}
*/
module.exports = {
appId: appId,
productName: productName,
@@ -194,7 +197,8 @@ module.exports = {
snap: {
autoStart: false,
confinement: "strict",
allowNativeWayland: true
allowNativeWayland: true,
base: "core22"
},
extraResources: ["app-update.yml", "./assets/**"],
extraMetadata: {

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "3.2.0",
"version": "3.2.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "3.2.0",
"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.0",
"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

@@ -97,7 +97,6 @@ export const Header = ({
style={{
flexDirection: "row",
justifyContent: "space-between",
marginTop: DefaultAppStyles.GAP_SMALL,
borderRadius: 10,
paddingVertical: 3,
borderWidth: hasSearch ? 1 : 0,

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

@@ -29,6 +29,7 @@ import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { useSettingStore } from "../../stores/use-setting-store";
import { NotesnookModule } from "../../utils/notesnook-module";
import { DefaultAppStyles } from "../../utils/styles";
import { SafeAreaView } from "react-native-safe-area-context";
const NoteItem = (props: {
id: string | number;
@@ -111,7 +112,7 @@ export const NotePreviewConfigure = () => {
);
return (
<View
<SafeAreaView
style={{
backgroundColor: colors.primary.background,
flex: 1
@@ -165,6 +166,6 @@ export const NotePreviewConfigure = () => {
ListFooterComponent={<View style={{ height: 200 }} />}
/>
</View>
</View>
</SafeAreaView>
);
};

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 3056
versionCode 3062
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -11,66 +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">
android:theme="@style/BootTheme">
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_reminders_info" />
</receiver>
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
<receiver
android:name=".NoteWidget"
android:exported="false"
android:label="@string/quick_note">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -80,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>
@@ -90,33 +96,49 @@
android:resource="@xml/note_widget_info" />
</receiver>
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
<receiver
android:name=".ReminderWidgetProvider"
android:exported="false"
android:label="@string/reminders_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_reminders_info" />
</receiver>
<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

@@ -48,7 +48,8 @@ public class NotePreviewWidget extends AppWidgetProvider {
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;

View File

@@ -31,7 +31,8 @@ public class NoteWidget extends AppWidgetProvider {
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;

View File

@@ -26,7 +26,8 @@ public class ReminderWidgetProvider extends AppWidgetProvider {
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;

View File

@@ -1,5 +1,4 @@
- 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
- 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 = 2134;
CURRENT_PROJECT_VERSION = 2141;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1165,7 +1163,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.6;
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 = 2134;
CURRENT_PROJECT_VERSION = 2141;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1270,7 +1268,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.6;
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 = 2134;
CURRENT_PROJECT_VERSION = 2141;
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.0;
MARKETING_VERSION = 3.2.6;
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 = 2134;
CURRENT_PROJECT_VERSION = 2141;
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.0;
MARKETING_VERSION = 3.2.6;
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 = 2134;
CURRENT_PROJECT_VERSION = 2141;
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.0;
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.6;
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 = 2134;
CURRENT_PROJECT_VERSION = 2141;
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.0;
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.6;
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

@@ -66,7 +66,7 @@
"react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14",
"@ammarahmed/react-native-sodium": "^1.6.3",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@react-native-community/datetimepicker": "^8.2.0",
"react-native-exit-app": "github:ammarahm-ed/react-native-exit-app",
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",

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": [
@@ -14,7 +14,6 @@
"app/"
],
"dependencies": {
"@ammarahmed/react-native-sodium": "^1.6.3",
"@notesnook/common": "file:../../packages/common",
"@notesnook/core": "file:../../packages/core",
"@notesnook/crypto": "file:../../packages/crypto",
@@ -28325,7 +28324,7 @@
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
"@ammarahmed/react-native-share-extension": "^2.9.0",
"@ammarahmed/react-native-sodium": "^1.6.3",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@bam.tech/react-native-image-resizer": "3.0.11",
"@callstack/repack": "~5.1.2",
"@formatjs/intl-locale": "4.0.0",
@@ -28479,9 +28478,9 @@
}
},
"node_modules/@ammarahmed/react-native-sodium": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.3.tgz",
"integrity": "sha512-TnJqAa/c3MmWouD4q32VR03JI4TgXqUarzBOrRk3tcX91JVsG2UnuPUHK3iRzyLI7/mBiLdMR4XBdzoy+oXmjw==",
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.4.tgz",
"integrity": "sha512-+OIBABV/8IPXVQPRxiub0WW86X/bs14GHnQ1VqtxWGv7phwfyOVzi9c1/LDeRqjCDKBAYHOv2a5PLHZb2RzeGw==",
"license": "ISC"
},
"node_modules/@ampproject/remapping": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.2.0",
"version": "3.2.6",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -54,7 +54,6 @@
"@types/validator": "^13.12.2",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.77.2",
"@ammarahmed/react-native-sodium": "^1.6.3"
"react-native": "0.77.2"
}
}
}

View File

@@ -0,0 +1,72 @@
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin b/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin
deleted file mode 100644
index f76dd23..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock b/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock
deleted file mode 100644
index 52d8866..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/gc.properties b/node_modules/react-native-pdf/android/.gradle/5.6.1/gc.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
deleted file mode 100644
index 1508dc9..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties b/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties
deleted file mode 100644
index f4a32a7..0000000
--- a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Sat Oct 12 21:44:37 CST 2019
-gradle.version=5.6.1
diff --git a/node_modules/react-native-pdf/android/.gradle/vcs-1/gc.properties b/node_modules/react-native-pdf/android/.gradle/vcs-1/gc.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/node_modules/react-native-pdf/android/build.gradle b/node_modules/react-native-pdf/android/build.gradle
index 3dfe1dc..70ef9da 100644
--- a/node_modules/react-native-pdf/android/build.gradle
+++ b/node_modules/react-native-pdf/android/build.gradle
@@ -126,6 +126,6 @@ dependencies {
// The repo from zacharee is based on PdfiumAndroidKt, a much newer fork of PdfiumAndroid, with better maintenance and updated native libraries.
implementation 'com.github.zacharee:AndroidPdfViewer:4.0.1'
// Depend on PdfiumAndroidKt directly so this can be updated independently of AndroidPdfViewer as updates are provided.
- implementation 'io.legere:pdfiumandroid:1.0.24'
+ 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.0",
"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

@@ -188,3 +188,7 @@ pre {
.docgen-content {
box-shadow: none;
}
nav details[open] {
max-height: max-content;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -4,24 +4,24 @@ title: Importing notes
# Import notes from any notes app
Notesnook importer is your one stop location to import all your notes from any notes app. We have added support for most of the popular note apps and common export formats such as markdown, html and text files.
Notesnook supports importing from most of the popular note apps and common export formats such as markdown, html and text files.
## Try it out
You can try out the importer by going to [https://importer.notesnook.com](https://importer.notesnook.com) to import your notes from any notes app.
You can try out the importer by opening the web or desktop app and going to `Settings > Notesnook Importer`.
## Supported note apps and formats
1. Plain text files
2. HTML files
3. Markdown (.md) files
4. OneNote
5. EverNote
6. Standard Notes
7. Simplenote
8. Google Keep
9. Joplin
10. Zoho Notebook
4. EverNote
5. Simplenote
6. Google Keep
7. Joplin
8. Zoho Notebook
9. Obsidian
10. Skiff Pages
**Don't see your notes app?** No worries, create an issue on [Github](https://github.com/streetwriters/notesnook/issues)

View File

@@ -23,19 +23,16 @@ The following steps will help you quickly import your notes from Evernote into N
Once you have all the .ENEX files containing your Evernote notes, its time to import them into Notesnook.
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select Evernote from the list of apps.
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select Evernote from the list of apps.
![](/static/evernote-importer/4.png)
2. Drop (or select) the `.enex` files you exported earlier from Evernote, and click the "Start processing" button.
3. Drop (or select) the `.enex` files you exported earlier from Evernote, and click the "Start processing" button.
![](/static/evernote-importer/5.png)
3. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
4. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
5. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats
Notesnook Importer is one of the most robust Evernote importers around supporting almost 100% of Evernote formats. Here's a list of everything that can (or can't be) imported into Notesnook:
Notesnook Importer is one of the most robust Evernote importers, supporting almost 100% of Evernote formats. Here's a list of everything that can (or can't be) imported into Notesnook:
- [x] Attachments
- [x] Web clips (full page, screenshot, bookmark, article, & simplified article are all supported)
@@ -44,6 +41,6 @@ Notesnook Importer is one of the most robust Evernote importers around supportin
- [x] Images
- [x] Rich text (bold, italic, lists etc.)
- [ ] Reminders
- [ ] Internal note links
- [x] Internal note links
- [x] Notebooks
- [x] Tags

View File

@@ -20,15 +20,12 @@ The following steps will help you quickly import your notes from Google Keep int
Once you have the Google Takeout containing your Google Keep notes, its time to import them into Notesnook.
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Google Keep` from list of apps.
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select `Google Keep` from list of apps.
![](/static/google-keep-importer/4.png)
2. Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup.
3. Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup.
![](/static/google-keep-importer/5.png)
3. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
4. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
5. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,13 +4,9 @@ title: HTML files
# How do I import notes from HTML files?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Text".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select `HTML` from list of apps.
![](/static/html/1.png)
3. Drop your .html files, or click anywhere inside the box to browse and select your .html files. You can also provide a .zip file containing all your .html files. Then click "Start processing".
![](/static/html/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).

View File

@@ -9,15 +9,12 @@ The following steps will help you import your notes from Joplin easily.
1. Open the Joplin Desktop app.
2. Click on `File > Export All -> JEX - Joplin Export File` and save the .JEX file at your desired location.
![](/static/joplin-importer/1.png)
3. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Joplin` from list of apps.
3. Open the Notesnook app (web or desktop)
4. Go to `Settings > Notesnook Importer` and select `Joplin` from list of apps.
![](/static/joplin-importer/2.png)
4. Drop (or select) the .jex backup file you exported earlier from Joplin:
5. Drop (or select) the .jex backup file you exported earlier from Joplin:
![](/static/joplin-importer/3.png)
5. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
6. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
7. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
6. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,16 +4,12 @@ title: Markdown files
# How do I import notes from Markdown files?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Markdown".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Markdown".
![](/static/markdown-importer/1.png)
3. Drop your .md files, or click anywhere inside the box to browse and select your .md files. You can also provide a .zip file containing all your .md files. Then click "Start processing".
![](/static/markdown-importer/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,16 +4,12 @@ title: Obsidian
# How do I import notes from Obsidian?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Obsidian".
![](/static/markdown-importer/1.png)
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Obsidian".
![](/static/obsidian-importer/1.png)
3. Drop your .md files from your Obsidian Vault, or click anywhere inside the box to browse and select your .md files. You can also provide a .zip file containing all your Obsidian .md files. Then click "Start processing".
![](/static/markdown-importer/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
<img src="/static/import-ready.png" alt="Download importer .zip file"/>
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
<img src="/static/import-zip-app.png" alt="Import .zip in Notesnook app"/>
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
![](/static/obsidian-importer/2.png)
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,13 +4,9 @@ title: Plaintext files
# How do I import notes from Plaintext files?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Text".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Text".
![](/static/plaintext-importer/1.png)
3. Drop your .txt files, or click anywhere inside the box to browse and select your .txt files. You can also provide a .zip file containing all your .txt files. Then click "Start processing".
![](/static/plaintext-importer/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).

View File

@@ -12,15 +12,12 @@ The following steps will help you import your notes from Simplenote easily.
3. Go to `Tools` tab in Settings
![](/static/simplenote-importer/2.png)
4. Click on `Export notes` to download your notes as a .zip file.
5. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Simplenote` from list of apps.
5. Open the Notesnook app (web or desktop)
6. Go to `Settings > Notesnook Importer` and select `Simplenote` from list of apps.
![](/static/simplenote-importer/3.png)
6. Drop the .zip backup file you exported earlier from Simplenote in the box or click anywhere to open system file picker to select the backup and click "Start processing".
7. Drop the .zip backup file you exported earlier from Simplenote in the box or click anywhere to open system file picker to select the backup and click "Start processing".
![](/static/simplenote-importer/4.png)
7. Once importer completes processing, download the .zip file.
![](/static/simplenote-importer/5.png)
8. After you have downloaded the `.zip` file, go to Notesnook app > Settings > Notesnook Importer and click on "Import from ZIP file" & select the .zip you downloaded earlier.
![](/static/import-zip-app.png)
9. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
8. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
## Supported formats

View File

@@ -19,16 +19,12 @@ The following steps will help you quickly import your notes from Skiff Pages int
Once you have the `Skiff.zip` file containing your Skiff pages, its time to import them into Notesnook.
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Markdown".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Skiff Pages".
![](/static/skiff-importer/3.png)
3. Drop your Skiff.zip file, or click anywhere inside the box to browse and select your Skiff.zip file. Then click "Start processing".
![](/static/skiff-importer/4.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -12,12 +12,9 @@ The following steps will help you import your notes from Standard notes easily.
3. Scroll down to `Data backups` section and choose backup type `Decrypted`.
![](/static/standard-notes-importer/2.png)
4. Go to `Backups` from the Sidebar & select "Data backup" to download backup .zip file.
5. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Standard Notes` from list of apps.
5. Open the Notesnook app (web or desktop)
6. Go to `Settings > Notesnook Importer` and select `Standard Notes` from list of apps.
![](/static/standard-notes-importer/3.png)
6. Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
7. Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
![](/static/standard-notes-importer/4.png)
7. Once importer completes processing, download the .zip file.
![](/static/standard-notes-importer/5.png)
8. After you have downloaded the `.zip` file, go to Notesnook app > Settings > Notesnook Importer and click on "Import from ZIP file" & select the .zip you downloaded earlier.
![](/static/import-zip-app.png)
9. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
8. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).

View File

@@ -14,12 +14,9 @@ The following steps will help you import your notes from Zoho notebook easily.
![](/static/zoho-importer/3.png)
4. Wait while your notes are exported. Once export completes, download exported .zip file
![](/static/zoho-importer/4.png)
5. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Zoho notebook` from the list of apps.
5. Open the Notesnook app (web or desktop)
6. Go to `Settings > Notesnook Importer` and select `Zoho notebook` from the list of apps.
![](/static/zoho-importer/5.png)
6. Drop the .zip backup file(s) you exported earlier in the box or click anywhere to open system file picker to select the backup then click start processing.
7. Drop the .zip backup file(s) you exported earlier in the box or click anywhere to open system file picker to select the backup then click start processing.
![](/static/zoho-importer/6.png)
7. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
8. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
9. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
8. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).

View File

@@ -34,9 +34,7 @@ navigation:
- path: importing-notes/import-notes-from-evernote.md
- path: importing-notes/import-notes-from-googlekeep.md
- path: importing-notes/import-notes-from-joplin.md
- path: importing-notes/import-notes-from-onenote.md
- path: importing-notes/import-notes-from-simplenote.md
- path: importing-notes/import-notes-from-standardnotes.md
- path: importing-notes/import-notes-from-zoho-notebook.md
- path: importing-notes/import-notes-from-skiff-pages.md
- path: importing-notes/import-notes-from-obsidian.md

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!

Some files were not shown because too many files have changed in this diff Show More