mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 02:58:34 +02:00
Compare commits
244 Commits
fix-editor
...
3.2.7-andr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c8b92cc4e | ||
|
|
31dd1619f6 | ||
|
|
afb010e3ae | ||
|
|
ddebdcda43 | ||
|
|
29c69418e9 | ||
|
|
3e565e5195 | ||
|
|
1b352d6162 | ||
|
|
258aa77cdd | ||
|
|
7789bd145c | ||
|
|
c1457cc83f | ||
|
|
8b316d0abf | ||
|
|
def5059ff1 | ||
|
|
e1febfae31 | ||
|
|
82aa506902 | ||
|
|
de1aa40ecf | ||
|
|
53724b437e | ||
|
|
1592620141 | ||
|
|
28ef87a95b | ||
|
|
d4019eea99 | ||
|
|
d13b46cbba | ||
|
|
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 |
@@ -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;
|
||||
@@ -17,12 +17,15 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Dimensions, TextInput, View } from "react-native";
|
||||
import Orientation from "react-native-orientation-locker";
|
||||
import Pdf from "react-native-pdf";
|
||||
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import downloadAttachment from "../../../common/filesystem/download-attachment";
|
||||
import { deleteCacheFileByPath, exists } from "../../../common/filesystem/io";
|
||||
import { cacheDir } from "../../../common/filesystem/utils";
|
||||
import { useAttachmentProgress } from "../../../hooks/use-attachment-progress";
|
||||
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
|
||||
@@ -30,8 +33,9 @@ import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../../services/event-manager";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import { Dialog } from "../../dialog";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
@@ -39,11 +43,6 @@ import SheetProvider from "../../sheet-provider";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { ProgressBarComponent } from "../../ui/svg/lazy";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import { deleteCacheFileByPath } from "../../../common/filesystem/io";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
const WIN_WIDTH = Dimensions.get("window").width;
|
||||
const WIN_HEIGHT = Dimensions.get("window").height;
|
||||
@@ -105,15 +104,19 @@ const PDFPreview = () => {
|
||||
const open = useCallback(
|
||||
async (attachment) => {
|
||||
setVisible(true);
|
||||
setLoading(true);
|
||||
setTimeout(async () => {
|
||||
setAttachment(attachment);
|
||||
let hash = attachment.hash;
|
||||
if (!hash) return;
|
||||
if (!(await exists(hash))) setLoading(true);
|
||||
const uri = await downloadAttachment(hash, false, {
|
||||
silent: true,
|
||||
cache: true
|
||||
});
|
||||
if (!(await exists(hash))) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
const path = `${cacheDir}/${uri}`;
|
||||
snapshotValue.current = snapshot.current;
|
||||
setPDFSource("file://" + path);
|
||||
@@ -166,8 +169,7 @@ const PDFPreview = () => {
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Animated.View
|
||||
exiting={FadeOut}
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
@@ -188,7 +190,7 @@ const PDFPreview = () => {
|
||||
>
|
||||
{strings.loadingWithProgress(progress?.percent)}
|
||||
</Paragraph>
|
||||
</Animated.View>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<View
|
||||
@@ -270,49 +272,42 @@ const PDFPreview = () => {
|
||||
</View>
|
||||
</View>
|
||||
{pdfSource ? (
|
||||
<Animated.View
|
||||
style={{
|
||||
flex: 1
|
||||
<Pdf
|
||||
source={{
|
||||
uri: pdfSource
|
||||
}}
|
||||
entering={FadeIn}
|
||||
>
|
||||
<Pdf
|
||||
source={{
|
||||
uri: pdfSource
|
||||
}}
|
||||
ref={pdfRef}
|
||||
onLoadComplete={(numberOfPages) => {
|
||||
setNumPages(numberOfPages);
|
||||
}}
|
||||
onPageChanged={(page) => {
|
||||
setCurrentPage(page);
|
||||
inputRef.current?.setNativeProps({
|
||||
text: page + ""
|
||||
});
|
||||
saveSnapshot({
|
||||
currentPage: page,
|
||||
scale: snapshot?.current?.scale
|
||||
});
|
||||
}}
|
||||
// scale={snapshotValue.current?.scale}
|
||||
// onScaleChanged={(scale) => {
|
||||
// saveSnapshot({
|
||||
// currentPage: snapshot?.current?.currentPage,
|
||||
// scale: scale
|
||||
// });
|
||||
// }}
|
||||
page={snapshotValue?.current?.currentPage}
|
||||
password={password}
|
||||
maxScale={6}
|
||||
onError={onError}
|
||||
onPressLink={(uri) => {}}
|
||||
style={{
|
||||
flex: 1,
|
||||
width: width,
|
||||
height: Dimensions.get("window").height
|
||||
}}
|
||||
/>
|
||||
</Animated.View>
|
||||
ref={pdfRef}
|
||||
onLoadComplete={(numberOfPages) => {
|
||||
setNumPages(numberOfPages);
|
||||
}}
|
||||
onPageChanged={(page) => {
|
||||
setCurrentPage(page);
|
||||
inputRef.current?.setNativeProps({
|
||||
text: page + ""
|
||||
});
|
||||
saveSnapshot({
|
||||
currentPage: page,
|
||||
scale: snapshot?.current?.scale
|
||||
});
|
||||
}}
|
||||
// scale={snapshotValue.current?.scale}
|
||||
// onScaleChanged={(scale) => {
|
||||
// saveSnapshot({
|
||||
// currentPage: snapshot?.current?.currentPage,
|
||||
// scale: scale
|
||||
// });
|
||||
// }}
|
||||
page={snapshotValue?.current?.currentPage}
|
||||
password={password}
|
||||
maxScale={6}
|
||||
onError={onError}
|
||||
onPressLink={(uri) => {}}
|
||||
style={{
|
||||
flex: 1,
|
||||
width: width,
|
||||
height: Dimensions.get("window").height
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -40,6 +40,7 @@ import Sync from "../../../services/sync";
|
||||
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import { logoutUser } from "../../../screens/settings/logout";
|
||||
import { sleep } from "../../../utils/time";
|
||||
export const UserSheet = () => {
|
||||
const ref = useSheetRef();
|
||||
const { colors } = useThemeColors();
|
||||
@@ -310,10 +311,11 @@ export const UserSheet = () => {
|
||||
{
|
||||
icon: "logout",
|
||||
title: strings.logout(),
|
||||
onPress: () => {
|
||||
onPress: async () => {
|
||||
console.log("logout");
|
||||
logoutUser();
|
||||
ref.current?.hide();
|
||||
await sleep(300);
|
||||
logoutUser();
|
||||
},
|
||||
hidden: !user
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -530,17 +530,11 @@ const onChangeTab = async (event: { i: number; from: number }) => {
|
||||
activateKeepAwake();
|
||||
eSendEvent(eOnEnterEditor);
|
||||
|
||||
if (!useTabStore.getState().getCurrentNoteId()) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
newNote: true
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
useTabStore.getState().getTab(useTabStore.getState().currentTab)
|
||||
?.session?.locked
|
||||
) {
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
if (
|
||||
useTabStore.getState().getTab(useTabStore.getState().currentTab)?.session
|
||||
?.locked
|
||||
) {
|
||||
eSendEvent(eUnlockNote);
|
||||
}
|
||||
} else {
|
||||
if (event.from === 2) {
|
||||
|
||||
@@ -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;
|
||||
@@ -736,6 +755,8 @@ export const useEditor = (
|
||||
? (data as ContentItem).noteId
|
||||
: data.id;
|
||||
|
||||
if (!useTabStore.getState().hasTabForNote(noteId)) return;
|
||||
|
||||
const note = data.type === "note" ? data : await db.notes?.note(noteId);
|
||||
lock.current = true;
|
||||
// Handle this case where note was locked on another device and synced.
|
||||
|
||||
@@ -98,7 +98,7 @@ class TabSessionStorage {
|
||||
|
||||
static get(id: string): TabSessionItem | null {
|
||||
if (!id) return null;
|
||||
return TabSessionStorage.storage.getMap(id);
|
||||
return TabSessionStorage.storage.getMap(id) || null;
|
||||
}
|
||||
|
||||
static set(id: string, session: TabSessionItem): void {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -113,7 +113,10 @@ async function onBackgroundSyncStarted() {
|
||||
useUserStore.getState().setSyncing(false);
|
||||
}
|
||||
await Notifications.setupReminders();
|
||||
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(false);
|
||||
}
|
||||
Notifications.restorePinnedNotes();
|
||||
NotePreviewWidget.updateNotes();
|
||||
deleteDCacheFiles();
|
||||
DatabaseLogger.info("BACKGROUND SYNC COMPLETE");
|
||||
@@ -142,6 +145,7 @@ const onBoot = async () => {
|
||||
Notifications.pinQuickNote(false);
|
||||
}
|
||||
Notifications.restorePinnedNotes();
|
||||
NotePreviewWidget.updateNotes();
|
||||
DatabaseLogger.info("BOOT TASK COMPLETE");
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e as Error);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -348,7 +348,7 @@ async function scheduleNotification(
|
||||
title: title,
|
||||
message: description || "",
|
||||
ongoing: true,
|
||||
subtitle: description || "",
|
||||
// subtitle: description || "",
|
||||
actions: [strings.unpin()]
|
||||
});
|
||||
}
|
||||
@@ -396,7 +396,7 @@ async function scheduleNotification(
|
||||
payload: payload || "",
|
||||
dateModified: reminder.dateModified + ""
|
||||
},
|
||||
subtitle: !description ? undefined : description,
|
||||
// subtitle: !description ? undefined : description,
|
||||
android: {
|
||||
channelId: await getChannelId(priority),
|
||||
smallIcon: "ic_stat_name",
|
||||
|
||||
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 3062
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,67 +11,69 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" tools:node="remove" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
tools:node="remove" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
tools:node="remove" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission
|
||||
android:name="android.permission.USE_FULL_SCREEN_INTENT"
|
||||
tools:node="remove" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="text/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="image/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="text/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="image/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
|
||||
<data android:mimeType="video/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="audio/*" />
|
||||
</intent>
|
||||
</queries>
|
||||
<data android:mimeType="video/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
|
||||
<data android:mimeType="audio/*" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/BootTheme"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
tools:replace="android:supportsRtl">
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/BootTheme">
|
||||
|
||||
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_reminders_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
|
||||
<receiver
|
||||
android:name=".NoteWidget"
|
||||
android:exported="false"
|
||||
android:label="@string/quick_note">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
@@ -81,7 +83,10 @@
|
||||
android:resource="@xml/new_note_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver android:exported="false" android:label="@string/note" android:name=".NotePreviewWidget">
|
||||
<receiver
|
||||
android:name=".NotePreviewWidget"
|
||||
android:exported="false"
|
||||
android:label="@string/note">
|
||||
<intent-filter>"
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
@@ -91,33 +96,49 @@
|
||||
android:resource="@xml/note_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
<receiver
|
||||
android:name=".ReminderWidgetProvider"
|
||||
android:exported="false"
|
||||
android:label="@string/reminders_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_reminders_info" />
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".NotePreviewConfigureActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:exported="true"
|
||||
android:label="NotePreviewConfigure"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustResize" android:name=".NotePreviewConfigureActivity">
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:resizeableActivity="true"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="Notesnook">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
@@ -128,6 +149,7 @@
|
||||
|
||||
<intent-filter android:label="Notesnook">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
@@ -135,22 +157,25 @@
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
<activity android:exported="false" android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
<activity
|
||||
android:name="com.facebook.react.devsupport.DevSettingsActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ShareActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:label="@string/title_activity_share"
|
||||
android:noHistory="true"
|
||||
android:screenOrientation="unspecified"
|
||||
android:exported="true"
|
||||
android:taskAffinity=""
|
||||
android:excludeFromRecents="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/AppThemeB">
|
||||
android:theme="@style/AppThemeB"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<intent-filter android:label="Make Note">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="text/*" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
@@ -161,6 +186,7 @@
|
||||
<intent-filter android:label="Make Note">
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="text/*" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
@@ -180,35 +206,41 @@
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:stopWithTask="false" />
|
||||
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
|
||||
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
|
||||
|
||||
<service android:name="com.streetwriters.notesnook.BootTaskService" android:foregroundServiceType="dataSync" />
|
||||
<service
|
||||
android:name="com.streetwriters.notesnook.BootTaskService"
|
||||
android:foregroundServiceType="specialUse">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="The service is required by the app to restore notifications of pinned notes, restore notification with reply input for creating notes and restore data in note preview widgets on device reboot." />
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".NotesnookTileService"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/add_note"
|
||||
android:label="New note"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".ReminderViewsService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<service
|
||||
android:name=".ReminderViewsService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_viewer_provider_paths" />
|
||||
</provider>
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_viewer_provider_paths" />
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="com.vinzscam.reactnativefileviewer.FileProvider"
|
||||
@@ -220,12 +252,14 @@
|
||||
android:resource="@xml/file_viewer_provider_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver android:exported="true" android:name=".BootRecieverService">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".BootRecieverService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -39,7 +39,6 @@ public class BootTaskService extends HeadlessJsTaskService {
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel("com.streetwriters.notesnook",
|
||||
"Default",
|
||||
@@ -55,7 +54,7 @@ public class BootTaskService extends HeadlessJsTaskService {
|
||||
this.startForeground(
|
||||
1,
|
||||
notification,
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
|
||||
} else {
|
||||
this.startForeground(
|
||||
1,
|
||||
|
||||
@@ -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 crash on device boot on android 15 devices
|
||||
- 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',
|
||||
|
||||
@@ -124,7 +124,6 @@
|
||||
6529D2B0257B4A2900B49BC3 /* NotesnookDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NotesnookDebug.entitlements; path = Notesnook/NotesnookDebug.entitlements; sourceTree = "<group>"; };
|
||||
656835802BB29A8300144BAB /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Italic.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Italic.ttf"; sourceTree = "<group>"; };
|
||||
6593E4A2281C345400492C50 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Notesnook/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = BetterTrigram.xcframework; sourceTree = "<group>"; };
|
||||
659BE46625E11A5100E05671 /* notesnook-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "notesnook-text.png"; path = "Notesnook/Images.xcassets/notesnook-text.png"; sourceTree = "<group>"; };
|
||||
65A7F34F255689AD00699170 /* Notesnook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Notesnook.entitlements; path = Notesnook/Notesnook.entitlements; sourceTree = "<group>"; };
|
||||
65AA857725E6DDEC00772A01 /* NotesWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotesWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -256,7 +255,6 @@
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */,
|
||||
6515C42E2580AA2F00E83E39 /* StoreKit.framework */,
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
|
||||
@@ -653,15 +651,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 +818,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 +872,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 +1089,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2141;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1137,7 +1163,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.6;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1168,7 +1194,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2141;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1242,7 +1268,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.6;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1401,7 +1427,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2141;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1413,7 +1439,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.6;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1444,7 +1470,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2141;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1457,7 +1483,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.2.6;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1487,10 +1513,16 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2141;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal\"",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built\"",
|
||||
"\"${PROJECT_DIR}\"",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -1561,7 +1593,8 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.2.6;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1592,11 +1625,17 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2141;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal\"",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built\"",
|
||||
"\"${PROJECT_DIR}\"",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -1667,7 +1706,8 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.2.6;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1750,6 +1790,7 @@
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
USE_HERMES = true;
|
||||
};
|
||||
name = Debug;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user