mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 10:09:26 +02:00
Compare commits
229 Commits
fix-append
...
3.2.3-andr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1eb215bb9 | ||
|
|
92c7e1a0c1 | ||
|
|
b81cc178b5 | ||
|
|
dd42b82dbf | ||
|
|
ca6b50081a | ||
|
|
4fa27046bb | ||
|
|
8cadc6eaa4 | ||
|
|
ccae629833 | ||
|
|
4a1cbe632c | ||
|
|
579813d93a | ||
|
|
00d00c02ea | ||
|
|
765949cc40 | ||
|
|
87cea6a120 | ||
|
|
83526d4d46 | ||
|
|
fca4faa114 | ||
|
|
171628c7b9 | ||
|
|
d456132dd6 | ||
|
|
4ac7941b6c | ||
|
|
e7f5463917 | ||
|
|
6e41ce459d | ||
|
|
f5095fa643 | ||
|
|
e6a5b85011 | ||
|
|
409c5a2517 | ||
|
|
35cd6b101d | ||
|
|
2440e28168 | ||
|
|
0078d509c5 | ||
|
|
0c2d57716d | ||
|
|
678aeb98c3 | ||
|
|
5f422f4eb7 | ||
|
|
2b26524fcd | ||
|
|
7326533cc1 | ||
|
|
c8aae5be61 | ||
|
|
be1a8bf849 | ||
|
|
2ffc2a27a5 | ||
|
|
b0a9f3320b | ||
|
|
0ef844f08c | ||
|
|
bbe00b0f9a | ||
|
|
4dcaf70346 | ||
|
|
76e5e3758a | ||
|
|
f460772d75 | ||
|
|
1cf1605564 | ||
|
|
0abbd41ff2 | ||
|
|
f16818ea93 | ||
|
|
719ab6dff3 | ||
|
|
ed50d8041e | ||
|
|
9a0de8dff8 | ||
|
|
caba714798 | ||
|
|
154cf897b3 | ||
|
|
323e53a1ef | ||
|
|
9b097cf233 | ||
|
|
75cb2fe194 | ||
|
|
7aa975457c | ||
|
|
1932d27f1c | ||
|
|
f226bdbff1 | ||
|
|
7843943130 | ||
|
|
a92afab3fc | ||
|
|
1354ee683f | ||
|
|
891e311374 | ||
|
|
881099c2c9 | ||
|
|
2dde6b6150 | ||
|
|
a1f75dfed3 | ||
|
|
493ee81c42 | ||
|
|
7109a36006 | ||
|
|
6fccac9aea | ||
|
|
fa43f97bb4 | ||
|
|
e3a973d61e | ||
|
|
542a628435 | ||
|
|
fdd25a11e7 | ||
|
|
449ae78700 | ||
|
|
6bda4a9a73 | ||
|
|
2d316ed1ec | ||
|
|
570ae00453 | ||
|
|
c845f7c1e1 | ||
|
|
659e963b39 | ||
|
|
63b8f7ef91 | ||
|
|
841e229866 | ||
|
|
4098a27df0 | ||
|
|
918824f26b | ||
|
|
83735705d2 | ||
|
|
8ac6ccb701 | ||
|
|
9ae00db676 | ||
|
|
c389a1ce4d | ||
|
|
817a58611e | ||
|
|
bfa0f09fa0 | ||
|
|
0e461c19ed | ||
|
|
7b289f8e58 | ||
|
|
071052ce37 | ||
|
|
ec6ad79586 | ||
|
|
96205fd5b7 | ||
|
|
4028a29bdb | ||
|
|
d812024c25 | ||
|
|
92f4e33ac7 | ||
|
|
ad056e0e3b | ||
|
|
dc40601fb5 | ||
|
|
65f3338df4 | ||
|
|
4f9a5cb074 | ||
|
|
eb550e95dc | ||
|
|
84dbab5bdf | ||
|
|
c2aff26dfc | ||
|
|
5dd9ce9f25 | ||
|
|
e5998f51e9 | ||
|
|
538f1f1910 | ||
|
|
9c2fa4eb30 | ||
|
|
ab04b13582 | ||
|
|
a6ef69905e | ||
|
|
565a2396f1 | ||
|
|
5a395aca3c | ||
|
|
f99ee88499 | ||
|
|
dc7e5dfa50 | ||
|
|
4f1bb994f2 | ||
|
|
6357a8f3a1 | ||
|
|
6c083bd76d | ||
|
|
c53fbb7441 | ||
|
|
6c28075070 | ||
|
|
bb027c6f25 | ||
|
|
7dc5daffe8 | ||
|
|
3e4862d768 | ||
|
|
29b0c2eeda | ||
|
|
08e5f889f8 | ||
|
|
bc198b4cd7 | ||
|
|
5c71b77b98 | ||
|
|
5660178760 | ||
|
|
8dac907ead | ||
|
|
5b7c8eaa70 | ||
|
|
7ab4a2210a | ||
|
|
c861b11c5f | ||
|
|
4f540dfdcb | ||
|
|
0e0add2f71 | ||
|
|
29edb3a3c3 | ||
|
|
ff723e5c78 | ||
|
|
ea76929c80 | ||
|
|
5d4d84bded | ||
|
|
0faf267735 | ||
|
|
197e95e66f | ||
|
|
9d4ae71bfe | ||
|
|
422869c3d2 | ||
|
|
b7d478088b | ||
|
|
1827cbeb93 | ||
|
|
66041a45f6 | ||
|
|
ccb0c858a2 | ||
|
|
659b937852 | ||
|
|
de48cea9eb | ||
|
|
404e3147b0 | ||
|
|
c0d64ff674 | ||
|
|
bbcce64549 | ||
|
|
1ca30b8a5b | ||
|
|
22b037ea1d | ||
|
|
2d74e3d4c0 | ||
|
|
1be0e4790e | ||
|
|
3770091b58 | ||
|
|
0c86f11e21 | ||
|
|
73bf283fd7 | ||
|
|
208bd13cdd | ||
|
|
42887a1dd5 | ||
|
|
0ec2f07be0 | ||
|
|
82b70d2951 | ||
|
|
8da961f300 | ||
|
|
4fba380f2d | ||
|
|
e1b046ec0c | ||
|
|
5d01159e20 | ||
|
|
aeef53df55 | ||
|
|
29433c4149 | ||
|
|
0edac2ee9d | ||
|
|
4607dc3084 | ||
|
|
41babda9b7 | ||
|
|
0fec38610c | ||
|
|
3ecc43d976 | ||
|
|
9730560fb7 | ||
|
|
6c0e51dc9d | ||
|
|
2a313a0026 | ||
|
|
5d4e60b829 | ||
|
|
c0666d8547 | ||
|
|
a5fa261e3b | ||
|
|
fa8aa4d88f | ||
|
|
b63be2de15 | ||
|
|
173caf41d0 | ||
|
|
30e4f55218 | ||
|
|
f3895f534e | ||
|
|
ea14db77de | ||
|
|
9bb48f4896 | ||
|
|
62b5cc853b | ||
|
|
90900628a2 | ||
|
|
ebf98bc652 | ||
|
|
ddd3fe6ac5 | ||
|
|
2964e7a8f0 | ||
|
|
f6d1f5890a | ||
|
|
1cc0cf19c8 | ||
|
|
efc4f3566a | ||
|
|
85b7760e1e | ||
|
|
40ee0ecf47 | ||
|
|
513c9ee8a7 | ||
|
|
2aacadc24d | ||
|
|
716bb67b96 | ||
|
|
916b6a17aa | ||
|
|
813401ea91 | ||
|
|
70e5efbcea | ||
|
|
6c8dc223f2 | ||
|
|
5b09528e7e | ||
|
|
5de5ffe068 | ||
|
|
1b511c8ddd | ||
|
|
68d1a3f50e | ||
|
|
74edd466d4 | ||
|
|
34aa21790c | ||
|
|
e7ec9e2aa3 | ||
|
|
20bbb6dd5b | ||
|
|
f937b164e9 | ||
|
|
5b4e888ddd | ||
|
|
410d983842 | ||
|
|
16cd92f03a | ||
|
|
c6ee1e6303 | ||
|
|
0f00d0e508 | ||
|
|
21fdc39db7 | ||
|
|
bb5d5a5731 | ||
|
|
bf5e68a82e | ||
|
|
cc7f479627 | ||
|
|
9eb183f6b5 | ||
|
|
cb29d71619 | ||
|
|
a6120ed90f | ||
|
|
48040c955d | ||
|
|
18b43882e1 | ||
|
|
37dc409b60 | ||
|
|
8ab05c5a98 | ||
|
|
38509c66c5 | ||
|
|
fc40a05254 | ||
|
|
8078c3ed3c | ||
|
|
57280f3ebb | ||
|
|
dc54644b4f | ||
|
|
ce31b0495d | ||
|
|
a62d4b5d4b |
@@ -69,8 +69,6 @@ jobs:
|
||||
run: |
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-reanimated/android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-reanimated//android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
|
||||
- name: CCache Stats Before Build
|
||||
run: ccache -sv
|
||||
|
||||
2
.github/workflows/android.publish.yml
vendored
2
.github/workflows/android.publish.yml
vendored
@@ -69,8 +69,6 @@ jobs:
|
||||
run: |
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-reanimated/android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-reanimated//android/build.gradle
|
||||
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
|
||||
|
||||
- name: CCache Stats Before Build
|
||||
run: ccache -sv
|
||||
|
||||
86
.github/workflows/desktop.publish.yml
vendored
86
.github/workflows/desktop.publish.yml
vendored
@@ -45,7 +45,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -107,7 +107,8 @@ jobs:
|
||||
name: Notesnook Desktop v${{ steps.app_metadata.outputs.app_version }}
|
||||
tag_name: v${{ steps.app_metadata.outputs.app_version }}
|
||||
files: ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip
|
||||
prerelease: ${{ endsWith(steps.app_metadata.outputs.app_version, '-beta') }}
|
||||
prerelease: ${{ inputs.release-track == 'beta' }}
|
||||
target_commitish: ${{ github.ref }}
|
||||
|
||||
- name: Generate flatpak sources
|
||||
if: inputs.publish-github && inputs.build-linux
|
||||
@@ -232,8 +233,8 @@ jobs:
|
||||
xcrun altool --upload-package $package -t osx --apiKey $API_KEY_ID --apiIssuer $API_KEY_ISSUER_ID --apple-id ${{ steps.app_metadata.outputs.apple_app_id }} --bundle-id ${{ steps.app_metadata.outputs.app_bundle_id }} --bundle-short-version-string ${{ steps.app_metadata.outputs.app_version }} --bundle-version ${{ steps.app_metadata.outputs.bundle_version }}
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
build-linux:
|
||||
name: Build for Linux
|
||||
build-linux-x64:
|
||||
name: Build for Linux x64
|
||||
needs: build
|
||||
if: inputs.build-linux
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -250,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
|
||||
@@ -266,7 +267,6 @@ jobs:
|
||||
|
||||
- name: Install sqlite-better-trigram for all arch
|
||||
run: |
|
||||
npm i --cpu arm64 sqlite-better-trigram
|
||||
npm i --cpu x64 sqlite-better-trigram
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
@@ -285,18 +285,86 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [ ${{ inputs.publish-github }} == true ]; then
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 -p always
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 -p always
|
||||
else
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 -p never
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 -p never
|
||||
fi
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- 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:
|
||||
name: Build for Linux arm64
|
||||
needs: build
|
||||
if: inputs.build-linux
|
||||
runs-on: ubuntu-22.04-arm
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
# - name: Setup Snapcraft Auth
|
||||
# if: inputs.publish-snap
|
||||
# run: echo "SNAPCRAFT_STORE_CREDENTIALS=${{ secrets.snapcraft_token }}" >> $GITHUB_ENV
|
||||
|
||||
# - name: Install Snapcraft
|
||||
# uses: samuelmeuli/action-snapcraft@v1
|
||||
# if: inputs.publish-snap
|
||||
|
||||
- name: Download build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build
|
||||
path: ./apps/web/build
|
||||
|
||||
- name: Install packages
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=desktop
|
||||
|
||||
- name: Install sqlite-better-trigram for all arch
|
||||
run: |
|
||||
npm i --cpu arm64 sqlite-better-trigram
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
# - name: Build snap
|
||||
# if: inputs.publish-snap
|
||||
# run: |
|
||||
# yarn electron-builder --config=electron-builder.config.js --linux snap:arm64 -p never
|
||||
# working-directory: ./apps/desktop
|
||||
|
||||
- name: Build AppImage
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [ ${{ inputs.publish-github }} == true ]; then
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:arm64 -p always
|
||||
else
|
||||
yarn electron-builder --config=electron-builder.config.js --linux AppImage:arm64 -p never
|
||||
fi
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
# - name: Publish on Snapcraft
|
||||
# if: inputs.publish-snap
|
||||
# run: |
|
||||
# snapcraft upload --release=stable ./output/notesnook_linux_arm64.snap
|
||||
# working-directory: ./apps/desktop
|
||||
|
||||
build-windows:
|
||||
name: Build for Windows
|
||||
needs: build
|
||||
|
||||
12
.github/workflows/ios.publish.yml
vendored
12
.github/workflows/ios.publish.yml
vendored
@@ -4,7 +4,7 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
@@ -14,6 +14,11 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: "16.1"
|
||||
|
||||
- name: Install node modules
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
@@ -54,7 +59,10 @@ jobs:
|
||||
key: ${{ runner.os }}-pods-${{ hashFiles('apps/mobile/native/ios/Podfile.lock') }}
|
||||
|
||||
- name: Install Pods
|
||||
run: npm run prepare:ios
|
||||
run: |
|
||||
cd apps/mobile/native/ios
|
||||
bundle install
|
||||
RCT_NEW_ARCH_ENABLED=0 bundle exec pod install
|
||||
|
||||
- name: CCache Stats Before Build
|
||||
run: ccache -sv
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,4 +11,4 @@ nx-cloud.env
|
||||
site
|
||||
node_modules.backup
|
||||
.nx
|
||||
*.patch
|
||||
/*.patch
|
||||
|
||||
@@ -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/)
|
||||
|
||||
@@ -58,10 +58,10 @@ This will compile and run the app in production mode but it won't generate any p
|
||||
npm run release -- --rebuild
|
||||
|
||||
# For macOS
|
||||
npx electron-builder --config=electron-builder.config.js --mac dmg --arm64 --x64 -publish never
|
||||
npx electron-builder --config=electron-builder.config.js --mac dmg --arm64 --x64 --publish never
|
||||
|
||||
# For Linux (AppImage)
|
||||
npx electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 -publish never
|
||||
npx electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 --publish never
|
||||
|
||||
# For Windows
|
||||
npx electron-builder --config=electron-builder.config.js --win --publish never
|
||||
|
||||
@@ -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,
|
||||
@@ -57,6 +60,7 @@ module.exports = {
|
||||
"node_modules/bindings",
|
||||
"node_modules/node-gyp-build",
|
||||
"node_modules/sqlite-better-trigram",
|
||||
"node_modules/sqlite3-fts5-html",
|
||||
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
|
||||
{
|
||||
from: "node_modules/sqlite-better-trigram-linux-${arch}",
|
||||
@@ -70,6 +74,20 @@ module.exports = {
|
||||
from: "node_modules/sqlite-better-trigram-windows-${arch}",
|
||||
to: "node_modules/sqlite-better-trigram-windows-${arch}"
|
||||
},
|
||||
|
||||
{
|
||||
from: "node_modules/sqlite3-fts5-html-linux-${arch}",
|
||||
to: "node_modules/sqlite3-fts5-html-linux-${arch}"
|
||||
},
|
||||
{
|
||||
from: "node_modules/sqlite3-fts5-html-darwin-${arch}",
|
||||
to: "node_modules/sqlite3-fts5-html-darwin-${arch}"
|
||||
},
|
||||
{
|
||||
from: "node_modules/sqlite3-fts5-html-windows-${arch}",
|
||||
to: "node_modules/sqlite3-fts5-html-windows-${arch}"
|
||||
},
|
||||
|
||||
"node_modules/sodium-native/index.js",
|
||||
"node_modules/sodium-native/package.json"
|
||||
],
|
||||
@@ -160,29 +178,27 @@ module.exports = {
|
||||
description: "Your private note taking space",
|
||||
executableName: linuxExecutableName,
|
||||
desktop: {
|
||||
actions: [
|
||||
{
|
||||
id: "new-note",
|
||||
name: "New note",
|
||||
args: "new note"
|
||||
desktopActions: {
|
||||
"new-note": {
|
||||
Name: "New note",
|
||||
Exec: `${linuxExecutableName} new note`
|
||||
},
|
||||
{
|
||||
id: "new-notebook",
|
||||
name: "New notebook",
|
||||
args: "new notebook"
|
||||
"new-notebook": {
|
||||
Name: "New notebook",
|
||||
Exec: `${linuxExecutableName} new notebook`
|
||||
},
|
||||
{
|
||||
id: "new-reminder",
|
||||
name: "New reminder",
|
||||
args: "new reminder"
|
||||
"new-reminder": {
|
||||
Name: "New reminder",
|
||||
Exec: `${linuxExecutableName} new reminder`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
snap: {
|
||||
autoStart: false,
|
||||
confinement: "strict",
|
||||
allowNativeWayland: true
|
||||
allowNativeWayland: true,
|
||||
base: "core22"
|
||||
},
|
||||
extraResources: ["app-update.yml", "./assets/**"],
|
||||
extraMetadata: {
|
||||
|
||||
1570
apps/desktop/package-lock.json
generated
1570
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.3",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
@@ -27,37 +27,42 @@
|
||||
"dependencies": {
|
||||
"@lingui/core": "5.1.2",
|
||||
"@notesnook/intl": "file:../../packages/intl",
|
||||
"@notesnook/ui": "file:../../packages/ui",
|
||||
"@trpc/client": "10.45.2",
|
||||
"@trpc/server": "10.45.2",
|
||||
"better-sqlite3-multiple-ciphers": "11.5.0",
|
||||
"better-sqlite3-multiple-ciphers": "^11.10.0",
|
||||
"electron-trpc": "0.7.1",
|
||||
"electron-updater": "^6.3.4",
|
||||
"icojs": "^0.19.4",
|
||||
"sqlite-better-trigram": "0.0.2",
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.3",
|
||||
"sqlite3-fts5-html": "^0.0.4",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "3.23.8"
|
||||
"zod": "3.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@streetwriters/kysely": "^0.27.4",
|
||||
"@types/node": "22.7.0",
|
||||
"@types/node": "22.15.3",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"chokidar": "^4.0.1",
|
||||
"electron": "^31.7.4",
|
||||
"electron-builder": "^25.1.8",
|
||||
"chokidar": "^4.0.3",
|
||||
"electron": "^34.0.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"esbuild": "0.21.5",
|
||||
"node-abi": "^3.68.0",
|
||||
"node-gyp-build": "^4.8.2",
|
||||
"playwright": "^1.48.2",
|
||||
"node-abi": "^4.5.0",
|
||||
"node-gyp-build": "^4.8.4",
|
||||
"playwright": "1.48.2",
|
||||
"prebuildify": "^6.0.1",
|
||||
"slugify": "1.6.6",
|
||||
"tree-kill": "^1.2.2",
|
||||
"undici": "^6.19.8",
|
||||
"vitest": "^2.1.8"
|
||||
"undici": "^7.8.0",
|
||||
"vitest": "2.1.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dmg-license": "^1.0.11"
|
||||
},
|
||||
"overrides": {
|
||||
"@electron/node-gyp": "^10.2.0-electron.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node scripts/dev.mjs",
|
||||
"staging": "node scripts/build.mjs --run",
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
diff --git a/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js b/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
|
||||
index 739a48c..7a7b4af 100644
|
||||
--- a/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
|
||||
+++ b/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
|
||||
@@ -117,6 +117,7 @@ class LinuxTargetHelper {
|
||||
StartupWMClass: appInfo.productName,
|
||||
...extra,
|
||||
...targetSpecificOptions.desktop,
|
||||
+ actions: undefined
|
||||
};
|
||||
const description = this.getDescription(targetSpecificOptions);
|
||||
if (!(0, builder_util_1.isEmptyOrSpaces)(description)) {
|
||||
@@ -160,6 +161,23 @@ class LinuxTargetHelper {
|
||||
data += `\n${name}=${desktopMeta[name]}`;
|
||||
}
|
||||
data += "\n";
|
||||
+
|
||||
+ if (targetSpecificOptions.desktop.actions) {
|
||||
+ let actionsData = "";
|
||||
+ const validActions = [];
|
||||
+ for (const action of targetSpecificOptions.desktop.actions) {
|
||||
+ if (!action.id || !action.name || !action.args) continue;
|
||||
+ actionsData += "\n";
|
||||
+ actionsData += `[Desktop Action ${action.id}]
|
||||
+Name=${action.name}
|
||||
+Exec=${desktopMeta.Exec} ${action.args}`;
|
||||
+ actionsData += "\n";
|
||||
+
|
||||
+ validActions.push(action.id);
|
||||
+ }
|
||||
+
|
||||
+ data += `Actions=${validActions.join(";")};\n${actionsData}`
|
||||
+ }
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
}
|
||||
13
apps/desktop/patches/app-builder-lib+26.0.12.patch
Normal file
13
apps/desktop/patches/app-builder-lib+26.0.12.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/node_modules/app-builder-lib/out/targets/snap.js b/node_modules/app-builder-lib/out/targets/snap.js
|
||||
index 0b5405e..753f6c6 100644
|
||||
--- a/node_modules/app-builder-lib/out/targets/snap.js
|
||||
+++ b/node_modules/app-builder-lib/out/targets/snap.js
|
||||
@@ -115,7 +115,7 @@ class SnapTarget extends core_1.Target {
|
||||
else {
|
||||
const archTriplet = archNameToTriplet(arch);
|
||||
appDescriptor.environment = {
|
||||
- DISABLE_WAYLAND: options.allowNativeWayland ? "" : "1",
|
||||
+ DISABLE_WAYLAND: options.allowNativeWayland ? "0" : "1",
|
||||
PATH: "$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH",
|
||||
SNAP_DESKTOP_RUNTIME: "$SNAP/gnome-platform",
|
||||
LD_LIBRARY_PATH: [
|
||||
@@ -0,0 +1,11 @@
|
||||
diff --git a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
|
||||
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 @@
|
||||
'SQLITE_TRACE_SIZE_LIMIT=32',
|
||||
'SQLITE_USER_AUTHENTICATION=0',
|
||||
'SQLITE_USE_URI=0',
|
||||
+ 'SQLITE_ENABLE_REGEXP',
|
||||
],
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
diff --git a/node_modules/electron-trpc/dist/main.mjs b/node_modules/electron-trpc/dist/main.mjs
|
||||
index 07971db..b67a3cf 100644
|
||||
--- a/node_modules/electron-trpc/dist/main.mjs
|
||||
+++ b/node_modules/electron-trpc/dist/main.mjs
|
||||
@@ -537,8 +537,15 @@ class be {
|
||||
attachWindow(r) {
|
||||
F(this, w).includes(r) || (y("Attaching window", r.id), F(this, w).push(r), v(this, N, Z).call(this, r));
|
||||
}
|
||||
- detachWindow(r) {
|
||||
- y("Detaching window", r.id), L(this, w, F(this, w).filter((e) => e !== r)), v(this, R, M).call(this, { webContentsId: r.webContents.id });
|
||||
+ detachWindow(r, webContentsId) {
|
||||
+ y("Detaching window", r.id);
|
||||
+
|
||||
+ if (r.isDestroyed() && webContentsId === undefined) {
|
||||
+ throw new Error('webContentsId is required when calling detachWindow on a destroyed window');
|
||||
+ }
|
||||
+
|
||||
+ L(this, w, F(this, w).filter((e) => e !== r));
|
||||
+ v(this, R, M).call(this, { webContentsId: webContentsId ?? r.webContents.id });
|
||||
}
|
||||
}
|
||||
w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
|
||||
@@ -548,6 +555,7 @@ w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
|
||||
for (const [i, a] of F(this, E).entries())
|
||||
i.startsWith(`${r}-${e ?? ""}`) && (y("Closing subscription", i), a.unsubscribe(), F(this, E).delete(i));
|
||||
}, N = new WeakSet(), Z = function(r) {
|
||||
+ const webContentsId = r.webContents.id;
|
||||
r.webContents.on("did-start-navigation", ({ frame: e }) => {
|
||||
y(
|
||||
"Handling webContents `did-start-navigation` event",
|
||||
@@ -558,7 +566,7 @@ w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
|
||||
frameRoutingId: e.routingId
|
||||
});
|
||||
}), r.webContents.on("destroyed", () => {
|
||||
- y("Handling webContents `destroyed` event"), this.detachWindow(r);
|
||||
+ y("Handling webContents `destroyed` event"), this.detachWindow(r, webContentsId);
|
||||
});
|
||||
};
|
||||
const me = ({
|
||||
@@ -43,8 +43,7 @@ if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
|
||||
);
|
||||
}
|
||||
|
||||
// temporary until there's support for prebuilt binaries for linux ARM
|
||||
if (os.platform() === "linux") await patchBetterSQLite3();
|
||||
await patchBetterSQLite3();
|
||||
|
||||
await fs.cp(path.join(webAppPath, "build"), path.join(root, "build"), {
|
||||
recursive: true,
|
||||
|
||||
@@ -17,7 +17,7 @@ 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 { readFile, writeFile } from "fs/promises";
|
||||
import { readFile, rm, writeFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
@@ -25,6 +25,8 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export async function patchBetterSQLite3() {
|
||||
console.log("Patching better-sqlite3");
|
||||
|
||||
const jsonPath = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
@@ -34,15 +36,23 @@ export async function patchBetterSQLite3() {
|
||||
);
|
||||
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
|
||||
|
||||
json.version = "11.5.1";
|
||||
json.homepage = "https://github.com/thecodrr/better-sqlite3-multiple-ciphers";
|
||||
json.repository.url =
|
||||
"git://github.com/thecodrr/better-sqlite3-multiple-ciphers.git";
|
||||
delete json.homepage;
|
||||
delete json.repository;
|
||||
|
||||
await writeFile(jsonPath, JSON.stringify(json));
|
||||
|
||||
await rm(
|
||||
path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"node_modules",
|
||||
"better-sqlite3-multiple-ciphers",
|
||||
"build"
|
||||
),
|
||||
{ force: true, recursive: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (process.argv[1] === __filename) {
|
||||
console.log("Patching better-sqlite3");
|
||||
patchBetterSQLite3();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import { app, dialog, nativeTheme, Notification, shell } from "electron";
|
||||
import {
|
||||
app,
|
||||
dialog,
|
||||
Menu,
|
||||
MenuItem,
|
||||
nativeImage,
|
||||
nativeTheme,
|
||||
Notification,
|
||||
shell
|
||||
} from "electron";
|
||||
import { AutoLaunch } from "../utils/autolaunch";
|
||||
import { config, DesktopIntegration } from "../utils/config";
|
||||
import { bringToFront } from "../utils/bring-to-front";
|
||||
@@ -33,6 +42,7 @@ import { isFlatpak, isSnap } from "../utils";
|
||||
import { setupDesktopIntegration } from "../utils/desktop-integration";
|
||||
import { rm } from "fs/promises";
|
||||
import { disableCustomDns, enableCustomDns } from "../utils/custom-dns";
|
||||
import type { MenuItem as NNMenuItem } from "@notesnook/ui";
|
||||
|
||||
const t = initTRPC.create();
|
||||
|
||||
@@ -225,5 +235,63 @@ export const osIntegrationRouter = t.router({
|
||||
nativeTheme.off("updated", updated);
|
||||
};
|
||||
})
|
||||
)
|
||||
),
|
||||
|
||||
showMenu: t.procedure
|
||||
.input(
|
||||
z.object({
|
||||
menuItems: z.array(z.any())
|
||||
})
|
||||
)
|
||||
.subscription(({ input: { menuItems } }) =>
|
||||
observable<string[]>((emit) => {
|
||||
const items = menuItems as NNMenuItem[];
|
||||
const menu = new Menu();
|
||||
for (const item of items) {
|
||||
const menuItem = toMenuItem(item, (id) => emit.next(id));
|
||||
if (menuItem) menu.append(menuItem);
|
||||
}
|
||||
if (menu.items.length > 0) menu.popup();
|
||||
return () => {
|
||||
menu.removeAllListeners();
|
||||
menu.closePopup();
|
||||
};
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
function toMenuItem(
|
||||
item: NNMenuItem,
|
||||
onClick: (id: string[]) => void,
|
||||
parentKey?: string
|
||||
): MenuItem | undefined {
|
||||
switch (item.type) {
|
||||
case "lazy-loader":
|
||||
return undefined;
|
||||
case "separator":
|
||||
return new MenuItem({ type: "separator" });
|
||||
case "button": {
|
||||
const submenu = item.menu ? new Menu() : undefined;
|
||||
if (submenu && item.menu) {
|
||||
for (const subitem of item.menu.items) {
|
||||
const subMenuItem = toMenuItem(subitem, onClick, item.key);
|
||||
if (subMenuItem) submenu.append(subMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
return new MenuItem({
|
||||
label: item.title,
|
||||
enabled: !item.isDisabled,
|
||||
visible: !item.isHidden,
|
||||
toolTip: item.tooltip,
|
||||
sublabel: item.tooltip,
|
||||
checked: item.isChecked,
|
||||
type: submenu ? "submenu" : item.isChecked ? "checkbox" : "normal",
|
||||
id: item.key,
|
||||
submenu,
|
||||
click: () => onClick(parentKey ? [parentKey, item.key] : [item.key]),
|
||||
accelerator: item.modifier?.replace("Mod", "CommandOrControl")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +48,6 @@ export class SQLite {
|
||||
this.sqlite = require("better-sqlite3-multiple-ciphers")(
|
||||
filePath
|
||||
).unsafeMode(true);
|
||||
const betterTrigram = require("sqlite-better-trigram");
|
||||
betterTrigram.load(this.sqlite);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,16 +143,44 @@ function setupMenu() {
|
||||
})
|
||||
);
|
||||
|
||||
if (params.isEditable)
|
||||
if (params.isEditable) {
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: strings.paste(),
|
||||
role: "pasteAndMatchStyle",
|
||||
role: "paste",
|
||||
enabled: clipboard.readText("clipboard").length > 0,
|
||||
accelerator: "CommandOrControl+V"
|
||||
})
|
||||
);
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label:
|
||||
process.platform === "darwin"
|
||||
? strings.pasteAndMatchStyle()
|
||||
: strings.pasteWithoutFormatting(),
|
||||
role: "pasteAndMatchStyle",
|
||||
enabled: clipboard.readText("clipboard").length > 0,
|
||||
accelerator:
|
||||
process.platform === "darwin"
|
||||
? "Option+Shift+Command+V"
|
||||
: "Shift+CommandOrControl+V"
|
||||
})
|
||||
);
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
type: "separator"
|
||||
})
|
||||
);
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: strings.spellCheck(),
|
||||
role: "toggleSpellChecker"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (menu.items.length > 0) menu.popup();
|
||||
});
|
||||
}
|
||||
|
||||
1
apps/mobile/.gitignore
vendored
1
apps/mobile/.gitignore
vendored
@@ -69,6 +69,7 @@ buck-out/
|
||||
*/fastlane/report.xml
|
||||
*/fastlane/Preview.html
|
||||
*/fastlane/screenshots
|
||||
native/vendor
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
||||
|
||||
@@ -167,6 +167,7 @@ const Actions = ({
|
||||
setAttachments();
|
||||
eSendEvent(eDBItemUpdate, attachment.id);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
positiveText: strings.rename()
|
||||
});
|
||||
|
||||
@@ -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 />
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -26,7 +26,7 @@ import { notesnook } from "../../../e2e/test.ids";
|
||||
import { getColorLinearShade } from "../../utils/colors";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Button } from "../ui/button";
|
||||
import { Button, ButtonProps } from "../ui/button";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
const DialogButtons = ({
|
||||
@@ -37,6 +37,14 @@ const DialogButtons = ({
|
||||
loading,
|
||||
doneText,
|
||||
positiveType
|
||||
}: {
|
||||
onPressPositive?: () => void;
|
||||
onPressNegative: () => void;
|
||||
positiveTitle: string;
|
||||
negativeTitle?: string;
|
||||
loading?: boolean;
|
||||
doneText?: string;
|
||||
positiveType?: ButtonProps["type"];
|
||||
}) => {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -17,15 +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 { KeyboardTypeOptions } from "react-native";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events";
|
||||
import { ButtonProps } from "../ui/button";
|
||||
|
||||
type DialogInfo = {
|
||||
export type DialogInfo = {
|
||||
title?: string;
|
||||
paragraph?: string;
|
||||
positiveText?: string;
|
||||
negativeText?: string;
|
||||
positivePress?: (...args: any[]) => void;
|
||||
positiveText: string;
|
||||
negativeText: string;
|
||||
background?: string;
|
||||
transparent?: boolean;
|
||||
statusBarTranslucent?: boolean;
|
||||
positivePress?: (...args: any[]) => Promise<any>;
|
||||
onClose?: () => void;
|
||||
positiveType?:
|
||||
| "transparent"
|
||||
@@ -44,16 +49,18 @@ type DialogInfo = {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
context: "global" | "local" | (string & {});
|
||||
secureTextEntry?: boolean;
|
||||
keyboardType?: string;
|
||||
keyboardType?: KeyboardTypeOptions;
|
||||
check?: {
|
||||
info: string;
|
||||
type?: string;
|
||||
type?: ButtonProps["type"];
|
||||
defaultValue?: boolean;
|
||||
};
|
||||
notice: {
|
||||
text: string;
|
||||
type: "alert" | "information";
|
||||
};
|
||||
disableBackdropClosing: boolean;
|
||||
component: JSX.Element | ((close?: () => void) => JSX.Element);
|
||||
};
|
||||
|
||||
export function presentDialog(data: Partial<DialogInfo>): void {
|
||||
|
||||
@@ -20,7 +20,7 @@ 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 { View } from "react-native";
|
||||
import { TextInput, View, ViewStyle } from "react-native";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
@@ -40,42 +40,26 @@ import Seperator from "../ui/seperator";
|
||||
import BaseDialog from "./base-dialog";
|
||||
import DialogButtons from "./dialog-buttons";
|
||||
import DialogHeader from "./dialog-header";
|
||||
import { DialogInfo } from "./functions";
|
||||
|
||||
export const Dialog = ({ context = "global" }) => {
|
||||
export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const values = useRef({
|
||||
const [visible, setVisible] = useState<boolean>();
|
||||
const [checked, setChecked] = useState<boolean>();
|
||||
const [loading, setLoading] = useState<boolean>();
|
||||
const values = useRef<{
|
||||
inputValue?: string;
|
||||
}>({
|
||||
inputValue: undefined
|
||||
});
|
||||
const inputRef = useRef();
|
||||
const [dialogInfo, setDialogInfo] = useState({
|
||||
title: "",
|
||||
paragraph: "",
|
||||
positiveText: strings.done(),
|
||||
negativeText: strings.cancel(),
|
||||
positivePress: () => {},
|
||||
onClose: () => {},
|
||||
positiveType: "transparent",
|
||||
icon: null,
|
||||
paragraphColor: colors.primary.paragraph,
|
||||
input: false,
|
||||
inputPlaceholder: "Enter some text",
|
||||
defaultValue: "",
|
||||
disableBackdropClosing: false,
|
||||
check: {
|
||||
info: "Check",
|
||||
type: "transparent",
|
||||
defaultValue: false
|
||||
}
|
||||
});
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const [dialogInfo, setDialogInfo] = useState<DialogInfo>();
|
||||
|
||||
const onPressPositive = async () => {
|
||||
if (dialogInfo.positivePress) {
|
||||
if (dialogInfo?.positivePress) {
|
||||
inputRef.current?.blur();
|
||||
setLoading(true);
|
||||
let result;
|
||||
let result = false;
|
||||
try {
|
||||
result = await dialogInfo.positivePress(
|
||||
values.current.inputValue || dialogInfo.defaultValue,
|
||||
@@ -97,7 +81,7 @@ export const Dialog = ({ context = "global" }) => {
|
||||
};
|
||||
|
||||
const show = useCallback(
|
||||
(data) => {
|
||||
(data: DialogInfo) => {
|
||||
if (!data.context) data.context = "global";
|
||||
if (data.context !== context) return;
|
||||
setDialogInfo(data);
|
||||
@@ -108,6 +92,14 @@ export const Dialog = ({ context = "global" }) => {
|
||||
[context]
|
||||
);
|
||||
|
||||
const hide = React.useCallback(() => {
|
||||
setChecked(false);
|
||||
values.current.inputValue = undefined;
|
||||
setVisible(false);
|
||||
setDialogInfo(undefined);
|
||||
dialogInfo?.onClose?.();
|
||||
}, [dialogInfo]);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent(eOpenSimpleDialog, show);
|
||||
eSubscribeEvent(eCloseSimpleDialog, hide);
|
||||
@@ -118,21 +110,14 @@ export const Dialog = ({ context = "global" }) => {
|
||||
};
|
||||
}, [hide, show]);
|
||||
|
||||
const hide = React.useCallback(() => {
|
||||
setChecked(false);
|
||||
values.current.inputValue = undefined;
|
||||
setVisible(false);
|
||||
dialogInfo.onClose?.();
|
||||
}, [dialogInfo]);
|
||||
|
||||
const onNegativePress = async () => {
|
||||
if (dialogInfo.onClose) {
|
||||
if (dialogInfo?.onClose) {
|
||||
await dialogInfo.onClose();
|
||||
}
|
||||
hide();
|
||||
};
|
||||
|
||||
const style = {
|
||||
const style: ViewStyle = {
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 400 : "85%",
|
||||
maxHeight: 450,
|
||||
@@ -143,11 +128,19 @@ export const Dialog = ({ context = "global" }) => {
|
||||
overflow: "hidden"
|
||||
};
|
||||
|
||||
return visible ? (
|
||||
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({
|
||||
@@ -160,89 +153,99 @@ export const Dialog = ({ context = "global" }) => {
|
||||
visible={true}
|
||||
onRequestClose={hide}
|
||||
>
|
||||
<View style={style}>
|
||||
<DialogHeader
|
||||
title={dialogInfo.title}
|
||||
icon={dialogInfo.icon}
|
||||
paragraph={dialogInfo.paragraph}
|
||||
paragraphColor={dialogInfo.paragraphColor}
|
||||
padding={12}
|
||||
style={{
|
||||
minHeight: 0
|
||||
}}
|
||||
/>
|
||||
<Seperator half />
|
||||
{typeof dialogInfo.component === "function"
|
||||
? dialogInfo.component(() => hide())
|
||||
: dialogInfo.component}
|
||||
|
||||
{dialogInfo.input ? (
|
||||
<View
|
||||
{dialogInfo.component ? null : (
|
||||
<View style={style}>
|
||||
<DialogHeader
|
||||
title={dialogInfo.title}
|
||||
icon={dialogInfo.icon}
|
||||
paragraph={dialogInfo.paragraph}
|
||||
paragraphColor={dialogInfo.paragraphColor}
|
||||
padding={12}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
autoCapitalize="none"
|
||||
onChangeText={(value) => {
|
||||
values.current.inputValue = value;
|
||||
}}
|
||||
testID="input-value"
|
||||
secureTextEntry={dialogInfo.secureTextEntry}
|
||||
//defaultValue={dialogInfo.defaultValue}
|
||||
onSubmit={onPressPositive}
|
||||
returnKeyLabel="Done"
|
||||
returnKeyType="done"
|
||||
keyboardType={dialogInfo.keyboardType || "default"}
|
||||
placeholder={dialogInfo.inputPlaceholder}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
/>
|
||||
<Seperator half />
|
||||
|
||||
{dialogInfo?.notice ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Notice
|
||||
type={dialogInfo.notice.type || "information"}
|
||||
text={dialogInfo.notice.text}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{dialogInfo.check ? (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setChecked(!checked);
|
||||
}}
|
||||
icon={
|
||||
checked
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
iconColor={checked ? colors.secondary.icon : colors.primary.icon}
|
||||
{dialogInfo.input ? (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "flex-start"
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
height={35}
|
||||
iconSize={20}
|
||||
width="100%"
|
||||
title={dialogInfo.check.info}
|
||||
type={checked ? dialogInfo.check.type || "plain" : "plain"}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
>
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
autoCapitalize="none"
|
||||
onChangeText={(value) => {
|
||||
values.current.inputValue = value;
|
||||
}}
|
||||
testID="input-value"
|
||||
secureTextEntry={dialogInfo.secureTextEntry}
|
||||
//defaultValue={dialogInfo.defaultValue}
|
||||
onSubmit={() => {
|
||||
onPressPositive();
|
||||
}}
|
||||
returnKeyLabel="Done"
|
||||
returnKeyType="done"
|
||||
keyboardType={dialogInfo.keyboardType || "default"}
|
||||
placeholder={dialogInfo.inputPlaceholder}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<DialogButtons
|
||||
onPressNegative={onNegativePress}
|
||||
onPressPositive={dialogInfo.positivePress && onPressPositive}
|
||||
loading={loading}
|
||||
positiveTitle={dialogInfo.positiveText}
|
||||
negativeTitle={dialogInfo.negativeText}
|
||||
positiveType={dialogInfo.positiveType}
|
||||
/>
|
||||
</View>
|
||||
{dialogInfo?.notice ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Notice
|
||||
type={dialogInfo.notice.type || "information"}
|
||||
text={dialogInfo.notice.text}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{dialogInfo.check ? (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setChecked(!checked);
|
||||
}}
|
||||
icon={
|
||||
checked
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
iconColor={
|
||||
checked ? colors.secondary.icon : colors.primary.icon
|
||||
}
|
||||
style={{
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
height={35}
|
||||
iconSize={20}
|
||||
width="100%"
|
||||
title={dialogInfo.check.info}
|
||||
type={checked ? dialogInfo.check.type || "plain" : "plain"}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<DialogButtons
|
||||
onPressNegative={onNegativePress}
|
||||
onPressPositive={dialogInfo.positivePress && onPressPositive}
|
||||
loading={loading}
|
||||
positiveTitle={dialogInfo.positiveText}
|
||||
negativeTitle={dialogInfo.negativeText}
|
||||
positiveType={dialogInfo.positiveType}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<Toast context="local" />
|
||||
</BaseDialog>
|
||||
) : null;
|
||||
@@ -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,
|
||||
|
||||
@@ -133,7 +133,9 @@ export const SectionHeader = React.memo<
|
||||
<Sort
|
||||
screen={screen}
|
||||
type={dataType}
|
||||
hideGroupOptions={screen === "Reminders"}
|
||||
hideGroupOptions={
|
||||
screen === "Reminders" || screen === "Search"
|
||||
}
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
181
apps/mobile/app/components/list-items/search-result/index.tsx
Normal file
181
apps/mobile/app/components/list-items/search-result/index.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
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 React from "react";
|
||||
import { HighlightedResult } from "@notesnook/core";
|
||||
import { View } from "react-native";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { Properties } from "../../properties";
|
||||
import { db } from "../../../common/database";
|
||||
import { eSendEvent } from "../../../services/event-manager";
|
||||
import { eOnLoadNote } from "../../../utils/events";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { fluidTabsRef } from "../../../utils/global-refs";
|
||||
type SearchResultProps = {
|
||||
item: HighlightedResult;
|
||||
};
|
||||
|
||||
export const SearchResult = (props: SearchResultProps) => {
|
||||
const [expanded, setExpanded] = React.useState(true);
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
const openNote = async (index?: number) => {
|
||||
const note = await db.notes.note(props.item.id);
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: {
|
||||
...note,
|
||||
content: props.item.rawContent
|
||||
? {
|
||||
data: props.item.rawContent || "",
|
||||
type: "tiptap"
|
||||
}
|
||||
: undefined
|
||||
},
|
||||
searchResultIndex: index
|
||||
});
|
||||
fluidTabsRef.current?.goToPage("editor");
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
borderRadius: 0
|
||||
}}
|
||||
onLongPress={async () => {
|
||||
const note = await db.notes.note(props.item.id);
|
||||
Properties.present(note);
|
||||
}}
|
||||
onPress={async () => openNote()}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL / 2,
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
{props.item.content?.length ? (
|
||||
<IconButton
|
||||
name={!expanded ? "chevron-right" : "chevron-down"}
|
||||
onPress={() => setExpanded((prev) => !prev)}
|
||||
size={AppFontSize.md + 2}
|
||||
color={colors.secondary.icon}
|
||||
style={{
|
||||
width: 23,
|
||||
height: 23
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Heading
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
{props.item.title.map((title, index) => (
|
||||
<React.Fragment key={props.item.id + index + "-srt"}>
|
||||
{title.prefix}
|
||||
<Heading
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
backgroundColor: colors.secondary.accent,
|
||||
color: colors.secondary.accentForeground
|
||||
}}
|
||||
>
|
||||
{title.match}
|
||||
</Heading>
|
||||
{title.suffix}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Heading>
|
||||
</View>
|
||||
{props.item.content?.length ? (
|
||||
<Paragraph size={AppFontSize.xxs} color={colors.secondary.paragraph}>
|
||||
{props.item.content.length}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{expanded &&
|
||||
props.item.content.map((content, index) => (
|
||||
<Pressable
|
||||
key={props.item.id + index}
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
borderColor: colors.primary.border,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
width: "100%"
|
||||
}}
|
||||
onLongPress={async () => {
|
||||
const note = await db.notes.note(props.item.id);
|
||||
Properties.present(note);
|
||||
}}
|
||||
onPress={() => {
|
||||
let activeIndex = 0;
|
||||
for (let i = 0; i <= index; i++) {
|
||||
activeIndex += props.item.content[i].length;
|
||||
}
|
||||
console.log(activeIndex);
|
||||
openNote(activeIndex);
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
{content.map((match, index) => (
|
||||
<React.Fragment key={props.item.id + index + "src"}>
|
||||
{match.prefix}
|
||||
<Paragraph
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
backgroundColor: colors.secondary.accent,
|
||||
color: colors.secondary.accentForeground
|
||||
}}
|
||||
>
|
||||
{match.match}
|
||||
</Paragraph>
|
||||
{match.suffix}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
))}
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
@@ -73,7 +73,7 @@ export default function List(props: ListProps) {
|
||||
? "home"
|
||||
: props.renderedInRoute === "Favorites"
|
||||
? "favorites"
|
||||
: props.renderedInRoute === "Trash"
|
||||
: props.renderedInRoute === "Trash" || props.dataType === "trash"
|
||||
? "trash"
|
||||
: `${props.dataType}s`;
|
||||
|
||||
@@ -150,7 +150,7 @@ export default function List(props: ListProps) {
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
{props.data?.placeholders?.length === 0 ? (
|
||||
{props.data?.placeholders?.length === 0 || !props.data ? (
|
||||
<>
|
||||
{props.CustomLisHeader ? (
|
||||
props.CustomLisHeader
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
GroupHeader,
|
||||
GroupOptions,
|
||||
GroupingKey,
|
||||
HighlightedResult,
|
||||
Item,
|
||||
ItemType,
|
||||
Note,
|
||||
@@ -49,6 +50,7 @@ import { NoteWrapper } from "../list-items/note/wrapper";
|
||||
import { NotebookWrapper } from "../list-items/notebook/wrapper";
|
||||
import ReminderItem from "../list-items/reminder";
|
||||
import TagItem from "../list-items/tag";
|
||||
import { SearchResult } from "../list-items/search-result";
|
||||
|
||||
type ListItemWrapperProps<TItem = Item> = {
|
||||
group?: GroupingKey;
|
||||
@@ -153,7 +155,9 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
},
|
||||
items?.cacheItem(index) ? 100 : 0
|
||||
);
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
/** empty */
|
||||
}
|
||||
})();
|
||||
}, [index, items, refreshItem]);
|
||||
|
||||
@@ -196,7 +200,7 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
notebooks={notebooks.current}
|
||||
reminder={reminder.current}
|
||||
attachmentsCount={attachmentsCount.current}
|
||||
date={getDate(item, group)}
|
||||
date={getDate(item as Note, group)}
|
||||
isRenderedInActionSheet={isSheet}
|
||||
index={index}
|
||||
locked={locked.current}
|
||||
@@ -227,7 +231,7 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
<NotebookWrapper
|
||||
item={item as Notebook}
|
||||
totalNotes={totalNotes.current}
|
||||
date={getDate(item, group)}
|
||||
date={getDate(item as Notebook, group)}
|
||||
index={index}
|
||||
/>
|
||||
</>
|
||||
@@ -285,12 +289,34 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case "searchResult":
|
||||
return (
|
||||
<>
|
||||
{groupHeader && previousIndex.current === index && !isSheet ? (
|
||||
<SectionHeader
|
||||
screen={props.renderedInRoute}
|
||||
item={groupHeader}
|
||||
index={index}
|
||||
dataType={item.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<SearchResult item={item as HighlightedResult} />
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getDate(item: Item, groupType?: GroupingKey): number {
|
||||
function getDate(item: Notebook | Note, groupType?: GroupingKey): number {
|
||||
return (
|
||||
getSortValue(
|
||||
groupType
|
||||
|
||||
@@ -58,7 +58,6 @@ const Line = ({ top = 6, bottom = 6 }) => {
|
||||
|
||||
export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const isColor = !!ColorValues[item.title];
|
||||
if (!item || !item.id) {
|
||||
return (
|
||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||
|
||||
@@ -36,6 +36,7 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
const TOP_BAR_ITEMS: ActionId[] = [
|
||||
"pin",
|
||||
"favorite",
|
||||
"archive",
|
||||
"lock-unlock",
|
||||
"publish",
|
||||
"local-only",
|
||||
@@ -66,6 +67,7 @@ const COLUMN_BAR_ITEMS: ActionId[] = [
|
||||
"move-notebook",
|
||||
"pin",
|
||||
"default-notebook",
|
||||
"default-tag",
|
||||
"default-homepage",
|
||||
"add-shortcut",
|
||||
"reorder",
|
||||
|
||||
@@ -24,11 +24,11 @@ import { TaggedNotes } from "../../screens/notes/tagged";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import ManageTagsSheet from "../sheets/manage-tags";
|
||||
import { Button } from "../ui/button";
|
||||
import { ColorTags } from "./color-tags";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import ManageTags from "../../screens/manage-tags";
|
||||
|
||||
export const Tags = ({ item, close }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -47,7 +47,8 @@ export const Tags = ({ item, close }) => {
|
||||
>
|
||||
<Button
|
||||
onPress={async () => {
|
||||
ManageTagsSheet.present([item.id]);
|
||||
ManageTags.present([item.id]);
|
||||
close();
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.primary.accent
|
||||
|
||||
@@ -42,10 +42,11 @@ import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import ExportNotesSheet from "../sheets/export-notes";
|
||||
import ManageTagsSheet from "../sheets/manage-tags";
|
||||
|
||||
import { MoveNotebook } from "../../screens/move-notebook";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import NativeTooltip from "../../utils/tooltip";
|
||||
import ManageTags from "../../screens/manage-tags";
|
||||
|
||||
export const SelectionHeader = React.memo(
|
||||
({
|
||||
@@ -209,7 +210,7 @@ export const SelectionHeader = React.memo(
|
||||
{
|
||||
title: strings.manageTags(),
|
||||
onPress: async () => {
|
||||
ManageTagsSheet.present(selectedItemsList);
|
||||
ManageTags.present(selectedItemsList);
|
||||
},
|
||||
visible: type === "note",
|
||||
icon: "pound"
|
||||
|
||||
@@ -24,23 +24,22 @@ import { TextInput, View } from "react-native";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { db } from "../../../common/database";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import {
|
||||
ToastManager,
|
||||
eSendEvent,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import { ToastManager, eSendEvent } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { eOnNotebookUpdated } from "../../../utils/events";
|
||||
import { getParentNotebookId } from "../../../utils/notebooks";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import DialogButtons from "../../dialog/dialog-buttons";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { defaultBorderRadius } from "../../../utils/size";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getContainerBorder } from "../../../utils/colors";
|
||||
|
||||
export const AddNotebookSheet = ({
|
||||
notebook,
|
||||
@@ -55,6 +54,7 @@ export const AddNotebookSheet = ({
|
||||
showMoveNotesOnComplete: boolean;
|
||||
defaultTitle?: string;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const title = useRef(notebook?.title || defaultTitle);
|
||||
const description = useRef(notebook?.description);
|
||||
const titleInput = useRef<TextInput>(null);
|
||||
@@ -124,57 +124,66 @@ export const AddNotebookSheet = ({
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
maxHeight: DDS.isTab ? "90%" : "97%",
|
||||
borderRadius: DDS.isTab ? 5 : 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 400 : "85%",
|
||||
maxHeight: 450,
|
||||
borderRadius: defaultBorderRadius,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12,
|
||||
...getContainerBorder(colors.primary.border, 0.5),
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<Heading size={AppFontSize.lg}>
|
||||
{notebook ? strings.editNotebook() : strings.newNotebook()}
|
||||
</Heading>
|
||||
|
||||
<Seperator />
|
||||
|
||||
<Input
|
||||
fwdRef={titleInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.title}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
}}
|
||||
onLayout={() => {
|
||||
setImmediate(() => {
|
||||
titleInput?.current?.focus();
|
||||
});
|
||||
}}
|
||||
placeholder={strings.enterNotebookTitle()}
|
||||
onSubmit={() => {
|
||||
descriptionInput.current?.focus();
|
||||
}}
|
||||
returnKeyLabel="Next"
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.title : title.current}
|
||||
/>
|
||||
|
||||
<Input
|
||||
fwdRef={descriptionInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.description}
|
||||
onChangeText={(value) => {
|
||||
description.current = value;
|
||||
}}
|
||||
placeholder={strings.enterNotebookDescription()}
|
||||
returnKeyLabel={strings.next()}
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.description : ""}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title={notebook ? strings.save() : strings.add()}
|
||||
type="accent"
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP * 2,
|
||||
width: "100%"
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
onPress={onSaveChanges}
|
||||
>
|
||||
<DialogHeader
|
||||
title={notebook ? strings.editNotebook() : strings.newNotebook()}
|
||||
/>
|
||||
|
||||
<Input
|
||||
fwdRef={titleInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.title}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
}}
|
||||
onLayout={() => {
|
||||
setTimeout(() => {
|
||||
titleInput?.current?.focus();
|
||||
}, 300);
|
||||
}}
|
||||
placeholder={strings.enterNotebookTitle()}
|
||||
onSubmit={() => {
|
||||
descriptionInput.current?.focus();
|
||||
}}
|
||||
returnKeyLabel="Next"
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.title : title.current}
|
||||
/>
|
||||
|
||||
<Input
|
||||
fwdRef={descriptionInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.description}
|
||||
onChangeText={(value) => {
|
||||
description.current = value;
|
||||
}}
|
||||
placeholder={strings.enterNotebookDescription()}
|
||||
returnKeyLabel={strings.next()}
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.description : ""}
|
||||
/>
|
||||
</View>
|
||||
<DialogButtons
|
||||
onPressNegative={() => {
|
||||
title.current = undefined;
|
||||
close?.(false);
|
||||
useSettingStore.getState().setSheetKeyboardHandler(true);
|
||||
}}
|
||||
positiveTitle={notebook ? strings.save() : strings.add()}
|
||||
negativeTitle={strings.cancel()}
|
||||
onPressPositive={onSaveChanges}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
@@ -188,9 +197,9 @@ AddNotebookSheet.present = (
|
||||
showMoveNotesOnComplete = true,
|
||||
defaultTitle?: string
|
||||
) => {
|
||||
presentSheet({
|
||||
presentDialog({
|
||||
context: context,
|
||||
component: (ref, close) => (
|
||||
component: (close) => (
|
||||
<AddNotebookSheet
|
||||
notebook={notebook}
|
||||
parentNotebook={parentNotebook}
|
||||
|
||||
@@ -283,7 +283,14 @@ export default function LinkNote(props: {
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Paragraph numberOfLines={1}>{selectedNote?.title}</Paragraph>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{selectedNote?.title}
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 { ItemReference, 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?: ItemReference;
|
||||
};
|
||||
|
||||
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?: ItemReference,
|
||||
isSheet?: boolean
|
||||
) => {
|
||||
presentSheet({
|
||||
context: isSheet ? "local" : undefined,
|
||||
component: (ref, close, update) => (
|
||||
<ReminderSheet
|
||||
actionSheetRef={ref}
|
||||
close={close}
|
||||
update={update}
|
||||
reminder={reminder}
|
||||
reference={reference}
|
||||
/>
|
||||
)
|
||||
});
|
||||
};
|
||||
@@ -52,23 +52,42 @@ const Sort = ({
|
||||
hideGroupOptions?: boolean;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
const groupType =
|
||||
screen === "Archive"
|
||||
? "archive"
|
||||
: screen === "Search"
|
||||
? "search"
|
||||
: screen === "Notes"
|
||||
? "home"
|
||||
: screen === "Trash" || type === "trash"
|
||||
? "trash"
|
||||
: ((type + "s") as GroupingKey);
|
||||
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings.getGroupOptions(
|
||||
screen === "Notes"
|
||||
? "home"
|
||||
: screen === "Trash"
|
||||
? "trash"
|
||||
: ((type + "s") as GroupingKey)
|
||||
)
|
||||
db.settings.getGroupOptions(groupType)
|
||||
);
|
||||
|
||||
const getSortButtonTitle = () => {
|
||||
const { sortDirection, groupBy, sortBy } = groupOptions || {};
|
||||
const isAlphabetical = groupBy === "abc" || sortBy === "title";
|
||||
const isDueDate = sortBy === "dueDate";
|
||||
const isRelevance = sortBy === "relevance";
|
||||
|
||||
if (sortDirection === "asc") {
|
||||
if (isAlphabetical) return strings.aToZ();
|
||||
if (isDueDate) return strings.earliestFirst();
|
||||
if (isRelevance) return strings.leastRelevantFirst();
|
||||
return strings.oldNew();
|
||||
} else {
|
||||
if (isAlphabetical) return strings.zToA();
|
||||
if (isDueDate) return strings.latestFirst();
|
||||
if (isRelevance) return strings.mostRelevantFirst();
|
||||
return strings.newOld();
|
||||
}
|
||||
};
|
||||
|
||||
const updateGroupOptions = async (_groupOptions: GroupOptions) => {
|
||||
const groupType =
|
||||
screen === "Notes"
|
||||
? "home"
|
||||
: screen === "Trash"
|
||||
? "trash"
|
||||
: ((type + "s") as GroupingKey);
|
||||
console.log("updateGroupOptions for group", groupType, "in", screen);
|
||||
await db.settings.setGroupOptions(groupType, _groupOptions);
|
||||
setGroupOptions(_groupOptions);
|
||||
setTimeout(() => {
|
||||
@@ -109,31 +128,10 @@ const Sort = ({
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
size={AppFontSize.lg}
|
||||
style={{
|
||||
alignSelf: "center"
|
||||
}}
|
||||
>
|
||||
{strings.sortBy()}
|
||||
</Heading>
|
||||
<Heading size={AppFontSize.lg}>{strings.sortBy()}</Heading>
|
||||
|
||||
<Button
|
||||
title={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
? groupOptions?.groupBy === "abc" ||
|
||||
groupOptions?.sortBy === "title"
|
||||
? strings.aToZ()
|
||||
: groupOptions?.sortBy === "dueDate"
|
||||
? strings.earliestFirst()
|
||||
: strings.oldNew()
|
||||
: groupOptions?.groupBy === "abc" ||
|
||||
groupOptions?.sortBy === "title"
|
||||
? strings.zToA()
|
||||
: groupOptions?.sortBy === "dueDate"
|
||||
? strings.latestFirst()
|
||||
: strings.newOld()
|
||||
}
|
||||
title={getSortButtonTitle()}
|
||||
icon={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
? "sort-ascending"
|
||||
@@ -157,13 +155,24 @@ const Sort = ({
|
||||
borderBottomColor: colors.primary.border
|
||||
}}
|
||||
>
|
||||
{Object.keys(SORT).map((item) =>
|
||||
(item === "dueDate" && screen !== "Reminders") ||
|
||||
(screen !== "Tags" &&
|
||||
screen !== "Reminders" &&
|
||||
item === "dateModified") ||
|
||||
((screen === "Tags" || screen === "Reminders") &&
|
||||
item === "dateEdited") ? null : (
|
||||
{Object.keys(SORT).map((item) => {
|
||||
const sortOptionVisibility = {
|
||||
relevance: screen === "Search",
|
||||
dueDate: screen === "Reminders",
|
||||
dateModified: screen === "Tags" || screen === "Reminders",
|
||||
dateEdited: screen !== "Tags" && screen !== "Reminders"
|
||||
};
|
||||
|
||||
// Check if this sort option should be skipped for the current screen
|
||||
const shouldSkip =
|
||||
item in sortOptionVisibility &&
|
||||
!sortOptionVisibility[item as keyof typeof sortOptionVisibility];
|
||||
|
||||
if (shouldSkip) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={item}
|
||||
type={groupOptions?.sortBy === item ? "selected" : "plain"}
|
||||
@@ -201,8 +210,8 @@ const Sort = ({
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
)
|
||||
)}
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
{!hideGroupOptions ? (
|
||||
|
||||
@@ -80,6 +80,9 @@ function _MenuItem({
|
||||
case "Monographs":
|
||||
setItemCount(await db.monographs.all.count());
|
||||
break;
|
||||
case "Archive":
|
||||
setItemCount(await db.notes.archived.count());
|
||||
break;
|
||||
case "Trash":
|
||||
setItemCount((await db.trash.all()).length);
|
||||
break;
|
||||
|
||||
@@ -93,7 +93,11 @@ export const NotebookItem = ({
|
||||
<View
|
||||
style={{
|
||||
paddingLeft:
|
||||
item.depth > 0 && item.depth < 6 ? 15 * item.depth : undefined,
|
||||
item.depth === 0
|
||||
? undefined
|
||||
: item.depth < 6
|
||||
? 15 * item.depth
|
||||
: 15 * 5,
|
||||
width: "100%",
|
||||
marginTop: 2
|
||||
}}
|
||||
|
||||
@@ -321,6 +321,9 @@ const Input = ({
|
||||
bottom={10}
|
||||
onPress={button.onPress}
|
||||
color={button.color}
|
||||
style={{
|
||||
marginRight: -8
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -21,11 +21,7 @@ import { FeatureType } from "./components/sheets/new-feature";
|
||||
|
||||
export const features: FeatureType[] = [
|
||||
{
|
||||
title: "Notebook Tree View",
|
||||
body: "Notebooks are now displayed in a tree view for better organization and user experience."
|
||||
},
|
||||
{
|
||||
title: "New SideBar",
|
||||
body: "Sidebar now contains tabs for Notebooks and Tags for easy navigation."
|
||||
title: "Improved Search",
|
||||
body: "Improved search results with better relevance and highlighting."
|
||||
}
|
||||
];
|
||||
|
||||
@@ -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,21 +53,24 @@ 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 AddReminder from "../screens/add-reminder";
|
||||
|
||||
export type ActionId =
|
||||
| "select"
|
||||
| "archive"
|
||||
| "restore"
|
||||
| "delete"
|
||||
| "reorder"
|
||||
@@ -108,7 +110,8 @@ export type ActionId =
|
||||
| "favorite"
|
||||
| "remove-from-notebook"
|
||||
| "trash"
|
||||
| "default-homepage";
|
||||
| "default-homepage"
|
||||
| "default-tag";
|
||||
|
||||
export type Action = {
|
||||
id: ActionId;
|
||||
@@ -156,6 +159,8 @@ export const useActions = ({
|
||||
const [defaultNotebook, setDefaultNotebook] = useState(
|
||||
db.settings.getDefaultNotebook()
|
||||
);
|
||||
const [defaultTag, setDefaultTag] = useState(db.settings.getDefaultTag());
|
||||
|
||||
const [noteInCurrentNotebook, setNoteInCurrentNotebook] = useState(false);
|
||||
const [locked, setLocked] = useState(false);
|
||||
const isHomepage = useSettingStore(
|
||||
@@ -443,7 +448,8 @@ export const useActions = ({
|
||||
title: strings.editReminder(),
|
||||
icon: "pencil",
|
||||
onPress: async () => {
|
||||
ReminderSheet.present(item);
|
||||
AddReminder.present(item);
|
||||
close();
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -467,15 +473,39 @@ export const useActions = ({
|
||||
}
|
||||
|
||||
if (item.type === "tag" || item.type === "notebook") {
|
||||
actions.push({
|
||||
id: "add-shortcut",
|
||||
title: isPinnedToMenu ? strings.removeShortcut() : strings.addShortcut(),
|
||||
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
|
||||
onPress: createMenuShortcut,
|
||||
isToggle: true,
|
||||
checked: isPinnedToMenu,
|
||||
activeColor: colors.error.paragraph
|
||||
});
|
||||
actions.push(
|
||||
{
|
||||
id: "add-shortcut",
|
||||
title: isPinnedToMenu
|
||||
? strings.removeShortcut()
|
||||
: strings.addShortcut(),
|
||||
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
|
||||
onPress: createMenuShortcut,
|
||||
isToggle: true,
|
||||
checked: isPinnedToMenu,
|
||||
activeColor: colors.error.paragraph
|
||||
},
|
||||
{
|
||||
id: "default-tag",
|
||||
title:
|
||||
defaultTag === item.id
|
||||
? strings.removeAsDefault()
|
||||
: strings.setAsDefault(),
|
||||
hidden: item.type !== "tag",
|
||||
icon: "pound",
|
||||
onPress: async () => {
|
||||
if (defaultTag === item.id) {
|
||||
await db.settings.setDefaultTag(undefined);
|
||||
setDefaultTag(undefined);
|
||||
} else {
|
||||
await db.settings.setDefaultTag(item.id);
|
||||
setDefaultTag(item.id);
|
||||
}
|
||||
close();
|
||||
},
|
||||
checked: defaultTag === item.id
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (item.type === "notebook") {
|
||||
@@ -651,7 +681,8 @@ export const useActions = ({
|
||||
|
||||
async function pinToNotifications() {
|
||||
if (notifPinned) {
|
||||
Notifications.remove(item.id);
|
||||
await Notifications.remove(item.id);
|
||||
await sleep(500);
|
||||
await Notifications.get();
|
||||
setNotifPinned(isNotePinnedInNotifications(item));
|
||||
return;
|
||||
@@ -665,7 +696,8 @@ export const useActions = ({
|
||||
return;
|
||||
}
|
||||
|
||||
Notifications.pinNote(item.id);
|
||||
await Notifications.pinNote(item.id);
|
||||
await sleep(500);
|
||||
await Notifications.get();
|
||||
setNotifPinned(isNotePinnedInNotifications(item));
|
||||
}
|
||||
@@ -877,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
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -926,7 +964,8 @@ export const useActions = ({
|
||||
title: strings.remindMe(),
|
||||
icon: "clock-plus-outline",
|
||||
onPress: () => {
|
||||
ReminderSheet.present(undefined, { id: item.id, type: "note" });
|
||||
close();
|
||||
AddReminder.present(undefined, item);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -972,6 +1011,19 @@ export const useActions = ({
|
||||
reference: item as ItemReference
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "archive",
|
||||
title: !item.archived ? strings.archive() : strings.unarchive(),
|
||||
icon: "archive",
|
||||
onPress: async () => {
|
||||
db.notes.archive(!item.archived, item.id);
|
||||
setItem((await db.notes.note(item.id)) as Item);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useArchivedStore.getState().refresh();
|
||||
},
|
||||
checked: item.archived,
|
||||
isToggle: true
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -341,8 +341,15 @@ const doAppLoadActions = async () => {
|
||||
};
|
||||
|
||||
const checkAppUpdateAvailable = async () => {
|
||||
if (__DEV__ || Config.isTesting === "true" || Config.FDROID_BUILD || BETA)
|
||||
if (
|
||||
__DEV__ ||
|
||||
Config.isTesting === "true" ||
|
||||
Config.FDROID_BUILD ||
|
||||
BETA ||
|
||||
!SettingsService.getProperty("checkForUpdates")
|
||||
)
|
||||
return;
|
||||
|
||||
try {
|
||||
const version =
|
||||
Config.GITHUB_RELEASE === "true"
|
||||
|
||||
@@ -32,7 +32,7 @@ export function useGroupOptions(type: any) {
|
||||
const onUpdate = (groupType: string) => {
|
||||
if (groupType !== type) return;
|
||||
const options = db.settings?.getGroupOptions(type) as any;
|
||||
|
||||
if (!options) return;
|
||||
if (
|
||||
groupOptions?.groupBy !== options.groupBy ||
|
||||
groupOptions?.sortBy !== options.sortBy ||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -43,7 +43,7 @@ let Reminders: any = null;
|
||||
let Monographs: any = null;
|
||||
let TaggedNotes: any = null;
|
||||
let ColoredNotes: any = null;
|
||||
|
||||
let Archive: any = null;
|
||||
const AppNavigation = React.memo(
|
||||
() => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -192,6 +192,14 @@ const AppNavigation = React.memo(
|
||||
initialParams={home.name === "ColoredNotes" ? home.params : undefined}
|
||||
/>
|
||||
|
||||
<AppStack.Screen
|
||||
name="Archive"
|
||||
getComponent={() => {
|
||||
Archive = Archive || require("../screens/archive").default;
|
||||
return Archive;
|
||||
}}
|
||||
/>
|
||||
|
||||
<AppStack.Screen
|
||||
name="Reminders"
|
||||
getComponent={() => {
|
||||
@@ -239,6 +247,8 @@ let LinkNotebooks: any = null;
|
||||
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(
|
||||
@@ -318,6 +328,24 @@ export const RootNavigation = () => {
|
||||
return Settings;
|
||||
}}
|
||||
/>
|
||||
|
||||
<RootStack.Screen
|
||||
name="ManageTags"
|
||||
getComponent={() => {
|
||||
ManageTags =
|
||||
ManageTags || require("../screens/manage-tags").default;
|
||||
return ManageTags;
|
||||
}}
|
||||
/>
|
||||
|
||||
<RootStack.Screen
|
||||
name="AddReminder"
|
||||
getComponent={() => {
|
||||
AddReminder =
|
||||
AddReminder || require("../screens/add-reminder").default;
|
||||
return AddReminder;
|
||||
}}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
608
apps/mobile/app/screens/add-reminder/index.tsx
Normal file
608
apps/mobile/app/screens/add-reminder/index.tsx
Normal 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
|
||||
});
|
||||
};
|
||||
88
apps/mobile/app/screens/archive/index.tsx
Normal file
88
apps/mobile/app/screens/archive/index.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
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 React from "react";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import { Header } from "../../components/header";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
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 { db } from "../../common/database";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useArchived } from "../../stores/use-archived-store";
|
||||
|
||||
export const Archive = ({ navigation, route }: NavigationProps<"Archive">) => {
|
||||
const [archive, loading, refresh] = useArchived();
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
Navigation.routeNeedsUpdate(
|
||||
route.name,
|
||||
Navigation.routeUpdateFunctions[route.name]
|
||||
);
|
||||
useNavigationStore.getState().setFocusedRouteId(route?.name);
|
||||
return false;
|
||||
},
|
||||
onBlur: () => false,
|
||||
delay: SettingsService.get().homepage === route.name ? 1 : -1
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SelectionHeader id={route.name} items={archive} type="note" />
|
||||
<Header
|
||||
renderedInRoute={route.name}
|
||||
title={strings.routes[route.name]()}
|
||||
canGoBack={false}
|
||||
hasSearch={true}
|
||||
id={route.name}
|
||||
onSearch={() => {
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(route.name),
|
||||
type: "note",
|
||||
title: route.name,
|
||||
route: route.name,
|
||||
items: db.notes.archived
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DelayLayout wait={loading}>
|
||||
<List
|
||||
data={archive}
|
||||
dataType="note"
|
||||
onRefresh={() => {
|
||||
refresh();
|
||||
}}
|
||||
renderedInRoute="Archive"
|
||||
loading={loading}
|
||||
placeholder={{
|
||||
title: strings.yourArchive(),
|
||||
paragraph: strings.yourArchiveIsEmpty(),
|
||||
loading: strings.loadingArchive()
|
||||
}}
|
||||
headerTitle={strings.routes.Archive()}
|
||||
/>
|
||||
</DelayLayout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Archive;
|
||||
@@ -236,6 +236,11 @@ class Commands {
|
||||
const tabId = useTabStore.getState().currentTab;
|
||||
return this.sendCommand("scrollIntoViewById", id, tabId);
|
||||
};
|
||||
|
||||
scrollToSearchResult = (index: number) => {
|
||||
const tabId = useTabStore.getState().currentTab;
|
||||
return this.sendCommand("scrollToSearchResult", index, tabId);
|
||||
};
|
||||
}
|
||||
|
||||
export default Commands;
|
||||
|
||||
@@ -38,12 +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 ManageTagsSheet from "../../../components/sheets/manage-tags";
|
||||
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 {
|
||||
@@ -74,11 +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 AddReminder from "../../add-reminder";
|
||||
|
||||
const publishNote = async () => {
|
||||
const user = useUserStore.getState().user;
|
||||
@@ -177,7 +178,7 @@ export const useEditorEvents = (
|
||||
useEffect(() => {
|
||||
const handleKeyboardDidShow: KeyboardEventListener = () => {
|
||||
editor.commands.keyboardShown(true);
|
||||
//editor.postMessage(NativeEvents.keyboardShown, undefined);
|
||||
editor.postMessage(NativeEvents.keyboardShown, undefined);
|
||||
};
|
||||
const handleKeyboardDidHide: KeyboardEventListener = () => {
|
||||
editor.commands.keyboardShown(false);
|
||||
@@ -437,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:
|
||||
@@ -448,7 +449,7 @@ export const useEditorEvents = (
|
||||
});
|
||||
return;
|
||||
}
|
||||
ManageTagsSheet.present([noteId]);
|
||||
ManageTags.present([noteId]);
|
||||
break;
|
||||
case EditorEvents.tag:
|
||||
if (editorMessage.value) {
|
||||
@@ -613,6 +614,10 @@ export const useEditorEvents = (
|
||||
eSendEvent(eEditorTabFocused, editorMessage.tabId);
|
||||
if (!editor.state.current.initialLoadCalled) break;
|
||||
if (editorMessage.noteId) {
|
||||
// Wait for next tick to ensure the tab is not changed.
|
||||
await sleep(1);
|
||||
if (useTabStore.getState().currentTab !== editorMessage.tabId)
|
||||
return;
|
||||
if (!useSettingStore.getState().isAppLoading) {
|
||||
const note = await db.notes.note(editorMessage.noteId);
|
||||
if (note) {
|
||||
|
||||
@@ -352,7 +352,7 @@ export const useEditor = (
|
||||
DatabaseLogger.log(`Note saved: ${id}...`);
|
||||
|
||||
clearTimeout(saveTimer);
|
||||
|
||||
const oldNote = currentNotes.current[id];
|
||||
if (id) {
|
||||
currentNotes.current[id] = await db.notes?.note(id);
|
||||
}
|
||||
@@ -365,22 +365,37 @@ export const useEditor = (
|
||||
}
|
||||
});
|
||||
|
||||
const defaultNotebook = db.settings.getDefaultNotebook();
|
||||
if (!state.current.onNoteCreated && defaultNotebook) {
|
||||
onNoteCreated(id, {
|
||||
type: "notebook",
|
||||
id: defaultNotebook
|
||||
});
|
||||
if (!state.current.onNoteCreated) {
|
||||
const defaultNotebook = db.settings.getDefaultNotebook();
|
||||
if (defaultNotebook) {
|
||||
onNoteCreated(id, {
|
||||
type: "notebook",
|
||||
id: defaultNotebook
|
||||
});
|
||||
}
|
||||
|
||||
const defaultTag = db.settings.getDefaultTag();
|
||||
if (defaultTag) {
|
||||
onNoteCreated(id, {
|
||||
type: "tag",
|
||||
id: defaultTag
|
||||
});
|
||||
}
|
||||
} else {
|
||||
state.current?.onNoteCreated && state.current.onNoteCreated(id);
|
||||
state.current.onNoteCreated(id);
|
||||
}
|
||||
}
|
||||
|
||||
postMessage(
|
||||
NativeEvents.title,
|
||||
currentNotes.current[id]?.title,
|
||||
tabId
|
||||
);
|
||||
if (
|
||||
oldNote?.title !== currentNotes.current[id]?.title &&
|
||||
!noteData.title
|
||||
) {
|
||||
postMessage(
|
||||
NativeEvents.title,
|
||||
currentNotes.current[id]?.title,
|
||||
tabId
|
||||
);
|
||||
}
|
||||
|
||||
if (Notifications.isNotePinned(id as string)) {
|
||||
Notifications.pinNote(id as string);
|
||||
@@ -464,8 +479,7 @@ export const useEditor = (
|
||||
}
|
||||
) => {
|
||||
currentNotes.current[note.id] = note;
|
||||
const locked = note && (await db.vaults.itemExists(note));
|
||||
if ((locked || note.content) && note.content?.data) {
|
||||
if (note.content && note.content?.data) {
|
||||
currentContents.current[note.id] = {
|
||||
data: note.content?.data,
|
||||
type: note.content?.type || "tiptap",
|
||||
@@ -493,6 +507,7 @@ export const useEditor = (
|
||||
session?: TabSessionItem;
|
||||
newTab?: boolean;
|
||||
refresh?: boolean;
|
||||
searchResultIndex?: number;
|
||||
}) => {
|
||||
loadNoteMutex.runExclusive(async () => {
|
||||
if (!event) return;
|
||||
@@ -678,13 +693,17 @@ export const useEditor = (
|
||||
{
|
||||
data: currentContents.current[item.id]?.data || "",
|
||||
scrollTop: tab?.session?.scrollTop,
|
||||
selection: tab?.session?.selection
|
||||
selection: tab?.session?.selection,
|
||||
searchResultIndex: event.searchResultIndex
|
||||
},
|
||||
tabId,
|
||||
10000
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
if (event.searchResultIndex !== undefined) {
|
||||
commands.scrollToSearchResult(event.searchResultIndex);
|
||||
}
|
||||
if (blockIdRef.current) {
|
||||
commands.scrollIntoViewById(blockIdRef.current);
|
||||
blockIdRef.current = undefined;
|
||||
|
||||
@@ -29,27 +29,30 @@ import React, {
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { TextInput, View, useWindowDimensions } from "react-native";
|
||||
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../../common/database";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
import { ToastManager, presentSheet } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { db } from "../../common/database";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import {
|
||||
ItemSelection,
|
||||
createItemSelectionStore
|
||||
} from "../../../stores/item-selection-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useTagStore } from "../../../stores/use-tag-store";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
|
||||
import Input from "../../ui/input";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
} from "../../stores/item-selection-store";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { useTagStore } from "../../stores/use-tag-store";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../utils/size";
|
||||
import Input from "../../components/ui/input";
|
||||
import { Pressable } from "../../components/ui/pressable";
|
||||
import Heading from "../../components/ui/typography/heading";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Header } from "../../components/header";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
|
||||
async function updateInitialSelectionState(items: string[]) {
|
||||
const relations = await db.relations
|
||||
@@ -85,16 +88,14 @@ async function updateInitialSelectionState(items: string[]) {
|
||||
|
||||
const useTagItemSelection = createItemSelectionStore(true);
|
||||
|
||||
const ManageTagsSheet = (props: {
|
||||
ids?: string[];
|
||||
actionSheetRef: RefObject<ActionSheetRef>;
|
||||
}) => {
|
||||
const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
const { colors } = useThemeColors();
|
||||
const ids = useMemo(() => props.ids || [], [props.ids]);
|
||||
const ids = props.route.params.ids || [];
|
||||
const [tags, setTags] = useState<VirtualizedGrouping<Tag>>();
|
||||
const [query, setQuery] = useState<string>();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const [focus, setFocus] = useState(false);
|
||||
useNavigationFocus(props.navigation, { focusOnInit: true });
|
||||
const [queryExists, setQueryExists] = useState(false);
|
||||
const dimensions = useWindowDimensions();
|
||||
const refreshSelection = useCallback(() => {
|
||||
@@ -239,112 +240,117 @@ const ManageTagsSheet = (props: {
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
<SafeAreaView
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
maxHeight: dimensions.height * 0.85
|
||||
backgroundColor: colors.primary.background,
|
||||
gap: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
button={{
|
||||
icon: "magnify",
|
||||
color: colors.primary.accent,
|
||||
size: AppFontSize.lg,
|
||||
onPress: () => {}
|
||||
}}
|
||||
testID="tag-input"
|
||||
fwdRef={inputRef}
|
||||
autoCapitalize="none"
|
||||
onChangeText={(v) => {
|
||||
setQuery(sanitizeTag(v));
|
||||
checkQueryExists(sanitizeTag(v));
|
||||
}}
|
||||
onFocusInput={() => {
|
||||
setFocus(true);
|
||||
}}
|
||||
onBlurInput={() => {
|
||||
setFocus(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
onSubmit();
|
||||
}}
|
||||
placeholder={strings.searchForTags()}
|
||||
/>
|
||||
|
||||
{query && !queryExists ? (
|
||||
<Pressable
|
||||
key={"query_item"}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginVertical: 5,
|
||||
justifyContent: "space-between",
|
||||
padding: DefaultAppStyles.GAP
|
||||
}}
|
||||
onPress={onSubmit}
|
||||
type="selected"
|
||||
>
|
||||
<Heading size={AppFontSize.sm} color={colors.selected.heading}>
|
||||
{strings.add()} {'"' + "#" + query + '"'}
|
||||
</Heading>
|
||||
<Icon
|
||||
name="plus"
|
||||
color={colors.selected.icon}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null}
|
||||
|
||||
<Header title={strings.manageTags()} canGoBack />
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexGrow: 1,
|
||||
height: "100%"
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<FlashList
|
||||
data={tags?.placeholders}
|
||||
keyboardShouldPersistTaps
|
||||
keyboardDismissMode="interactive"
|
||||
estimatedItemSize={50}
|
||||
renderItem={renderTag}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 200,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading size={50} color={colors.secondary.heading}>
|
||||
#
|
||||
</Heading>
|
||||
<Paragraph
|
||||
textBreakStrategy="balanced"
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{strings.emptyPlaceholders("tag")}
|
||||
</Paragraph>
|
||||
</View>
|
||||
}
|
||||
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||
<Input
|
||||
button={{
|
||||
icon: "magnify",
|
||||
color: colors.primary.accent,
|
||||
size: AppFontSize.lg,
|
||||
onPress: () => {}
|
||||
}}
|
||||
testID="tag-input"
|
||||
fwdRef={inputRef}
|
||||
autoCapitalize="none"
|
||||
onChangeText={(v) => {
|
||||
setQuery(sanitizeTag(v));
|
||||
checkQueryExists(sanitizeTag(v));
|
||||
}}
|
||||
onFocusInput={() => {
|
||||
setFocus(true);
|
||||
}}
|
||||
onBlurInput={() => {
|
||||
setFocus(false);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
onSubmit();
|
||||
}}
|
||||
placeholder={strings.searchForTags()}
|
||||
/>
|
||||
|
||||
{query && !queryExists ? (
|
||||
<Pressable
|
||||
key={"query_item"}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
onPress={onSubmit}
|
||||
type="selected"
|
||||
>
|
||||
<Heading size={AppFontSize.sm} color={colors.selected.heading}>
|
||||
{strings.add()} {'"' + "#" + query + '"'}
|
||||
</Heading>
|
||||
<Icon
|
||||
name="plus"
|
||||
color={colors.selected.icon}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexGrow: 1,
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
<FlashList
|
||||
data={tags?.placeholders}
|
||||
keyboardShouldPersistTaps
|
||||
keyboardDismissMode="interactive"
|
||||
estimatedItemSize={50}
|
||||
renderItem={renderTag}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 200,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading size={50} color={colors.secondary.heading}>
|
||||
#
|
||||
</Heading>
|
||||
<Paragraph
|
||||
textBreakStrategy="balanced"
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{strings.emptyPlaceholders("tag")}
|
||||
</Paragraph>
|
||||
</View>
|
||||
}
|
||||
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
ManageTagsSheet.present = (ids?: string[]) => {
|
||||
presentSheet({
|
||||
component: (ref) => {
|
||||
return <ManageTagsSheet actionSheetRef={ref} ids={ids} />;
|
||||
}
|
||||
ManageTags.present = (ids?: string[]) => {
|
||||
Navigation.push("ManageTags", {
|
||||
ids: ids
|
||||
});
|
||||
};
|
||||
|
||||
export default ManageTagsSheet;
|
||||
export default ManageTags;
|
||||
|
||||
const TagItem = ({
|
||||
id,
|
||||
@@ -248,7 +248,13 @@ const SelectableNoteItem = React.memo(
|
||||
flexGrow: 1
|
||||
}}
|
||||
>
|
||||
<Heading size={AppFontSize.sm} numberOfLines={1}>
|
||||
<Heading
|
||||
size={AppFontSize.sm}
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
maxWidth: "93%"
|
||||
}}
|
||||
>
|
||||
{item?.title}
|
||||
</Heading>
|
||||
{item.type == "note" && item.headline ? (
|
||||
@@ -256,6 +262,9 @@ const SelectableNoteItem = React.memo(
|
||||
numberOfLines={1}
|
||||
color={colors?.secondary.paragraph}
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
maxWidth: "93%"
|
||||
}}
|
||||
>
|
||||
{item.headline}
|
||||
</Paragraph>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
|
||||
@@ -17,35 +17,37 @@ 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 { Item, Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
import {
|
||||
FilteredSelector,
|
||||
Item,
|
||||
Note,
|
||||
VirtualizedGrouping
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { DatabaseLogger, db } from "../../common/database";
|
||||
import List from "../../components/list";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import {
|
||||
ToastManager,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { ToastManager, eSubscribeEvent } from "../../services/event-manager";
|
||||
import { NavigationProps } from "../../services/navigation";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { eOnRefreshSearch } from "../../utils/events";
|
||||
import { eGroupOptionsUpdated, eOnRefreshSearch } from "../../utils/events";
|
||||
import { SearchBar } from "./search-bar";
|
||||
import { FilteredSelector } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
const [results, setResults] = useState<VirtualizedGrouping<Item>>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchStatus, setSearchStatus] = useState<string>();
|
||||
const currentQuery = useRef<string>();
|
||||
const timer = useRef<NodeJS.Timeout>();
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
useNavigationFocus(navigation, {
|
||||
onFocus: (prev) => {
|
||||
useNavigationStore.getState().setFocusedRouteId(route.name);
|
||||
return !prev?.current;
|
||||
},
|
||||
onBlur: () => false
|
||||
onBlur: () => {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const onSearch = React.useCallback(
|
||||
@@ -60,32 +62,35 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
let results: VirtualizedGrouping<Item> | undefined;
|
||||
|
||||
const groupOptions = db.settings.getGroupOptions("search");
|
||||
switch (route.params.type) {
|
||||
case "note":
|
||||
results = await db.lookup
|
||||
.notes(query, route.params.items as FilteredSelector<Note>)
|
||||
.sorted();
|
||||
results = await db.lookup.notesWithHighlighting(
|
||||
query,
|
||||
route.params.items as FilteredSelector<Note>,
|
||||
groupOptions
|
||||
);
|
||||
|
||||
break;
|
||||
case "notebook":
|
||||
results = await db.lookup.notebooks(query).sorted();
|
||||
results = await db.lookup.notebooks(query).sorted(groupOptions);
|
||||
break;
|
||||
case "tag":
|
||||
results = await db.lookup.tags(query).sorted();
|
||||
results = await db.lookup.tags(query).sorted(groupOptions);
|
||||
break;
|
||||
case "reminder":
|
||||
results = await db.lookup.reminders(query).sorted();
|
||||
results = await db.lookup.reminders(query).sorted(groupOptions);
|
||||
break;
|
||||
case "trash":
|
||||
results = await db.lookup.trash(query).sorted();
|
||||
results = await db.lookup.trash(query).sorted(groupOptions);
|
||||
break;
|
||||
case "attachment":
|
||||
results = await db.lookup.attachments(query).sorted();
|
||||
results = await db.lookup.attachments(query).sorted(groupOptions);
|
||||
break;
|
||||
default:
|
||||
results = undefined;
|
||||
}
|
||||
|
||||
if (currentQuery.current !== query) return;
|
||||
if (!results) {
|
||||
setSearchStatus(strings.noResultsFound(query));
|
||||
setLoading(false);
|
||||
@@ -97,6 +102,7 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
);
|
||||
if (currentQuery.current !== query) return;
|
||||
await results.item(0);
|
||||
if (currentQuery.current !== query) return;
|
||||
setResults(results);
|
||||
if (results.placeholders?.length === 0) {
|
||||
setSearchStatus(strings.noResultsFound(query));
|
||||
@@ -118,9 +124,18 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
onSearch(currentQuery.current);
|
||||
}
|
||||
};
|
||||
eSubscribeEvent(eOnRefreshSearch, onRefreshSearch);
|
||||
|
||||
const subs = [
|
||||
eSubscribeEvent(eGroupOptionsUpdated, (groupType) => {
|
||||
if (groupType === "search") {
|
||||
onSearch(currentQuery.current);
|
||||
}
|
||||
}),
|
||||
eSubscribeEvent(eOnRefreshSearch, onRefreshSearch)
|
||||
];
|
||||
|
||||
return () => {
|
||||
eUnSubscribeEvent(eOnRefreshSearch, onRefreshSearch);
|
||||
subs.forEach((sub) => sub?.unsubscribe());
|
||||
};
|
||||
}, [onSearch, route.params?.type]);
|
||||
|
||||
@@ -137,7 +152,7 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = setTimeout(() => {
|
||||
onSearch(query);
|
||||
}, 300);
|
||||
}, 500);
|
||||
}}
|
||||
loading={loading}
|
||||
/>
|
||||
|
||||
@@ -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} />
|
||||
)
|
||||
});
|
||||
};
|
||||
@@ -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 />
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
@@ -82,7 +80,8 @@ export const settingsGroups: SettingSection[] = [
|
||||
useHook: () => useUserStore((state) => state.user),
|
||||
hidden: (current) => !current,
|
||||
name: (current) => {
|
||||
const user = current as User;
|
||||
const user = (current as User) || useUserStore.getState().user;
|
||||
if (!user) return strings.subscribeToPro();
|
||||
const isBasic = user.subscription?.type === SUBSCRIPTION_STATUS.BASIC;
|
||||
const isTrial = user.subscription?.type === SUBSCRIPTION_STATUS.TRIAL;
|
||||
return isBasic || !user.subscription?.type
|
||||
@@ -96,6 +95,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
icon: "crown",
|
||||
description: (current) => {
|
||||
const user = current as User;
|
||||
if (!user) return strings.neverHesitate();
|
||||
const subscriptionDaysLeft =
|
||||
user &&
|
||||
getTimeLeft(
|
||||
@@ -241,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()
|
||||
},
|
||||
{
|
||||
@@ -670,6 +668,13 @@ export const settingsGroups: SettingSection[] = [
|
||||
});
|
||||
},
|
||||
disabled: () => !db.settings.getDefaultNotebook()
|
||||
},
|
||||
{
|
||||
id: "disable-update-check",
|
||||
type: "switch",
|
||||
name: strings.autoUpdateCheck(),
|
||||
description: strings.autoUpdateCheckDesc(),
|
||||
property: "checkForUpdates"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
rootNavigatorRef
|
||||
} from "../utils/global-refs";
|
||||
import { eSendEvent } from "./event-manager";
|
||||
import { useArchivedStore } from "../stores/use-archived-store";
|
||||
|
||||
/**
|
||||
* Routes that should be updated on focus
|
||||
@@ -66,7 +67,10 @@ const routeNames = {
|
||||
Reminders: "Reminders",
|
||||
MoveNotebook: "MoveNotebook",
|
||||
LinkNotebooks: "LinkNotebooks",
|
||||
MoveNotes: "MoveNotes"
|
||||
MoveNotes: "MoveNotes",
|
||||
Archive: "Archive",
|
||||
ManageTags: "ManageTags",
|
||||
AddReminder: "AddReminder"
|
||||
};
|
||||
|
||||
export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
|
||||
@@ -92,7 +96,8 @@ const routeUpdateFunctions: {
|
||||
TopicNotes: (params) => eSendEvent("TopicNotes", params),
|
||||
Monographs: (params) => eSendEvent("Monographs", params),
|
||||
Reminders: () => useReminderStore.getState().refresh(),
|
||||
Search: () => eSendEvent(eOnRefreshSearch)
|
||||
Search: () => eSendEvent(eOnRefreshSearch),
|
||||
Archive: () => useArchivedStore.getState().refresh()
|
||||
};
|
||||
|
||||
function clearRouteFromQueue(routeName: RouteName) {
|
||||
|
||||
30
apps/mobile/app/stores/use-archived-store.ts
Normal file
30
apps/mobile/app/stores/use-archived-store.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 { db } from "../common/database";
|
||||
import createDBCollectionStore from "./create-db-collection-store";
|
||||
|
||||
const { useStore: useArchivedStore, useCollection: useArchived } =
|
||||
createDBCollectionStore({
|
||||
getCollection: () =>
|
||||
db.notes.archived.grouped(db.settings.getGroupOptions("archive")),
|
||||
eagerlyFetchFirstBatch: true
|
||||
});
|
||||
|
||||
export { useArchivedStore, useArchived };
|
||||
@@ -74,6 +74,7 @@ export interface RouteParams extends ParamListBase {
|
||||
TaggedNotes: NotesScreenParams;
|
||||
ColoredNotes: NotesScreenParams;
|
||||
TopicNotes: NotesScreenParams;
|
||||
Archive: GenericRouteParam;
|
||||
Monographs: NotesScreenParams;
|
||||
Reminders: GenericRouteParam;
|
||||
SettingsGroup: GenericRouteParam;
|
||||
@@ -90,6 +91,13 @@ export interface RouteParams extends ParamListBase {
|
||||
MoveNotes: {
|
||||
notebook: Notebook;
|
||||
};
|
||||
ManageTags: {
|
||||
ids?: string[];
|
||||
};
|
||||
AddReminder: {
|
||||
reminder?: Reminder;
|
||||
reference?: Note;
|
||||
};
|
||||
}
|
||||
|
||||
export type RouteName = keyof RouteParams;
|
||||
|
||||
@@ -98,6 +98,7 @@ export type Settings = {
|
||||
lastFullBackupDate?: number;
|
||||
serverUrls?: Record<HostId, string>;
|
||||
defaultSidebarTab: number;
|
||||
checkForUpdates?: boolean;
|
||||
};
|
||||
|
||||
type DimensionsType = {
|
||||
@@ -196,7 +197,8 @@ export const defaultSettings: SettingStore["settings"] = {
|
||||
settingsVersion: 0,
|
||||
backupType: "partial",
|
||||
fullBackupReminder: "never",
|
||||
lastFullBackupDate: 0
|
||||
lastFullBackupDate: 0,
|
||||
checkForUpdates: true
|
||||
};
|
||||
|
||||
export const useSettingStore = create<SettingStore>((set, get) => ({
|
||||
|
||||
@@ -45,7 +45,8 @@ export const SORT = {
|
||||
dateEdited: "Date edited",
|
||||
dateCreated: "Date created",
|
||||
title: "Title",
|
||||
dueDate: "Due date"
|
||||
dueDate: "Due date",
|
||||
relevance: "Relevance"
|
||||
};
|
||||
|
||||
export const itemSkus = [
|
||||
|
||||
@@ -77,12 +77,13 @@ export const MenuItemsList: SideMenuItem[] = [
|
||||
},
|
||||
type: "side-menu-item"
|
||||
},
|
||||
// {
|
||||
// dataType: "note",
|
||||
// id: "Archive",
|
||||
// title: "Archive",
|
||||
// icon: "archive"
|
||||
// },
|
||||
{
|
||||
dataType: "note",
|
||||
id: "Archive",
|
||||
title: "Archive",
|
||||
icon: "archive",
|
||||
type: "side-menu-item"
|
||||
},
|
||||
{
|
||||
dataType: "note",
|
||||
id: "Trash",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby ">= 2.6.10"
|
||||
# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper
|
||||
# bound in the template on Cocoapods with next React Native release.
|
||||
gem 'cocoapods', '>= 1.13', '< 1.15'
|
||||
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
|
||||
|
||||
# Exclude problematic versions of cocoapods and activesupport that causes build failures.
|
||||
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
|
||||
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
|
||||
gem 'xcodeproj', '< 1.26.0'
|
||||
gem 'concurrent-ruby', '< 1.3.4'
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.5)
|
||||
CFPropertyList (3.0.7)
|
||||
base64
|
||||
nkf
|
||||
rexml
|
||||
activesupport (6.1.5)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
activesupport (7.2.2.1)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
base64 (0.3.0)
|
||||
benchmark (0.4.1)
|
||||
bigdecimal (3.2.1)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.11.3)
|
||||
cocoapods (1.15.2)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.11.3)
|
||||
cocoapods-core (= 1.15.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||
cocoapods-downloader (>= 2.1, < 3.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
@@ -32,10 +43,10 @@ GEM
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 1.0, < 3.0)
|
||||
xcodeproj (>= 1.21.0, < 2.0)
|
||||
cocoapods-core (1.11.3)
|
||||
activesupport (>= 5.0, < 7)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.23.0, < 2.0)
|
||||
cocoapods-core (1.15.2)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
@@ -45,7 +56,7 @@ GEM
|
||||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (1.6.3)
|
||||
cocoapods-downloader (2.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
@@ -54,47 +65,56 @@ GEM
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.1.10)
|
||||
concurrent-ruby (1.3.3)
|
||||
connection_pool (2.5.3)
|
||||
drb (2.2.3)
|
||||
escape (0.0.4)
|
||||
ethon (0.15.0)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
ffi (1.15.5)
|
||||
ffi (1.17.2)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.10.0)
|
||||
httpclient (2.9.0)
|
||||
mutex_m
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.6.1)
|
||||
minitest (5.15.0)
|
||||
json (2.12.2)
|
||||
logger (1.7.0)
|
||||
minitest (5.25.5)
|
||||
molinillo (0.8.0)
|
||||
mutex_m (0.3.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
nkf (0.2.0)
|
||||
public_suffix (4.0.7)
|
||||
rexml (3.2.5)
|
||||
rexml (3.4.1)
|
||||
ruby-macho (2.5.1)
|
||||
typhoeus (1.4.0)
|
||||
securerandom (0.4.1)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.4)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
xcodeproj (1.21.0)
|
||||
xcodeproj (1.25.1)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
zeitwerk (2.5.4)
|
||||
rexml (>= 3.3.6, < 4.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods (~> 1.11, >= 1.11.2)
|
||||
activesupport (>= 6.1.7.5, != 7.1.0)
|
||||
cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
|
||||
concurrent-ruby (< 1.3.4)
|
||||
xcodeproj (< 1.26.0)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.7.4p191
|
||||
ruby 3.4.1p0
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.27
|
||||
2.6.2
|
||||
|
||||
@@ -48,6 +48,9 @@ react {
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
hermesFlags = ["-O", "-output-source-map"]
|
||||
|
||||
/* Autolinking */
|
||||
autolinkLibrariesWithApp()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +124,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3049
|
||||
versionCode 3059
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
@@ -258,7 +261,3 @@ copyFontFiles.dependsOn(copyEditorBundle);
|
||||
copyPlainEditorBundle.dependsOn(copyFontFiles);
|
||||
preBuild.dependsOn(copyPlainEditorBundle)
|
||||
|
||||
apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
<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.READ_MEDIA_IMAGES"/>
|
||||
<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" />
|
||||
@@ -59,18 +58,8 @@
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/BootTheme"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
tools:replace="android:supportsRtl">
|
||||
|
||||
<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>
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
|
||||
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
|
||||
<intent-filter>
|
||||
@@ -92,6 +81,15 @@
|
||||
android:resource="@xml/note_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<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>
|
||||
|
||||
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:label="NotePreviewConfigure"
|
||||
android:launchMode="singleTask"
|
||||
|
||||
@@ -14,19 +14,21 @@ public class BootRecieverService extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!isAppOnForeground((context))) {
|
||||
/**
|
||||
We will start our service and send extra info about
|
||||
network connections
|
||||
**/
|
||||
Intent serviceIntent = new Intent(context, BootTaskService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
HeadlessJsTaskService.acquireWakeLockNow(context);
|
||||
}
|
||||
try {
|
||||
if (!isAppOnForeground((context))) {
|
||||
/**
|
||||
We will start our service and send extra info about
|
||||
network connections
|
||||
**/
|
||||
Intent serviceIntent = new Intent(context, BootTaskService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
HeadlessJsTaskService.acquireWakeLockNow(context);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
private boolean isAppOnForeground(Context context) {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
|
||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.soloader.SoLoader
|
||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
@@ -36,7 +37,7 @@ class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
SoLoader.init(this, false)
|
||||
SoLoader.init(this, OpenSourceMergedSoMapping)
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
load()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,16 +3,16 @@ import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "34.0.0"
|
||||
minSdkVersion = 23
|
||||
compileSdkVersion = 34
|
||||
targetSdkVersion = 34
|
||||
kotlinVersion = '1.9.22'
|
||||
buildToolsVersion = "35.0.0"
|
||||
minSdkVersion = 24
|
||||
compileSdkVersion = 35
|
||||
targetSdkVersion = 35
|
||||
kotlinVersion = '2.0.21'
|
||||
androidXAnnotation = "1.1.0"
|
||||
androidXCoreVersion = "1.7.0"
|
||||
androidXCore = "1.7.0"
|
||||
androidXBrowser = "1.0.0"
|
||||
ndkVersion = "23.2.8568313"
|
||||
ndkVersion = "27.1.12297006"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -23,7 +23,7 @@ org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
# android.enableJetifier=true
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
@@ -47,4 +47,4 @@ hermesEnabled=true
|
||||
# fdroid
|
||||
fdroidBuild=false
|
||||
# -DSQLITE_USER_AUTHENTICATION=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_DEPRECATED=1 -DSQLITE_OMIT_PROGRESS_CALLBACK=1
|
||||
quickSqliteFlags=-DSQLITE3MC_OMIT_AES_HARDWARE_SUPPORT -DHAVE_CIPHER_AES_128_CBC=0 -DHAVE_CIPHER_AES_256_CBC=0 -DHAVE_CIPHER_SQLCIPHER=0 -DHAVE_CIPHER_RC4=0 -DHAVE_CIPHER_CHACHA20=1 -DSQLITE_ENABLE_FTS5 -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_DEPRECATED=1 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_USE_ALLOCA=1
|
||||
quickSqliteFlags=-DSQLITE3MC_OMIT_AES_HARDWARE_SUPPORT -DHAVE_CIPHER_AES_128_CBC=0 -DHAVE_CIPHER_AES_256_CBC=0 -DHAVE_CIPHER_SQLCIPHER=0 -DHAVE_CIPHER_RC4=0 -DHAVE_CIPHER_CHACHA20=1 -DSQLITE_ENABLE_FTS5 -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_DEPRECATED=1 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_USE_ALLOCA=1 -DSQLITE_ENABLE_REGEXP=1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
7
apps/mobile/native/android/gradlew
vendored
7
apps/mobile/native/android/gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
2
apps/mobile/native/android/gradlew.bat
vendored
2
apps/mobile/native/android/gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
- v3.1.0 brings new features and improvements to enhance your Notesnook experience.
|
||||
- Notebooks and Tags are not located in the Sidebar
|
||||
- Tree view for notebooks
|
||||
- Improved list view UI for better user experience
|
||||
- Improved notebook related actions by integrating tree view in most places
|
||||
- You can set any notebook, tag or colors as home screen
|
||||
- Fix toolbar hidden behing keyboard on android 15 and 16
|
||||
- Minor bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -1,4 +1,9 @@
|
||||
pluginManagement { includeBuild("../../node_modules/@react-native/gradle-plugin") }
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
||||
rootProject.name = 'Notesnook'
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
includeBuild('../../node_modules/@react-native/gradle-plugin')
|
||||
|
||||
include ":lazysodium"
|
||||
project(':lazysodium').projectDir = new File(rootProject.projectDir, '../../node_modules/@ammarahmed/react-native-sodium/android/lazysodium')
|
||||
|
||||
@@ -6,7 +6,7 @@ module.exports = function (api, opts) {
|
||||
if (!PROCESS_ENV) PROCESS_ENV = 'production';
|
||||
|
||||
const env = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-named-capturing-groups-regex',
|
||||
'react-native-reanimated/plugin',
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
6517B7C12B6838EB0079FF37 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7BE2B6838EB0079FF37 /* OpenSans-Regular.ttf */; };
|
||||
6517B7C22B6838EB0079FF37 /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7BF2B6838EB0079FF37 /* OpenSans-SemiBold.ttf */; };
|
||||
6517B7C32B6838EB0079FF37 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */; };
|
||||
651F09DA2E0D262B00495DED /* fts5-html.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651F09D92E0D262B00495DED /* fts5-html.xcframework */; };
|
||||
651F09DB2E0D262B00495DED /* fts5-html.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 651F09D92E0D262B00495DED /* fts5-html.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
651F09DD2E0D263300495DED /* fts5-html.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651F09D92E0D262B00495DED /* fts5-html.xcframework */; };
|
||||
651F09DE2E0D263300495DED /* fts5-html.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 651F09D92E0D262B00495DED /* fts5-html.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */; };
|
||||
656835812BB29A9800144BAB /* OpenSans-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 656835802BB29A8300144BAB /* OpenSans-Italic.ttf */; };
|
||||
6569927F2C5754F10041CD41 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */; };
|
||||
@@ -79,6 +83,28 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
651F09DC2E0D262C00495DED /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
651F09DB2E0D262B00495DED /* fts5-html.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
651F09DF2E0D263300495DED /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
651F09DE2E0D263300495DED /* fts5-html.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
65A7F34B255687E600699170 /* Embed App Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -120,11 +146,11 @@
|
||||
6517B7BE2B6838EB0079FF37 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Regular.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Regular.ttf"; sourceTree = "<group>"; };
|
||||
6517B7BF2B6838EB0079FF37 /* OpenSans-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-SemiBold.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-SemiBold.ttf"; sourceTree = "<group>"; };
|
||||
6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Bold.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Bold.ttf"; sourceTree = "<group>"; };
|
||||
651F09D92E0D262B00495DED /* fts5-html.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = "fts5-html.xcframework"; sourceTree = "<group>"; };
|
||||
6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = Notesnook/BootSplash.storyboard; sourceTree = "<group>"; };
|
||||
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; };
|
||||
@@ -177,6 +203,7 @@
|
||||
files = (
|
||||
2B3F87EC6B2264CD8ABA5DA8 /* libPods-Notesnook.a in Frameworks */,
|
||||
6515C42F2580AA3000E83E39 /* StoreKit.framework in Frameworks */,
|
||||
651F09DA2E0D262B00495DED /* fts5-html.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -209,6 +236,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
651F09DD2E0D263300495DED /* fts5-html.xcframework in Frameworks */,
|
||||
C619D096A9DE2070DBEAC70F /* libPods-Make Note.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -256,7 +284,7 @@
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */,
|
||||
651F09D92E0D262B00495DED /* fts5-html.xcframework */,
|
||||
6515C42E2580AA2F00E83E39 /* StoreKit.framework */,
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
|
||||
@@ -400,6 +428,7 @@
|
||||
240525450DF9ABA0F332B52E /* [CP] Copy Pods Resources */,
|
||||
65A7F34B255687E600699170 /* Embed App Extensions */,
|
||||
48C834D962D612F18A0388B3 /* [CP] Embed Pods Frameworks */,
|
||||
651F09DC2E0D262C00495DED /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -478,6 +507,7 @@
|
||||
65B5013E25A672B200E2D264 /* Resources */,
|
||||
658D06A625A7446E008C70C0 /* Run Script */,
|
||||
E81EC4451F76606456BCA61F /* [CP] Copy Pods Resources */,
|
||||
651F09DF2E0D263300495DED /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -653,15 +683,25 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook/Pods-Notesnook-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@@ -810,15 +850,25 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook-NotesnookTests/Pods-Notesnook-NotesnookTests-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@@ -854,11 +904,19 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Make Note/Pods-Make Note-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@@ -1063,7 +1121,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2137;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1137,7 +1195,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.3;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1168,7 +1226,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2137;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1242,7 +1300,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.3;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1401,7 +1459,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2137;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1413,7 +1471,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1444,7 +1502,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2137;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1457,7 +1515,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.3;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1487,7 +1545,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2137;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1561,7 +1619,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1592,7 +1650,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2137;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1667,7 +1725,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.3;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1744,12 +1802,10 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
USE_HERMES = true;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1811,10 +1867,7 @@
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import "RNShortcuts.h"
|
||||
#import "RNBootSplash.h"
|
||||
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
self.dependencyProvider = [RCTAppDependencyProvider new];
|
||||
self.moduleName = @"Notesnook";
|
||||
// You can add your custom initial props in the dictionary below.
|
||||
// They will be passed down to the ViewController used by React Native.
|
||||
|
||||
@@ -114,7 +114,7 @@ post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
if target.name == "react-native-quick-sqlite" then
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = 'SQLITE_CORE=1 SQLITE_ENABLE_FTS5=1 SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT=1 HAVE_CIPHER_AES_128_CBC=0 HAVE_CIPHER_AES_256_CBC=0 HAVE_CIPHER_SQLCIPHER=0 HAVE_CIPHER_RC4=0 HAVE_CIPHER_CHACHA20=1 SQLITE_OMIT_PROGRESS_CALLBACK=1 SQLITE_MAX_EXPR_DEPTH=0 SQLITE_OMIT_DEPRECATED=1 SQLITE_LIKE_DOESNT_MATCH_BLOBS=1 SQLITE_DQS=0 SQLITE_DEFAULT_MEMSTATUS=0 SQLITE_USE_ALLOCA=1'
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = 'SQLITE_CORE=1 SQLITE_ENABLE_FTS5=1 SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT=1 HAVE_CIPHER_AES_128_CBC=0 HAVE_CIPHER_AES_256_CBC=0 HAVE_CIPHER_SQLCIPHER=0 HAVE_CIPHER_RC4=0 HAVE_CIPHER_CHACHA20=1 SQLITE_OMIT_PROGRESS_CALLBACK=1 SQLITE_MAX_EXPR_DEPTH=0 SQLITE_OMIT_DEPRECATED=1 SQLITE_LIKE_DOESNT_MATCH_BLOBS=1 SQLITE_DQS=0 SQLITE_DEFAULT_MEMSTATUS=0 SQLITE_USE_ALLOCA=1 SQLITE_ENABLE_REGEXP=1'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
44
apps/mobile/native/ios/fts5-html.xcframework/Info.plist
Normal file
44
apps/mobile/native/ios/fts5-html.xcframework/Info.plist
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>fts5-html.dylib</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>fts5-html.dylib</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>fts5-html.dylib</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>fts5-html.dylib</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
Binary file not shown.
@@ -5,13 +5,13 @@
|
||||
"main": "index.js",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"overrides": {
|
||||
"react-native": "0.74.5",
|
||||
"react-native": "0.77.2",
|
||||
"react": "18.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ammarahmed/notifee-react-native": "7.4.7",
|
||||
"@callstack/repack": "~5.0.0",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
"dependencies": {
|
||||
"@ammarahmed/notifee-react-native": "7.4.9",
|
||||
"@callstack/repack": "~5.1.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.11",
|
||||
"@react-native-clipboard/clipboard": "^1.9.0",
|
||||
"@react-native-community/checkbox": "^0.5.8",
|
||||
"@react-native-community/netinfo": "^9.3.7",
|
||||
@@ -20,9 +20,9 @@
|
||||
"@react-navigation/elements": "^1.3.3",
|
||||
"@react-navigation/native": "^6.0.10",
|
||||
"@react-navigation/native-stack": "6.6.2",
|
||||
"@sayem314/react-native-keep-awake": "^1.0.4",
|
||||
"@sayem314/react-native-keep-awake": "^1.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5",
|
||||
"react-native": "0.77.2",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-background-actions": "^2.6.6",
|
||||
"react-native-blob-util": "^0.18.3",
|
||||
@@ -33,31 +33,32 @@
|
||||
"react-native-document-picker": "^7.1.1",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"react-native-file-viewer": "^2.1.1",
|
||||
"react-native-gesture-handler": "^2.18.0",
|
||||
"react-native-reanimated": "3.14.0",
|
||||
"react-native-safe-area-context": "^4.10.8",
|
||||
"react-native-screens": "^3.33.0",
|
||||
"react-native-webview": "^13.10.0",
|
||||
"@shopify/flash-list": "1.7.0",
|
||||
"react-native-gesture-handler": "^2.25.0",
|
||||
"react-native-reanimated": "3.17.5",
|
||||
"react-native-safe-area-context": "^5.4.0",
|
||||
"react-native-screens": "^4.10.0",
|
||||
"react-native-vector-icons": "9.2.0",
|
||||
"react-native-get-random-values": "^1.7.0",
|
||||
"react-native-gzip": "1.1.0",
|
||||
"react-native-html-to-pdf-lite": "^0.9.1",
|
||||
"react-native-iap": "12.15.1",
|
||||
"react-native-iap": "12.16.2",
|
||||
"react-native-in-app-review": "4.3.3",
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-pdf": "6.6.2",
|
||||
"@shopify/flash-list": "^1.8.0",
|
||||
"react-native-mmkv-storage": "^0.11.2",
|
||||
"react-native-quick-sqlite": "^8.2.7",
|
||||
"react-native-svg": "^15.12.0",
|
||||
"react-native-webview": "^13.13.5",
|
||||
"react-native-pdf": "6.7.7",
|
||||
"react-native-scoped-storage": "^1.9.5",
|
||||
"react-native-securerandom": "^1.0.1",
|
||||
"react-native-share": "^12.0.3",
|
||||
"react-native-svg": "^12.3.0",
|
||||
"react-native-swiper-flatlist": "3.2.2",
|
||||
"react-native-tooltips": "^1.0.3",
|
||||
"react-native-zip-archive": "6.0.9",
|
||||
"react-native-quick-sqlite": "^8.0.6",
|
||||
"react-native-theme-switch-animation": "^0.6.0",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"react-native-image-crop-picker": "^0.40.2",
|
||||
@@ -65,8 +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.1",
|
||||
"react-native-mmkv-storage": "^0.10.2",
|
||||
"@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",
|
||||
@@ -78,19 +78,26 @@
|
||||
"react-native-orientation-locker": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rspack/core": "^1.2.7",
|
||||
"@babel/core": "^7.27.1",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@react-native-community/cli": "15.0.1",
|
||||
"@rspack/core": "^1.3.15",
|
||||
"@react-native-community/cli-platform-android": "15.0.1",
|
||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||
"@react-native/babel-preset": "0.77.2",
|
||||
"@react-native/eslint-config": "0.77.2",
|
||||
"@react-native/metro-config": "0.77.2",
|
||||
"@react-native/typescript-config": "0.77.2",
|
||||
"@types/jest": "^29.5.13",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"@babel/plugin-transform-unicode-property-regex": "^7.25.9",
|
||||
"@callstack/repack-plugin-reanimated": "^5.0.0",
|
||||
"@callstack/repack-plugin-reanimated": "^5.1.2",
|
||||
"babel-plugin-syntax-hermes-parser": "^0.26.0",
|
||||
"@swc/helpers": "^0.5.15",
|
||||
"detox": "^20.27.6",
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/runtime": "^7.20.0",
|
||||
"@react-native/eslint-config": "0.74.86",
|
||||
"@react-native/metro-config": "0.74.86",
|
||||
"@tsconfig/react-native": "^3.0.2",
|
||||
"@types/html-to-text": "^8.0.1",
|
||||
"@types/metro-config": "^0.76.3",
|
||||
@@ -98,22 +105,20 @@
|
||||
"@types/react-native": "^0.69.1",
|
||||
"@types/react-native-vector-icons": "^6.4.10",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||
"@typescript-eslint/parser": "^5.14.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.1",
|
||||
"@typescript-eslint/parser": "^8.32.1",
|
||||
"babel-jest": "^29.6.3",
|
||||
"eslint": "^8.19.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"@react-native/babel-preset": "0.74.86",
|
||||
"prettier": "2.8.8",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-prettier": "^5.4.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-native": "^5.0.0",
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
"prettier": "3.5.3",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-bundle-visualizer": "^3.1.1",
|
||||
"react-native-cli-bump-version": "^1.3.0",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"metro-react-native-babel-preset": "0.77.0",
|
||||
"acorn-import-attributes": "1.9.5",
|
||||
"expect": "^29.7.0",
|
||||
@@ -122,7 +127,6 @@
|
||||
"jest-image-snapshot": "^6.4.0",
|
||||
"ts-jest": "^29.2.5",
|
||||
"pixelmatch": "^6.0.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@messageformat/parser": "^5.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user