Compare commits

...

155 Commits

Author SHA1 Message Date
Ammar Ahmed
b6502ce7cc mobile: fix readonly editor not loading 2024-11-20 12:35:56 +05:00
Abdullah Atta
97a75d9498 ci: generate & publish flatpak sources during desktop release 2024-11-20 11:51:04 +05:00
Abdullah Atta
950028e3b4 web: bump version to v3.0.21 2024-11-20 11:26:23 +05:00
Abdullah Atta
874726c712 global: update lockfiles 2024-11-20 11:25:28 +05:00
Abdullah Atta
0f380b9962 Revert "editor: fix heading and p gap (#6530)"
This reverts commit c26988134f.
2024-11-20 11:21:48 +05:00
Abdullah Atta
2ce569726c editor: fix image on load animation 2024-11-20 11:17:25 +05:00
Abdullah Atta
a38fe7da43 web: fix editor margins state reset on restart 2024-11-20 11:09:45 +05:00
Abdullah Atta
057b563608 web: fix password app lock stuck on "wrong password" 2024-11-20 11:07:05 +05:00
Ammar Ahmed
3741e26219 mobile: fix error when changing 2fa method 2024-11-20 10:25:11 +05:00
Ammar Ahmed
86bf8f68bf ci: build ios app on latest mac runner 2024-11-19 19:28:45 +05:00
Ammar Ahmed
e7f67d1bb9 mobile: do not show migration on first launch 2024-11-19 18:52:27 +05:00
Ammar Ahmed
9c28e6bb93 mobile: fix build errors 2024-11-19 18:38:29 +05:00
Abdullah Atta
0f84fab810 mobile: only show migration progress for main db 2024-11-19 17:01:44 +05:00
Abdullah Atta
228a8e612f web: only show migration progress for main db 2024-11-19 17:01:33 +05:00
Abdullah Atta
175c6cdba0 core: send db name with migration events 2024-11-19 17:01:24 +05:00
Abdullah Atta
d3e2fcc3ac web: fix app stuck on splash screen after logout 2024-11-19 16:35:25 +05:00
Abdullah Atta
dafc2de2b3 ci: run desktop macos x64 tests on macos-13 runner 2024-11-19 16:00:08 +05:00
Abdullah Atta
f9eb9a1ad9 desktop: add test timeout and force exit 2024-11-19 15:52:27 +05:00
Abdullah Atta
1f5001210b ci: build electron wrapper after npm i 2024-11-19 15:52:17 +05:00
Abdullah Atta
3150eafb89 core: workaround for tokens shorter than 3 chars 2024-11-19 15:45:40 +05:00
Abdullah Atta
ad1e3f2c67 ci: Install sqlite-better-trigram for all arch 2024-11-19 15:16:21 +05:00
Abdullah Atta
d67b78d86e desktop: remove sqlite-better-trigram installation from build script 2024-11-19 12:21:44 +05:00
Abdullah Atta
25f74ff04d desktop: always install both arm64 and x64 packages on all platforms 2024-11-19 12:10:52 +05:00
Abdullah Atta
be7c1453fe desktop: force install both x64 & arm64 packages for sqlite-better-trigram on macos 2024-11-19 12:00:21 +05:00
Abdullah Atta
daa60939c3 desktop: take app screenshot if test fails 2024-11-19 11:55:37 +05:00
Abdullah Atta
ac659b05e4 web: ignore network error 2024-11-19 11:49:31 +05:00
Abdullah Atta
8391d51e02 ci: run desktop tests on macos x64 2024-11-19 11:09:37 +05:00
01zulfi
ad78b3a931 editor: fix backspace deleting formatting in list item (#6712) (#6878)
* fix hitting backspace inside the second (or next) p in list item deleted the formatting of the entire list item

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-19 10:30:13 +05:00
Abdullah Atta
e3a1fdd87d ci: simplify macos desktop test step 2024-11-18 23:43:44 +05:00
Abdullah Atta
12f24db3af desktop: add tests (#6900)
* desktop: add tests

* ci: fix install packages step

* ci: set NOTESNOOK_STAGING env

* ci: fix working dir

* ci: use cross-env on windows

* desktop: fix tests

* ci: use `xvfb-run` on linux

* ci: install playwright deps

* ci: remove DEBUG=pw*

* ci: no need to install playwright deps
2024-11-18 23:38:41 +05:00
Abdullah Atta
2af805017c web: bump version to v3.0.20 2024-11-18 20:01:20 +05:00
Abdullah Atta
97d4ca0afe desktop: install and load sqlite-better-trigram module 2024-11-18 19:03:57 +05:00
Ammar Ahmed
8b5c246531 mobile: release 3.0.21 2024-11-18 18:39:36 +05:00
Ammar Ahmed
8f2113b155 mobile: show migration progress 2024-11-18 13:45:52 +05:00
Abdullah Atta
a378bd397b web: show status when db is migrating on startup 2024-11-18 13:45:52 +05:00
Abdullah Atta
a315473ea4 core: add migration start/finished events 2024-11-18 13:45:52 +05:00
Ammar Ahmed
7dc581b9f3 mobile: copy support email automatically 2024-11-18 12:42:22 +05:00
alihamuh
5ef392ca31 web: add copy button for support email in settings 2024-11-18 12:42:22 +05:00
Abdullah Atta
846c949cd1 desktop: handle Error while decrypting the ciphertext provided to safeStorage.decryptString. error 2024-11-18 12:16:58 +05:00
Abdullah Atta
d88ead4609 web: fix dialogs not closing when app gets locked 2024-11-18 11:51:29 +05:00
Abdullah Atta
055c109563 web: fix crash immediately after unlocking app 2024-11-18 11:51:09 +05:00
dyw770
378a7197c2 monograph: fix code block copy (#6891)
Signed-off-by: dyw770 <dyw770@qq.com>
2024-11-18 11:31:45 +05:00
Abdullah Atta
89ee0f8d27 core: fix sync tests and remove obsolete snapshots 2024-11-16 15:16:18 +05:00
Abdullah Atta
c9ea9b1d4e web: fix app note starting due to outdated sqlite wasm libs 2024-11-16 14:16:01 +05:00
Ammar Ahmed
a91532839f mobile: add better-trigram extension 2024-11-16 14:16:01 +05:00
Abdullah Atta
b32f6b2460 core: remove special handling for queries < 3 in length 2024-11-16 14:16:01 +05:00
Abdullah Atta
e02970b936 web: install better trigram sqlite extension 2024-11-16 14:16:01 +05:00
Abdullah Atta
1f24fd4758 core: use better_trigram for improved content tokenization 2024-11-16 14:16:01 +05:00
Abdullah Atta
5a16403a13 web: fix Could not find a provider port to communicate with. error 2024-11-16 07:42:37 +05:00
01zulfi
48bbaad1b8 editor: fix markdown link pasting (#6639) (#6866)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-16 06:41:22 +05:00
Ammar Ahmed
520711eec9 mobile: match height with mobile action sheet 2024-11-15 16:22:29 +05:00
Abdullah Atta
3f82a4ebd4 core: fix using wrong encryption key shows incorrect password error 2024-11-15 16:02:55 +05:00
Abdullah Atta
4feb22fc12 web: open empty note by default on startup 2024-11-15 16:02:55 +05:00
Abdullah Atta
4e0e8dbb66 web: even faster startup 2024-11-15 16:02:55 +05:00
Ammar Ahmed
431c4eae14 mobile: fix localization issues 2024-11-15 15:54:20 +05:00
Ammar Ahmed
6373cbc9a4 mobile: fix core api usage 2024-11-15 15:54:20 +05:00
Ammar Ahmed
bbf3477512 editor: fix action-sheet not clickable 2024-11-15 15:54:19 +05:00
01zulfi
ca30ac86d7 web: fix close tab opening again
* introduced in #6817

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-15 15:47:39 +05:00
Abdullah Atta
88aecb3af2 setup: use --moduleResolution Bundler when running esm build 2024-11-15 12:31:56 +05:00
Abdullah Atta
c5a78b0293 global: update lockfiles 2024-11-15 12:31:56 +05:00
Abdullah Atta
b0bc1ec4a6 web: fix FOUC on startup 2024-11-15 12:31:56 +05:00
Abdullah Atta
84b3892053 desktop: update electron to v31 2024-11-15 12:31:56 +05:00
Abdullah Atta
d985683c0f desktop: fix Electron failed to install correctly error 2024-11-15 12:31:55 +05:00
Ammar Ahmed
008ce9222e mobile: fix warning 2024-11-15 12:00:41 +05:00
Ammar Ahmed
c360fa8c9b mobile: fix build 2024-11-15 11:56:46 +05:00
Abdullah Atta
c1afd4692d desktop: disable shrinking for titlebar 2024-11-15 10:43:01 +05:00
Abdullah Atta
7748cf453c desktop: fix node version is different error on startup 2024-11-15 10:42:17 +05:00
Abdullah Atta
34d0123351 ci: upgrade all actions to actions/checkout@v4 2024-11-13 18:24:14 +05:00
Abdullah Atta
af1cfc92c2 web: use unique note during sync tests 2024-11-13 18:13:16 +05:00
Abdullah Atta
59e06ef938 core: fix sync not running on app startup 2024-11-13 18:12:54 +05:00
Abdullah Atta
9d12ef82c0 web: do not prefetch wa-sqlite wasm module on desktop 2024-11-13 17:52:40 +05:00
Abdullah Atta
0a9bfd4b69 web: get rid of mitata; run benchmarks directly 2024-11-13 17:29:07 +05:00
Abdullah Atta
329248ebbd web: reduce pbkdf2 db key generator iterations to 100k
this is for performance reasons mainly. 100k should still be super secure.
2024-11-13 17:29:07 +05:00
Ammar Ahmed
cd51f2f2c9 ci: fix tests 2024-11-13 15:53:14 +05:00
Abdullah Atta
a3e7d5bd84 ci: get rid of codspeed 2024-11-13 15:48:33 +05:00
Abdullah Atta
6a261d284f web: update @playwright/test to v1.48.2 2024-11-13 15:48:33 +05:00
Abdullah Atta
eddf9f6d04 web: kill server after running benchmarks 2024-11-13 15:48:33 +05:00
Abdullah Atta
33e0f81988 ci: install playwright chromium before running benchmark 2024-11-13 15:48:33 +05:00
Abdullah Atta
0df8f80b48 ci: add benchmark workflow for @notesnook/web 2024-11-13 15:48:33 +05:00
Abdullah Atta
2f4590fbb6 web: add benchmarks 2024-11-13 15:48:33 +05:00
Abdullah Atta
a011857286 web: more fixes to new notebooks tree 2024-11-13 15:48:33 +05:00
Abdullah Atta
cc9e7e10b6 web: fix tests 2024-11-13 15:48:33 +05:00
Abdullah Atta
78e04bfac5 global: update lockfiles 2024-11-13 15:48:33 +05:00
Abdullah Atta
652231d5fd editor(mobile): fix editor not loading in dev 2024-11-13 15:48:33 +05:00
Abdullah Atta
343920d376 editor(mobile): fix ui for new custom action sheet 2024-11-13 15:48:33 +05:00
Abdullah Atta
1f3e7574f5 web: improve app startup 2024-11-13 15:48:33 +05:00
Abdullah Atta
ea9b10ff23 web: get rid of text-scramble 2024-11-13 15:48:33 +05:00
Abdullah Atta
232a347f4a web: lazy load hash-wasm 2024-11-13 15:48:33 +05:00
Abdullah Atta
00053cb99d web: get rid of web-streams-polyfill 2024-11-13 15:48:33 +05:00
Abdullah Atta
03a2a2f9cc web: lazily load zip/unzip streams 2024-11-13 15:48:33 +05:00
Abdullah Atta
09c8c66841 web: @notesnook/core related changes 2024-11-13 15:48:33 +05:00
Abdullah Atta
53e4bda69d web: stop lazy loading fs
we really should split it up so its easier to partially import
2024-11-13 15:48:33 +05:00
Abdullah Atta
4c77c8e22c web: get rid of react-complex-tree
build a custom implementation using react-virtuouso
2024-11-13 15:48:33 +05:00
Abdullah Atta
9afb4a52c2 web: get rid of tinycolor2 2024-11-13 15:48:33 +05:00
Abdullah Atta
b3d6fd7f5b web: import svg assets as url to exclude them from main bundle 2024-11-13 15:48:33 +05:00
Abdullah Atta
245d022f58 web: prefetch sqlite wasm module 2024-11-13 15:48:33 +05:00
Abdullah Atta
2d226db271 web: replace react-day-picker with custom built day picker
it uses `@rehookify/datepicker`
2024-11-13 15:48:33 +05:00
Abdullah Atta
9281f9a390 web: get rid of framer-motion 2024-11-13 15:48:33 +05:00
Abdullah Atta
d30bb56e75 web: remove unused modules & components 2024-11-13 15:48:33 +05:00
Abdullah Atta
eb92989478 web: remove unused packages 2024-11-13 15:48:33 +05:00
Abdullah Atta
d0dcdcd337 web: replace marked with snarkdown 2024-11-13 15:48:33 +05:00
Abdullah Atta
60aa92ca03 setup: update typescript to 5.6 2024-11-13 15:48:33 +05:00
Abdullah Atta
e2bbc1425b setup: add build script 2024-11-13 15:48:33 +05:00
Abdullah Atta
fc01103353 ui: remove framer-motion 2024-11-13 15:48:33 +05:00
Abdullah Atta
22919561fe theme: remove @types/tinycolor 2024-11-13 15:48:33 +05:00
Abdullah Atta
745cbc082f editor: replace tinycolor2 with colord 2024-11-13 15:48:33 +05:00
Abdullah Atta
6671a97181 editor: get rid of framer-motion 2024-11-13 15:48:33 +05:00
Abdullah Atta
b7aaa382b1 editor: fix postbuild script 2024-11-13 15:48:33 +05:00
Abdullah Atta
ecfa0e2c6b theme: get rid of tinycolor2 2024-11-13 15:48:33 +05:00
Abdullah Atta
58c6cce4e4 common: await getContentFromData 2024-11-13 15:48:33 +05:00
Abdullah Atta
b388a550a5 core: remove unneeded packages 2024-11-13 15:48:33 +05:00
Abdullah Atta
f0c5f6b949 core: lazy init compressor 2024-11-13 15:48:33 +05:00
Abdullah Atta
459cd70c32 core: lazy load signalr 2024-11-13 15:48:33 +05:00
Abdullah Atta
ece142e8d8 core: lazy load content-type 2024-11-13 15:48:33 +05:00
Abdullah Atta
5f1f308540 core: replace mime-db with mime and lazy load it 2024-11-13 15:48:33 +05:00
Abdullah Atta
7437b29518 core: fix type errors in migrations 2024-11-13 15:48:33 +05:00
Abdullah Atta
7acb0a8c0f common: use tsc for builds instead of tsup 2024-11-13 15:48:33 +05:00
Abdullah Atta
a1a24ee865 editor: use tsc for builds instead of tsup 2024-11-13 15:48:33 +05:00
Abdullah Atta
02008ad098 core: use tsc instead of tsup for builds 2024-11-13 15:48:33 +05:00
Abdullah Atta
0a02431f6a web: remove unneeded modules 2024-11-13 15:48:33 +05:00
Ammar Ahmed
9291711036 ci: enable all tests 2024-11-13 11:22:32 +05:00
01zulfi
f3e0fda5fa web: fix button is not defined error
* introduced in #6848

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-13 11:13:17 +05:00
Ammar Ahmed
0e721a4948 mobile: add e2e test workflow 2024-11-13 09:17:11 +05:00
Ammar Ahmed
e0a704d022 mobile: fix e2e tests 2024-11-13 09:17:11 +05:00
Abdullah Atta
e36bc816ea web: fix more failing tests 2024-11-13 09:17:11 +05:00
Abdullah Atta
f8a62ca437 web: fix note & notebook tests 2024-11-13 09:17:11 +05:00
Abdullah Atta
22ecb079b0 intl: update po files 2024-11-13 09:17:11 +05:00
Abdullah Atta
06333cf9aa web: localize more missing strings 2024-11-13 09:17:11 +05:00
Ammar Ahmed
1ad199ce53 mobile: cleanup and fix errors 2024-11-13 09:17:11 +05:00
Abdullah Atta
ceebda5314 web: localize missing strings 2024-11-13 09:17:11 +05:00
Abdullah Atta
e2d33381cc intl: fix type error in vite.config.mts due to duplicate rollup packages 2024-11-13 09:17:11 +05:00
Abdullah Atta
2417f8e233 global: fix many localization related errors and issues 2024-11-13 09:17:11 +05:00
Ammar Ahmed
a240b0b3d7 ci: add e2e workflow 2024-11-11 19:28:08 +05:00
01zulfi
fab085579a web: add text zoom functionality in editor (#6633) (#6846)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-09 12:15:27 +05:00
Abdullah Atta
a5faa4acea web: fix checkout (and make it load faster) 2024-11-08 17:06:17 +05:00
Ammar Ahmed
4014f2a5b5 mobile: support monograph server url 2024-11-08 12:21:07 +05:00
Abdullah Atta
fb62839dad mobile: use mongraph host from core (instead of a static value) 2024-11-08 12:21:07 +05:00
Abdullah Atta
7bf8f1cfc1 web: use mongraph host from core (instead of a static value) 2024-11-08 12:21:07 +05:00
Abdullah Atta
b8a727eee8 monograph: add /api/version endpoint for self hosting support 2024-11-08 12:21:07 +05:00
Abdullah Atta
52e12de296 web: add support for using custom monograph server 2024-11-08 12:21:07 +05:00
Abdullah Atta
0bb2345ccb core: add host for monograph server 2024-11-08 12:21:07 +05:00
Abdullah Atta
78e957eed4 monograph: use public url everywhere instead of monogr.ph 2024-11-08 12:21:07 +05:00
Muhammad Ali
865efaa860 desktop: fix duplicate window controls showing on Linux desktop app (#6844) 2024-11-08 12:16:33 +05:00
01zulfi
2931fd0b3e web: fix copy link button copying link text instead of link (#6642) (#6845)
Signed-off-by: Zulfiqar Ali <85733202+01zulfi@users.noreply.github.com>
2024-11-08 12:13:50 +05:00
Ammar Ahmed
faae68928b mobile: fix localization 2024-11-07 12:23:36 +05:00
01zulfi
21ada0e2d2 web: fix image preview opening as PDF (#6822) (#6843)
Signed-off-by: Zulfiqar Ali <85733202+01zulfi@users.noreply.github.com>
2024-11-07 11:33:57 +05:00
Abdullah Atta
2337b8cd85 web: fix notebook tests 2024-11-06 16:14:39 +05:00
Abdullah Atta
52c8d0b86c desktop: do not use tsc for building desktop app 2024-11-06 16:00:44 +05:00
Abdullah Atta
ce98cea4e7 desktop: update lockfile 2024-11-06 15:42:27 +05:00
Abdullah Atta
b66a093cc2 web: fix missing imports in importer.tsx 2024-11-06 15:38:00 +05:00
Abdullah Atta
a3e5ae4cf5 desktop: use tsup for builds 2024-11-06 15:03:49 +05:00
Abdullah Atta
cddd08c32d desktop: use nodenext module resolution to fix could not resolve errors 2024-11-06 14:51:26 +05:00
Ammar Ahmed
5c3ad96cbe mobile: check item restored successfully 2024-11-06 14:22:30 +05:00
01zulfi
55b441d14e web: fix restore notebooks over limit for free users (#6812)
Signed-off-by: Zulfiqar Ali <85733202+01zulfi@users.noreply.github.com>
2024-11-06 14:22:30 +05:00
Ammar Ahmed
f740a211ce Fix selection scrolls to incorrect position on focus and press enter on mobile (#6838)
* mobile: fix keep-in-view overscrolling

* mobile: fix editor status flickers too much

* mobile: fix setting startingOffset

* mobile: set sticky state if not set already
2024-11-06 13:14:14 +05:00
310 changed files with 16047 additions and 10907 deletions

100
.github/workflows/android.e2e.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: e2e-android
on: workflow_dispatch
jobs:
e2e-android:
runs-on: ubuntu-latest
env:
API_LEVEL: 34
ARCH: x86_64
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: false
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install node modules
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=mobile
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: wrapper
cache-read-only: false
- name: Use specific Java version for the builds
uses: joschi/setup-jdk@v2
with:
java-version: "17"
architecture: "x64"
- name: Install Detox CLI
run: npm install detox-cli --global
- name: Detox build
run: |
yarn build:android
ls apps/mobile/native/android/app/build/outputs/apk
ls apps/mobile/native/android/app/build/outputs/apk/release
- name: Get device name
id: device
run: |
AVD_NAME=$(node -p "require('./apps/mobile/native/.detoxrc.js').devices.emulator.device.avdName")
echo "AVD_NAME=$AVD_NAME" >> $GITHUB_OUTPUT
- name: AVD cache
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: ${{ steps.device.outputs.AVD_NAME }}
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ env.API_LEVEL }}
arch: ${{ env.ARCH }}
avd-name: ${{ steps.device.outputs.AVD_NAME }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: echo "Generated AVD snapshot for caching."
- name: Detox test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ env.API_LEVEL }}
arch: ${{ env.ARCH }}
avd-name: ${{ steps.device.outputs.AVD_NAME }}
disable-animations: true
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
force-avd-creation: false
script: yarn test:android --headless --record-logs failing --record-videos failing --take-screenshots failing
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: detox-artifacts
path: artifacts
retention-days: 14

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache

View File

@@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
@@ -84,6 +84,26 @@ jobs:
files: ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip
prerelease: ${{ endsWith(steps.app_metadata.outputs.app_version, '-beta') }}
- name: Generate flatpak sources
if: inputs.publish-github && inputs.build-linux
run: |
sudo apt-get install -y flatpak flatpak-builder pipx
git clone https://github.com/flatpak/flatpak-builder-tools.git /tmp/flatpak-builder-tools
cd /tmp/flatpak-builder-tools/node
pipx install .
cd -
node scripts/generate-sources.mjs
- name: Upload flatpak sources
uses: softprops/action-gh-release@v1
if: inputs.publish-github && inputs.build-linux
with:
draft: true
name: Notesnook Desktop v${{ steps.app_metadata.outputs.app_version }}
tag_name: v${{ steps.app_metadata.outputs.app_version }}
files: ./generated-sources.json
prerelease: ${{ endsWith(steps.app_metadata.outputs.app_version, '-beta') }}
build-macos:
name: Build for macOS
needs: build
@@ -92,7 +112,7 @@ jobs:
steps:
- name: Check out Git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
@@ -136,6 +156,12 @@ jobs:
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
npm i --cpu x64 sqlite-better-trigram
working-directory: ./apps/desktop
- name: Install provisioning profile
run: echo "${{ secrets.MAC_PROVISIONING_PROFILE }}" | base64 --decode > embedded.provisionprofile
working-directory: ./apps/desktop
@@ -188,7 +214,7 @@ jobs:
steps:
- name: Check out Git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
@@ -212,6 +238,12 @@ jobs:
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
npm i --cpu x64 sqlite-better-trigram
working-directory: ./apps/desktop
- name: Build Electron wrapper
run: npx nx run release --project @notesnook/desktop
working-directory: ./apps/desktop
@@ -247,7 +279,7 @@ jobs:
steps:
- name: Check out Git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v2
@@ -272,6 +304,12 @@ jobs:
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
npm i --cpu x64 sqlite-better-trigram
working-directory: ./apps/desktop
- name: Build
run: npx nx run release --project @notesnook/desktop

233
.github/workflows/desktop.tests.yml vendored Normal file
View File

@@ -0,0 +1,233 @@
name: Test @notesnook/desktop
on:
workflow_dispatch:
push:
branches:
- "master"
paths:
- "app/desktop/**"
# re-run workflow if workflow file changes
- ".github/workflows/desktop.tests.yml"
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=web
- name: Setup environment
run: |
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
- name: Generate desktop build
run: npx nx build:desktop @notesnook/web
- name: Archive build artifact
uses: actions/upload-artifact@v4
with:
name: build
path: apps/web/build/**/*
test-macos-x64:
name: Test macOS x64
needs: build
runs-on: macos-13
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- 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: Build Electron wrapper
run: npx nx run release --project @notesnook/desktop
working-directory: ./apps/desktop
- name: Build app
run: |
yarn electron-builder --mac --dir --x64
working-directory: ./apps/desktop
- name: Run tests x64
run: |
EXECUTABLE_PATH=output/mac/Notesnook.app/Contents/MacOS/Notesnook npm run test
working-directory: ./apps/desktop
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results-macos-x64
path: apps/desktop/test-results
retention-days: 5
test-macos:
name: Test macOS
needs: build
runs-on: macos-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- 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: Build Electron wrapper
run: npx nx run release --project @notesnook/desktop
working-directory: ./apps/desktop
- name: Build app
run: |
yarn electron-builder --mac --dir --arm64
working-directory: ./apps/desktop
- name: Run tests arm64
run: |
EXECUTABLE_PATH=output/mac-arm64/Notesnook.app/Contents/MacOS/Notesnook npm run test
working-directory: ./apps/desktop
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results-macos
path: apps/desktop/test-results
retention-days: 5
test-linux:
name: Test for Linux
needs: build
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- 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
npm i --cpu x64 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 app
run: |
yarn electron-builder --linux --dir --arm64 --x64
working-directory: ./apps/desktop
- name: Run tests
run: |
EXECUTABLE_PATH=output/linux-unpacked/notesnook xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" -- npm run test
working-directory: ./apps/desktop
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results-linux
path: apps/desktop/test-results
retention-days: 5
test-windows:
name: Test for Windows
needs: build
runs-on: windows-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- 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
npm i --cpu x64 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 app
run: |
npx cross-env NOTESNOOK_STAGING=true yarn electron-builder --win --dir --arm64 --x64
working-directory: ./apps/desktop
- name: Run tests
run: |
npx cross-env EXECUTABLE_PATH=output/win-unpacked/Notesnook.exe npm run test
working-directory: ./apps/desktop
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results-win
path: apps/desktop/test-results
retention-days: 5

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache

View File

@@ -1,6 +1,6 @@
name: Publish Notesnook Help
on:
on:
workflow_dispatch:
push:
branches:
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
toolchain: stable

View File

@@ -4,7 +4,7 @@ on: workflow_dispatch
jobs:
build:
runs-on: macos-13
runs-on: macos-latest
timeout-minutes: 60
steps:

View File

@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache

View File

@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache

44
.github/workflows/web.benchmarks.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Benchmark @notesnook/web
on:
workflow_dispatch:
push:
branches:
- "master"
paths:
- "apps/web/**"
# re-run workflow if workflow file changes
- ".github/workflows/web.benchmarks.yml"
pull_request:
jobs:
bench:
name: Bench
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=web
- name: Setup environment
run: |
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
- name: Build
run: npm run build:web
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: apps/web
- name: Run benchmarks
id: benchmark
run: node __bench__/app.bench.mjs
working-directory: apps/web

View File

@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
@@ -53,7 +53,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Download build
uses: actions/download-artifact@v4

View File

@@ -0,0 +1,53 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import test from "node:test";
import { launchApp } from "./utils.mjs";
import assert from "assert";
import slugify from "slugify";
import path from "path";
import { mkdir } from "fs/promises";
test("make sure app loads", async (t) => {
const { app, page } = await launchApp();
try {
await page.waitForSelector("#authForm");
assert.ok(
await page.getByRole("button", { name: "Create account" }).isVisible()
);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await page.waitForSelector(".ProseMirror");
} catch (e) {
await mkdir("test-results", { recursive: true });
await page.screenshot({
path: path.join(
"test-results",
`${slugify(t.name)}-${process.platform}-${process.arch}-error.png`
)
});
throw e;
} finally {
await app.close();
}
});

View File

@@ -17,12 +17,22 @@ 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 { motion } from "framer-motion";
import { Flex, Box, Image, Text } from "@theme-ui/components";
import { Input } from "@theme-ui/components";
import { _electron as electron } from "playwright";
export const AnimatedFlex = motion(Flex);
export const AnimatedBox = motion(Box);
export const AnimatedImage = motion(Image);
export const AnimatedText = motion(Text);
export const AnimatedInput = motion(Input);
const executablePath = process.env.EXECUTABLE_PATH;
// process.platform === "linux"
// ? "output/linux-unpacked/Notesnook"
// : process.platform === "darwin"
// ? "output/mac/Notesnook.app/Contents/MacOS/Notesnook"
// : "output/win-unpacked/Notesnook.exe";
export async function launchApp() {
const app = await electron.launch({
executablePath
});
const page = await app.firstWindow();
return { app, page };
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,11 +2,25 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.0.18",
"version": "3.0.21",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"sideEffects": false,
"exports": {
".": {
"require": {
"types": "./dist/types/index.d.ts",
"default": "./dist/cjs/index.js"
},
"import": {
"types": "./dist/types/index.d.ts",
"default": "./dist/esm/index.js"
}
}
},
"homepage": "https://notesnook.com/",
"repository": "https://github.com/streetwriters/notesnook",
"license": "GPL-3.0-or-later",
@@ -15,10 +29,11 @@
"@notesnook/intl": "file:../../packages/intl",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"better-sqlite3-multiple-ciphers": "11.2.1",
"better-sqlite3-multiple-ciphers": "11.5.0",
"electron-trpc": "0.6.1",
"electron-updater": "^6.3.4",
"icojs": "^0.19.4",
"sqlite-better-trigram": "^0.0.2",
"typed-emitter": "^2.1.0",
"yargs": "^17.7.2",
"zod": "^3.23.8"
@@ -28,14 +43,17 @@
"@types/node": "22.7.0",
"@types/yargs": "^17.0.33",
"chokidar": "^4.0.1",
"electron": "^30.5.1",
"electron-builder": "^25.1.7",
"electron": "^31.7.4",
"electron-builder": "^25.1.8",
"esbuild": "^0.24.0",
"node-abi": "^3.68.0",
"node-gyp-build": "^4.8.2",
"playwright": "^1.48.2",
"prebuildify": "^6.0.1",
"slugify": "^1.6.6",
"tree-kill": "^1.2.2",
"undici": "^6.19.8"
"undici": "^6.19.8",
"vitest": "^2.1.5"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
@@ -44,10 +62,11 @@
"start": "node scripts/dev.mjs",
"staging": "node scripts/build.mjs --run",
"release": "node scripts/build.mjs",
"build": "tsc",
"build": "node ../../scripts/build.mjs",
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --external:better-sqlite3-multiple-ciphers --external:sodium-native --minify --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
"bundle:mas": "esbuild electron=./src/main.ts ./src/preload.ts --minify --external:electron --external:fsevents --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=true --define:RELEASE=true",
"postinstall": "patch-package"
"postinstall": "patch-package",
"test": "node --test --test-timeout 30000 --test-force-exit"
},
"author": {
"name": "Streetwriters (Private) Limited",
@@ -76,7 +95,20 @@
"node_modules/file-uri-to-path",
"node_modules/bindings",
"node_modules/node-gyp-build",
"node_modules/sqlite-better-trigram",
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
{
"from": "node_modules/sqlite-better-trigram-linux-${arch}",
"to": "node_modules/sqlite-better-trigram-linux-${arch}"
},
{
"from": "node_modules/sqlite-better-trigram-darwin-${arch}",
"to": "node_modules/sqlite-better-trigram-darwin-${arch}"
},
{
"from": "node_modules/sqlite-better-trigram-windows-${arch}",
"to": "node_modules/sqlite-better-trigram-windows-${arch}"
},
"node_modules/sodium-native/index.js",
"node_modules/sodium-native/package.json"
],
@@ -149,10 +181,12 @@
]
}
],
"signingHashAlgorithms": [
"sha256"
],
"sign": "./scripts/sign.js",
"signtoolOptions": {
"signingHashAlgorithms": [
"sha256"
],
"sign": "./scripts/sign.js"
},
"icon": "assets/icons/app.ico"
},
"portable": {

View File

@@ -63,7 +63,7 @@ if (args.variant === "mas") {
await exec(`yarn run bundle`);
}
await exec(`yarn tsc`);
await exec(`yarn run build`);
if (args.run) {
await exec(`yarn electron-builder --dir --x64`);
@@ -94,7 +94,7 @@ async function patchBetterSQLite3() {
);
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
json.version = "11.2.2";
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";

View File

@@ -36,6 +36,8 @@ const ENV = {
FORCE_COLOR: "false",
COLOR: "0"
};
process.chdir(path.join(__dirname, ".."));
await onChange(true);
console.log("Watching...");
@@ -53,11 +55,13 @@ async function onChange(first) {
if (first) {
await fs.rm("./build/", { force: true, recursive: true });
await exec("npm rebuild electron --verbose --foreground-scripts");
await exec("yarn electron-builder install-app-deps");
}
await exec(`yarn run bundle`);
execAsync(`yarn`, [`tsc`]);
await exec(`yarn run build`);
if (await isBundleSame()) {
console.log("Bundle is same. Doing nothing.");

View File

@@ -46,6 +46,8 @@ export class SQLite {
this.sqlite = require("better-sqlite3-multiple-ciphers")(
filePath
).unsafeMode(true);
const betterTrigram = require("sqlite-better-trigram");
betterTrigram.load(this.sqlite);
}
/**

View File

@@ -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/>.
*/
export * from "./constants";
export { PATHS } from "./constants";
export type { AppRouter } from "./api";
export { type UpdateInfo } from "builder-util-runtime";
export { type DesktopIntegration } from "./utils/config";

View File

@@ -101,7 +101,7 @@ async function createWindow() {
...(config.desktopSettings.nativeTitlebar
? {}
: {
titleBarStyle: "hidden",
titleBarStyle: process.platform === "win32" || process.platform === "darwin" ? "hidden" : "default",
frame: process.platform === "win32" || process.platform === "darwin",
titleBarOverlay: {
height: 37,

View File

@@ -19,22 +19,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* eslint-disable no-var */
import { ELECTRON_TRPC_CHANNEL } from "electron-trpc/main";
import { type RendererGlobalElectronTRPC } from "electron-trpc/src/types";
// import type { NNCrypto } from "@notesnook/crypto";
import { ipcRenderer } from "electron";
import { platform } from "os";
declare global {
var os: () => "mas" | ReturnType<typeof platform>;
var electronTRPC: RendererGlobalElectronTRPC;
var electronTRPC: any;
// var NativeNNCrypto: (new () => NNCrypto) | undefined;
}
process.once("loaded", async () => {
const electronTRPC: RendererGlobalElectronTRPC = {
sendMessage: (operation) =>
const electronTRPC = {
sendMessage: (operation: any) =>
ipcRenderer.send(ELECTRON_TRPC_CHANNEL, operation),
onMessage: (callback) =>
onMessage: (callback: any) =>
ipcRenderer.on(ELECTRON_TRPC_CHANNEL, (_event, args) => callback(args))
};
globalThis.electronTRPC = electronTRPC;

View File

@@ -57,7 +57,8 @@ function getSystemTheme() {
nativeTheme.themeSource = oldThemeSource;
setTimeout(
() => listeners.forEach((a) => nativeTheme.addListener("updated", a)),
() =>
listeners.forEach((a) => nativeTheme.addListener("updated", () => a())),
1000
);
return currentTheme;
@@ -72,7 +73,8 @@ function changeTheme(theme: Theme) {
nativeTheme.themeSource = theme;
setTimeout(
() => listeners.forEach((a) => nativeTheme.addListener("updated", a)),
() =>
listeners.forEach((a) => nativeTheme.addListener("updated", () => a())),
1000
);
}

View File

@@ -2,7 +2,8 @@
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "./dist/",
"lib": ["ESNext"]
"lib": ["ESNext"],
"moduleResolution": "Bundler"
},
"include": ["src", "global.d.ts"]
}

View File

@@ -39,6 +39,7 @@ DerivedData
*.xcuserstate
*.hprof
**/.xcode.env.local
native/cache
# Android/IntelliJ
#
rn-build-deps/

View File

@@ -54,10 +54,10 @@ export async function setupDatabase(password) {
storage: Storage,
eventsource: Platform.OS === "ios" ? EventSource : AndroidEventSource,
fs: filesystem,
compressor: {
compressor: () => ({
compress: Gzip.deflate,
decompress: Gzip.inflate
},
}),
batchSize: 100,
sqliteOptions: {
dialect: (name) => ({

View File

@@ -140,7 +140,7 @@ export async function downloadAttachments(attachments) {
useAttachmentStore.getState().setDownloading({
current: 0,
total: 1,
message: `${strings.savingZipFile()}... ${strings.pleaseWait()}`,
message: strings.savingZipFile(),
groupId
});
// If all goes well, zip the notesnook-attachments folder in cache.
@@ -150,9 +150,7 @@ export async function downloadAttachments(attachments) {
groupId,
current: progress,
total: 1,
message: `${strings.savingZipFile()} (${(progress * 100).toFixed(
1
)}%)... ${strings.pleaseWait()}`
message: strings.savingZipFile((progress * 100).toFixed(1))
});
});
await zip(zipSourceFolder, zipOutputFile);
@@ -246,7 +244,7 @@ export default async function downloadAttachment(
);
}
let filename = getFileNameWithExtension(
let filename = await getFileNameWithExtension(
attachment.filename,
attachment.mimeType
);

View File

@@ -235,7 +235,7 @@ export const AttachmentDialog = ({
for (let i = 0; i < filteredAttachments.placeholders.length; i++) {
if (!getState().isWorking) {
ToastManager.show({
message: "Attachment recheck cancelled",
message: strings.attachmentRecheckCancelled(),
type: "info",
context: isSheet ? "local" : "global"
});
@@ -322,7 +322,7 @@ export const AttachmentDialog = ({
) : null}
{!isSheet ? (
<Header
title="Manage attachments"
title={strings.manageAttachments()}
renderedInRoute="SettingsGroup"
canGoBack
headerRightButtons={[
@@ -330,23 +330,23 @@ export const AttachmentDialog = ({
onPress() {
onCheck();
},
title: "Recheck all"
title: strings.recheckAll()
},
{
onPress() {
if (!attachments) return;
presentDialog({
title: `Download ${attachments.placeholders.length} attachments`,
paragraph:
"Are you sure you want to download all attachments?",
positiveText: "Download",
title: strings.doActions.download.attachment(
attachments.placeholders.length
),
positiveText: strings.network.download(),
positivePress: async () => {
downloadAttachments(await attachments.ids());
},
negativeText: "Cancel"
negativeText: strings.cancel()
});
},
title: "Download all"
title: strings.downloadAllAttachments()
}
]}
/>
@@ -388,15 +388,14 @@ export const AttachmentDialog = ({
onPress={() => {
if (!attachments) return;
presentDialog({
title: `Download ${attachments.placeholders.length} attachments`,
paragraph:
"Are you sure you want to download all attachments?",
context: "local",
positiveText: "Download",
title: strings.doActions.download.attachment(
attachments.placeholders.length
),
positiveText: strings.network.download(),
positivePress: async () => {
downloadAttachments(await attachments.ids());
},
negativeText: "Cancel"
negativeText: strings.cancel()
});
}}
size={SIZE.lg}
@@ -457,14 +456,16 @@ export const AttachmentDialog = ({
<Paragraph>
{rechecker.isWorking
? note
? `Checking ${rechecker.filter.toLowerCase()} note attachments`
: `Checking ${rechecker.filter.toLowerCase()} attachments`
: "Attachments recheck complete"}
? strings.checkingNoteAttachments()
: strings.checkingAllAttachments()
: strings.attachmentRecheckComplete()}
</Paragraph>
<Paragraph>
{`${rechecker.isWorking ? "Please wait... " : ""}Passed: ${
{`${
rechecker.isWorking ? `${strings.pleaseWait()} ` : ""
}${strings.passed()}: ${
rechecker.passed
}, Failed: ${rechecker.failed}`}
}, ${strings.failed()}: ${rechecker.failed}`}
</Paragraph>
</View>
</View>

View File

@@ -17,8 +17,11 @@ 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, { useEffect, useRef, useState } from "react";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useRef, useState } from "react";
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 {
@@ -26,19 +29,14 @@ import {
presentSheet,
ToastManager
} from "../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
import { Pressable } from "../ui/pressable";
import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
import { ScrollView } from "react-native-actions-sheet";
import { strings } from "@notesnook/intl";
const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
const { colors } = useThemeColors();
@@ -164,7 +162,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
textAlign: "center"
}}
>
{strings["2faCodeHelpText"][currentMethod.method]() ||
{strings["2faCodeHelpText"][currentMethod.method]?.() ||
strings.select2faCodeHelpText()}
</Paragraph>

View File

@@ -25,7 +25,7 @@ import {
View
} from "react-native";
import { Image as ImageType } from "react-native-image-crop-picker";
import { useThemeColors } from "../../../../../../packages/theme/dist";
import { useThemeColors } from "@notesnook/theme";
import { presentSheet } from "../../../services/event-manager";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";

View File

@@ -164,7 +164,7 @@ export default function Progress() {
/>
</View>
{data?.canHideProgress ? null : (
{!data?.canHideProgress ? null : (
<Button
title={cancelCallback.current ? "Cancel" : "Hide"}
type="secondaryAccented"

View File

@@ -311,8 +311,10 @@ export class VaultDialog extends Component {
loading: true
});
try {
let verified = await db.user.verifyPassword(this.password);
if (!(await db.user.getUser())) verified = true;
let verified = true;
if (await db.user.getUser()) {
verified = await db.user.verifyPassword(this.password);
}
if (verified) {
let noteIds = [];
if (this.state.deleteAll) {
@@ -320,20 +322,21 @@ export class VaultDialog extends Component {
const relations = await db.relations.from(vault, "note").get();
noteIds = relations.map((item) => item.toId);
}
await db.vault.delete(this.state.deleteAll);
noteIds.forEach((id) => {
eSendEvent(
eUpdateNoteInEditor,
{
id: id,
deleted: true
},
true
);
});
if (this.state.deleteAll) {
noteIds.forEach((id) => {
eSendEvent(
eUpdateNoteInEditor,
{
id: id,
deleted: true
},
true
);
});
}
console.log("VAULT UPDATED EVENT");
eSendEvent("vaultUpdated");
this.setState({
loading: false
@@ -517,7 +520,6 @@ export class VaultDialog extends Component {
});
this.close();
} else {
eSendEvent("vaultUpdated");
ToastManager.show({
heading: strings.vaultCreated(),
type: "success",
@@ -525,6 +527,7 @@ export class VaultDialog extends Component {
});
this.close();
}
eSendEvent("vaultUpdated");
}
_permanantUnlock() {

View File

@@ -33,7 +33,6 @@ import { eScrollEvent } from "../../utils/events";
import { LeftMenus } from "./left-menus";
import { RightMenus } from "./right-menus";
import { Title } from "./title";
import { useNavigation } from "@react-navigation/native";
type HeaderRightButton = {
title: string;

View File

@@ -17,30 +17,23 @@ 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 from "react";
import {
Linking,
ScrollView,
TouchableOpacity,
useWindowDimensions,
View
} from "react-native";
import { Linking, ScrollView, useWindowDimensions, View } from "react-native";
import { SwiperFlatList } from "react-native-swiper-flatlist";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { getElevationStyle } from "../../utils/elevation";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { eSendEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { getElevationStyle } from "../../utils/elevation";
import { eOpenLoginDialog } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { AuthMode } from "../auth";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import Navigation from "../../services/navigation";
import { eOpenLoginDialog } from "../../utils/events";
import { AuthMode } from "../auth";
import { eSendEvent } from "../../services/event-manager";
import { strings } from "@notesnook/intl";
const Intro = ({ navigation }) => {
const { colors } = useThemeColors();
@@ -90,7 +83,7 @@ const Intro = ({ navigation }) => {
>
{item.headings?.map((heading) => (
<Heading
key={heading}
key={heading()}
style={{
marginBottom: 5
}}

View File

@@ -41,7 +41,7 @@ export const openNotebook = (item: Notebook | BaseTrashItem<Notebook>) => {
positiveText: strings.restore(),
negativeText: strings.delete(),
positivePress: async () => {
await db.trash.restore(item.id);
if ((await db.trash.restore(item.id)) === false) return;
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(undefined);
ToastManager.show({

View File

@@ -34,12 +34,13 @@ import {
Reminder,
Tag,
TrashItem,
VirtualizedGrouping
VirtualizedGrouping,
getSortValue
} from "@notesnook/core";
import { getSortValue } from "@notesnook/core";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { View } from "react-native";
import { db } from "../../common/database";
import { useIsCompactModeEnabled } from "../../hooks/use-is-compact-mode-enabled";
import { eSendEvent } from "../../services/event-manager";
import { RouteName } from "../../stores/use-navigation-store";
import { eOpenJumpToDialog } from "../../utils/events";
@@ -48,7 +49,6 @@ 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 { useIsCompactModeEnabled } from "../../hooks/use-is-compact-mode-enabled";
type ListItemWrapperProps<TItem = Item> = {
group?: GroupingKey;

View File

@@ -48,7 +48,7 @@ export default function NotePreview({ session, content, note }) {
async function restore() {
if (note && note.type === "trash") {
await db.trash.restore(note.id);
if ((await db.trash.restore(note.id)) === false) return;
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(false);
ToastManager.show({

View File

@@ -137,14 +137,13 @@ export const Items = ({ item, buttons, close }) => {
style={{
alignSelf: "flex-start",
paddingHorizontal: 0,
width: topBarItemWidth
flex: 1
}}
>
<View
onPress={item.func}
style={{
height: topBarItemWidth,
width: topBarItemWidth,
justifyContent: "center",
alignItems: "center",
textAlign: "center",
@@ -169,7 +168,7 @@ export const Items = ({ item, buttons, close }) => {
<Paragraph
textBreakStrategy="simple"
size={SIZE.xxs + 1}
size={SIZE.xxs}
style={{ textAlign: "center" }}
>
{item.title}
@@ -242,7 +241,7 @@ export const Items = ({ item, buttons, close }) => {
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
paddingRight: 25,
gap: 5
gap: 15
}}
>
{topBarItems.map(renderTopBarItem)}

View File

@@ -90,7 +90,7 @@ export const SelectionHeader = React.memo(
const restoreItem = async () => {
if (!selectedItemsList.length) return;
await db.trash.restore(...selectedItemsList);
if ((await db.trash.restore(...selectedItemsList)) === false) return;
Navigation.queueRoutesForUpdate();
clearSelection();
@@ -102,9 +102,8 @@ export const SelectionHeader = React.memo(
const deleteItem = async () => {
presentDialog({
title: strings.deleteItems(type as string, selectedItemsList.length),
paragraph: strings.deleteItemsConfirmation(
type as string,
title: strings.doActions.delete[type](selectedItemsList.length),
paragraph: strings.actionConfirmations.delete[type](
selectedItemsList.length
),
positiveText: strings.delete(),

View File

@@ -81,7 +81,7 @@ export const AddNotebookSheet = ({
id: notebook?.id
});
if (parentNotebook) {
if (parentNotebook && id) {
await db.relations.add(parentNotebook, {
type: "notebook",
id: id
@@ -94,7 +94,8 @@ export const AddNotebookSheet = ({
useNotebookStore.getState().refresh();
const parent =
parentNotebook?.id || (await getParentNotebookId(notebook?.id || id));
parentNotebook?.id ||
(await getParentNotebookId(notebook?.id || (id as string)));
eSendEvent(eOnNotebookUpdated, parent);
if (notebook) {
@@ -104,7 +105,7 @@ export const AddNotebookSheet = ({
});
}
if (!notebook && showMoveNotesOnComplete) {
if (!notebook && showMoveNotesOnComplete && id) {
MoveNotes.present(await db.notebooks.notebook(id));
} else {
close?.(true);

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Note } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, {
RefObject,
@@ -49,13 +50,11 @@ import DialogHeader from "../../dialog/dialog-header";
import SheetProvider from "../../sheet-provider";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Paragraph from "../../ui/typography/paragraph";
import { AddNotebookSheet } from "../add-notebook";
import { NotebookItem } from "./notebook-item";
import { useNotebookItemSelectionStore } from "./store";
import { AddNotebookSheet } from "../add-notebook";
import Input from "../../ui/input";
import { presentDialog } from "../../dialog/functions";
import { strings } from "@notesnook/intl";
async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations

View File

@@ -171,7 +171,7 @@ const ExportNotesSheet = ({
>
<DialogHeader
icon="export"
title={`${strings.export()} ${strings.notes(ids.length)}`}
title={strings.exportNotes(ids.length)}
/>
</View>

View File

@@ -43,7 +43,7 @@ const NewFeature = ({
version
}: {
features: FeatureType[];
version?: string | null;
version?: string;
}) => {
const { colors } = useThemeColors();
@@ -57,7 +57,7 @@ const NewFeature = ({
}}
>
<Heading color={colors.secondary.heading} size={SIZE.md}>
{!version ? `${strings.newVersion()} ` : `v${version} `}Highlights 🎉
{strings.newVersionHighlights(version)}
</Heading>
<Seperator />
@@ -122,7 +122,7 @@ NewFeature.present = () => {
component: (
<NewFeature
features={features}
version={SettingsService.getProperty("version")}
version={SettingsService.getProperty("version") || undefined}
/>
),
disableClosing: true

View File

@@ -206,7 +206,7 @@ export const NotebookSheet = () => {
backgroundInteractionEnabled
gestureEnabled
>
<View
{/* <View
style={{
position: "absolute",
right: 24 + normalize(50),
@@ -214,17 +214,11 @@ export const NotebookSheet = () => {
}}
>
<Pressable
testID={notesnook.buttons.add}
testID="add-notebook-button"
type="secondary"
onPress={() => {
if (!notebook) return;
AddNotebookSheet.present(
undefined,
notebook,
undefined,
undefined,
false
);
}}
style={{
borderRadius: 100
@@ -245,7 +239,7 @@ export const NotebookSheet = () => {
/>
</View>
</Pressable>
</View>
</View> */}
<View
style={{
@@ -404,6 +398,27 @@ export const NotebookSheet = () => {
height: 40 * fontScale
}}
/>
<IconButton
testID="add-notebook-button"
name="notebook-plus"
onPress={() => {
if (!notebook) return;
AddNotebookSheet.present(
undefined,
notebook,
undefined,
undefined,
false
);
}}
color={colors.primary.icon}
size={22}
style={{
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
</>
)}
</View>

View File

@@ -36,7 +36,7 @@ import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { requestInAppReview } from "../../../services/app-review";
import { Note } from "@notesnook/core";
import { hosts, Note } from "@notesnook/core";
import { strings } from "@notesnook/intl";
const PublishNoteSheet = ({
@@ -54,7 +54,7 @@ const PublishNoteSheet = ({
const [note, setNote] = useState<Note | undefined>(item);
const [publishing, setPublishing] = useState(false);
const publishUrl =
note && `https://monogr.ph/${db.monographs.monograph(note?.id)}`;
note && `${hosts.MONOGRAPH_HOST}/${db.monographs.monograph(note?.id)}`;
const isPublished = note && db.monographs.isPublished(note?.id);
const pwdInput = useRef(null);
const passwordValue = useRef<string>();
@@ -121,8 +121,8 @@ const PublishNoteSheet = ({
}}
>
<DialogHeader
title={`${strings.publish()} ${strings.note()}`}
paragraph={strings.publishDesc()}
title={strings.publishNote()}
paragraph={strings.publishNoteDesc()}
/>
{publishing ? (

View File

@@ -16,8 +16,8 @@ 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 { VirtualizedGrouping } from "@notesnook/core";
import { Item, ItemReference } from "@notesnook/core";
import { Item, ItemReference, VirtualizedGrouping } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useEffect, useState } from "react";
import { View } from "react-native";
@@ -37,8 +37,6 @@ import SheetProvider from "../../sheet-provider";
import { Button } from "../../ui/button";
import { PressableProps } from "../../ui/pressable";
import Paragraph from "../../ui/typography/paragraph";
import { isStringLiteralOrJsxExpression } from "typescript";
import { strings } from "@notesnook/intl";
type RelationsListProps = {
actionSheetRef: RefObject<ActionSheetRef>;
@@ -125,7 +123,9 @@ export const RelationsList = ({
// width="100%"
type="inverted"
icon="plus"
title={strings.addItem(referenceType)}
title={strings.addItem(
referenceType as "notebook" | "tag" | "reminder" | "note"
)}
/>
</View>
) : (

View File

@@ -154,7 +154,9 @@ export const Toast = ({ context = "global" }) => {
>
<Icon
name={
toastOptions.type === "success"
toastOptions.icon
? toastOptions.icon
: toastOptions.type === "success"
? "check"
: toastOptions.type === "info"
? "information"

View File

@@ -70,7 +70,6 @@ interface InputProps extends TextInputProps {
};
buttons?: React.ReactNode;
onBlurInput?: () => void;
onPress?: () => void;
height?: number;
fontSize?: number;
onFocusInput?: () => void;

View File

@@ -17,6 +17,9 @@ 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 { Notebook } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { Linking, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
@@ -26,26 +29,22 @@ import {
SUPPORT_SVG,
WELCOME_SVG
} from "../../assets/images/assets";
import { ThemeStore } from "../../stores/use-theme-store";
import useRotator from "../../hooks/use-rotator";
import { eSendEvent } from "../../services/event-manager";
import { getContainerBorder } from "../../utils/colors";
import { getElevationStyle } from "../../utils/elevation";
import { eOpenAddNotebookDialog } from "../../utils/events";
import { SIZE } from "../../utils/size";
import useRotator from "../../hooks/use-rotator";
import { AccentColorPicker } from "../../screens/settings/appearance";
import { Button } from "../ui/button";
import { SvgView } from "../ui/svg";
import { PinItem } from "../side-menu/pinned-section";
import { Button } from "../ui/button";
import Seperator from "../ui/seperator";
import { SvgView } from "../ui/svg";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useThemeColors } from "@notesnook/theme";
import { getContainerBorder } from "../../utils/colors";
import { strings } from "@notesnook/intl";
export type TStep = {
text?: string;
walkthroughItem: (colors: ThemeStore["colors"]) => React.ReactNode;
walkthroughItem: (colors: any) => React.ReactNode;
title?: string;
button?: {
type: "next" | "done";
@@ -62,18 +61,18 @@ const NotebookWelcome = () => {
const { colors } = useThemeColors();
const data = useRotator([
{
title: "Work and office",
description: "Everything related to my job",
title: strings.workAndOffice(),
description: strings.workAndOfficeDesc(),
count: 2
},
{
title: "School work",
description: "I don't like doing this but I have to.",
title: strings.schoolWork(),
description: strings.schoolWorkDesc(),
count: 5
},
{
title: "Recipes",
description: "I love cooking and collecting recipes",
title: strings.recipes(),
description: strings.recipesDesc(),
count: 10
}
]);
@@ -122,17 +121,17 @@ const notebooks: { id: string; steps: TStep[] } = {
id: "notebooks",
steps: [
{
title: "Notebooks",
text: "Boost your productivity with Notebooks and organize your notes.",
title: strings.notebooks(),
text: strings.boostProductivityNotebook(),
walkthroughItem: () => <NotebookWelcome />,
button: {
type: "next",
title: "Next"
title: strings.next()
}
},
{
title: "Notebook > Notes",
text: "Every Notebook can have notes and sub notebooks.",
title: strings.notebookNotes(),
text: strings.notebookNotesDesc(),
walkthroughItem: (colors: any) => (
<View
style={{
@@ -154,9 +153,9 @@ const notebooks: { id: string; steps: TStep[] } = {
}}
>
<Heading size={SIZE.md} color={colors.primary.heading}>
Work and office
{strings.workAndOffice()}
</Heading>
<Paragraph>Everything related to my job in one place.</Paragraph>
<Paragraph>{strings.workAndOfficeDesc()}</Paragraph>
<Paragraph
style={{
@@ -165,7 +164,7 @@ const notebooks: { id: string; steps: TStep[] } = {
size={SIZE.xs}
color={colors.secondary.paragraph}
>
Notebook - 2 notes
{strings.notes(2)}
</Paragraph>
</View>
<View
@@ -184,7 +183,7 @@ const notebooks: { id: string; steps: TStep[] } = {
size={SIZE.sm}
name="bookmark"
/>{" "}
Tasks
{strings.tasks()}
</Paragraph>
</View>
<View
@@ -200,7 +199,7 @@ const notebooks: { id: string; steps: TStep[] } = {
>
<Paragraph size={SIZE.xs}>
<Icon color={colors.primary.icon} size={SIZE.sm} name="note" />{" "}
February 2022 Week 2
{strings.taskAValue()}
</Paragraph>
</View>
<View
@@ -216,7 +215,7 @@ const notebooks: { id: string; steps: TStep[] } = {
>
<Paragraph size={SIZE.xs}>
<Icon color={colors.primary.icon} size={SIZE.sm} name="note" />{" "}
February 2022 Week 1
{strings.taskBValue()}
</Paragraph>
</View>
<View
@@ -235,19 +234,19 @@ const notebooks: { id: string; steps: TStep[] } = {
size={SIZE.sm}
name="bookmark"
/>{" "}
Meetings
{strings.meetings()}
</Paragraph>
</View>
</View>
),
button: {
type: "next",
title: "Next"
title: strings.next()
}
},
{
title: "Easy access",
text: "You can create shortcuts of frequently accessed notebooks in the side menu",
title: strings.easyAccess(),
text: strings.easyAccessDesc(),
walkthroughItem: () => (
<View
style={{
@@ -256,29 +255,31 @@ const notebooks: { id: string; steps: TStep[] } = {
}}
>
<PinItem
index={0}
placeholder={true}
item={{
title: "Tasks",
type: "topic"
}}
isPlaceholder={true}
item={
{
title: strings.tasks(),
type: "notebook"
} as Notebook
}
onPress={() => {}}
/>
<PinItem
index={1}
placeholder={true}
item={{
title: "Work and office",
type: "notebook"
}}
isPlaceholder={true}
item={
{
title: strings.workAndOffice(),
type: "notebook"
} as Notebook
}
onPress={() => {}}
/>
</View>
),
button: {
type: "done",
title: "Add your first notebook",
title: strings.addFirstNotebook(),
action: () => {
eSendEvent(eOpenAddNotebookDialog);
}
@@ -287,67 +288,32 @@ const notebooks: { id: string; steps: TStep[] } = {
]
};
const ChooseTheme = () => {
return (
<View
style={{
alignItems: "center",
marginTop: 20
}}
>
<Heading>Make yourself at home</Heading>
<Paragraph
style={{
textAlign: "center",
alignSelf: "center",
maxWidth: "80%"
}}
size={SIZE.md}
>
Pick a theme of your choice
</Paragraph>
<Seperator />
<AccentColorPicker />
<Seperator />
</View>
);
};
const trialstarted: { id: string; steps: TStep[] } = {
id: "trialstarted",
steps: [
{
title: "Your trial is activated",
text: "You can use all premium features for free for the next 14 days",
title: strings.trialStarted(),
text: strings.trialStartedDesc(),
walkthroughItem: (colors) => (
<SvgView src={LAUNCH_ROCKET(colors.primary.paragraph)} />
),
button: {
type: "next",
title: "Next"
}
},
{
walkthroughItem: () => <ChooseTheme />,
button: {
type: "next",
title: "Next"
title: strings.next()
}
},
{
title: "Join the cause",
text: "Meet other privacy-minded people and talk to us directly about your concerns, issues and suggestions.",
title: strings.joinTheCause(),
text: strings.meetPrivacyMinded(),
walkthroughItem: (colors) => (
<SvgView src={COMMUNITY_SVG(colors.primary.paragraph)} />
),
button: {
type: "done",
title: "Continue"
title: strings.continue()
},
actionButton: {
text: "Join Discord Community",
text: strings.joinDiscord(),
action: () => {
Linking.openURL("https://discord.gg/zQBK97EE22").catch(console.log);
}
@@ -360,14 +326,14 @@ const emailconfirmed: { id: string; steps: TStep[] } = {
id: "emailconfirmed",
steps: [
{
title: "Email confirmed",
text: "Your email was confirmed successfully. Thank you for choosing end-to-end encrypted note taking.",
title: strings.emailConfirmed(),
text: strings.emailNotConfirmedDesc(),
walkthroughItem: (colors) => (
<SvgView src={WELCOME_SVG(colors.primary.paragraph)} />
),
button: {
type: "done",
title: "Continue"
title: strings.continue()
}
}
]
@@ -382,15 +348,14 @@ const Support = () => {
}}
>
<SvgView src={SUPPORT_SVG()} />
<Heading>Get Priority Support</Heading>
<Heading>{strings.prioritySupport()}</Heading>
<Paragraph
style={{
textAlign: "center"
}}
size={SIZE.md}
>
You can reach out to us via multiple channels if you face an issue or
want to just talk.
{strings.weAreAlwaysListening()}
</Paragraph>
<Seperator />
@@ -405,7 +370,7 @@ const Support = () => {
}}
icon="discord"
type="secondary"
title="Join our community on Discord"
title={strings.joinDiscord()}
/>
<Button
@@ -419,7 +384,7 @@ const Support = () => {
}}
icon="telegram"
type="secondary"
title="Join our Telegram group"
title={strings.joinTelegram()}
/>
<Button
style={{
@@ -429,7 +394,7 @@ const Support = () => {
}}
icon="bug"
type="secondary"
title="Submit an issue from Settings"
title={strings.reportAnIssue()}
/>
<Button
style={{
@@ -439,7 +404,7 @@ const Support = () => {
}}
icon="mail"
type="secondary"
title="Email us at support@streetwriters.co"
title={strings.emailSupport()}
/>
</View>
);
@@ -449,21 +414,21 @@ const prouser: { id: string; steps: TStep[] } = {
id: "prouser",
steps: [
{
title: "Welcome to Notesnook Pro",
text: "Thank you for reaffirming our idea that privacy comes first",
title: strings.welcomeToNotesnookPro(),
text: strings.thankYouPrivacy(),
walkthroughItem: (colors) => (
<SvgView src={LAUNCH_ROCKET(colors.primary.paragraph)} />
),
button: {
type: "next",
title: "Next"
title: strings.next()
}
},
{
walkthroughItem: () => <Support />,
button: {
type: "done",
title: "Continue"
title: strings.continue()
}
}
]

View File

@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* eslint-disable no-inner-declarations */
import { VAULT_ERRORS } from "@notesnook/core";
import {
Color,
ItemReference,
@@ -25,8 +24,11 @@ import {
Notebook,
Reminder,
Tag,
TrashItem
TrashItem,
VAULT_ERRORS,
createInternalLink
} from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { DisplayedNotification } from "@notifee/react-native";
import Clipboard from "@react-native-clipboard/clipboard";
import React, { useCallback, useEffect, useRef, useState } from "react";
@@ -39,8 +41,10 @@ import NoteHistory from "../components/note-history";
import { AddNotebookSheet } from "../components/sheets/add-notebook";
import MoveNoteSheet from "../components/sheets/add-to";
import ExportNotesSheet from "../components/sheets/export-notes";
import { MoveNotebookSheet } from "../components/sheets/move-notebook";
import { MoveNotes } from "../components/sheets/move-notes/movenote";
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";
@@ -60,15 +64,10 @@ import { useRelationStore } from "../stores/use-relation-store";
import { useSelectionStore } from "../stores/use-selection-store";
import { useTagStore } from "../stores/use-tag-store";
import { useUserStore } from "../stores/use-user-store";
import Errors from "../utils/errors";
import { eOpenLoginDialog, eUpdateNoteInEditor } from "../utils/events";
import { deleteItems } from "../utils/functions";
import { convertNoteToText } from "../utils/note-to-text";
import { sleep } from "../utils/time";
import { ReferencesList } from "../components/sheets/references";
import { createInternalLink } from "@notesnook/core";
import { MoveNotebookSheet } from "../components/sheets/move-notebook";
import { strings } from "@notesnook/intl";
export const useActions = ({
close,
@@ -141,7 +140,7 @@ export const useActions = ({
async function restoreTrashItem() {
if (!checkItemSynced()) return;
close();
await db.trash.restore(item.id);
if ((await db.trash.restore(item.id)) === false) return;
Navigation.queueRoutesForUpdate();
const type = item.type === "trash" ? item.itemType : item.type;
ToastManager.show({
@@ -255,8 +254,8 @@ export const useActions = ({
item.type === "color"
) {
presentDialog({
title: strings.deleteItem(item.type),
paragraph: strings.deleteItemConfirmation(item.type),
title: strings.doActions.delete[item.type](1),
paragraph: strings.actionConfirmations.delete[item.type](1),
positivePress: async () => {
if (item.type === "reminder") {
await db.reminders.remove(item.id);
@@ -304,8 +303,8 @@ export const useActions = ({
close();
await sleep(300);
presentDialog({
title: strings.delete(),
paragraph: strings.deleteItemsConfirmation(item.itemType, 1),
title: strings.doActions.delete[item.itemType](1),
paragraph: strings.actionConfirmations.delete[item.itemType](1),
positiveText: strings.delete(),
negativeText: strings.cancel(),
positivePress: async () => {
@@ -314,7 +313,10 @@ export const useActions = ({
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(undefined);
ToastManager.show({
heading: strings.itemDeleted(1, item.itemType),
heading:
strings.actions.deleted[
item.itemType as keyof typeof strings.actions.deleted
](1),
type: "success",
context: "local"
});
@@ -955,7 +957,11 @@ export const useActions = ({
id: "trash",
title:
item.type !== "notebook" && item.type !== "note"
? strings.doAction(item.type, 1, "delete")
? strings.doActions.delete[
item.type === "trash"
? item.itemType
: (item.type as keyof typeof strings.doActions.delete)
](1)
: strings.moveToTrash(),
icon: "delete-outline",
type: "error",

View File

@@ -93,6 +93,7 @@ import { sleep } from "../utils/time";
import { NotesnookModule } from "../utils/notesnook-module";
import { changeSystemBarColors } from "../stores/use-theme-store";
import { strings } from "@notesnook/intl";
import { endProgress, startProgress } from "../components/dialogs/progress";
const onCheckSyncStatus = async (type: SyncStatusEvent) => {
const { disableSync, disableAutoSync } = SettingsService.get();
@@ -577,6 +578,27 @@ export const useAppEvents = () => {
EV.subscribe(EVENTS.uploadCanceled, (data) => {
useAttachmentStore.getState().setUploading(data);
}),
EV.subscribe(EVENTS.migrationStarted, (name) => {
if (
name !== "notesnook" ||
!SettingsService.getProperty("introCompleted")
)
return;
startProgress({
title: "Migrating Data",
paragraph: "Please wait while we migrate your data",
canHideProgress: false,
fillBackground: true
});
}),
EV.subscribe(EVENTS.migrationFinished, (name) => {
if (
name !== "notesnook" ||
!SettingsService.getProperty("introCompleted")
)
return;
endProgress();
}),
EV.subscribe(EVENTS.vaultLocked, async () => {
// Lock all notes in all tabs...
for (const tab of useTabStore.getState().tabs) {

View File

@@ -18,11 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useCallback, useEffect } from "react";
import { db } from "../common/database";
import BiometricService from "../services/biometrics";
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
import { db } from "../common/database";
const VaultStatusCache = {
const VaultStatusDefaults = {
exists: false,
biometryEnrolled: false,
isBiometryAvailable: false
@@ -35,18 +35,12 @@ export type VaultStatusType = {
};
export const useVaultStatus = () => {
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusCache);
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusDefaults);
const checkVaultStatus = useCallback(() => {
db.vault?.exists().then(async (exists) => {
const available = await BiometricService.isBiometryAvailable();
const fingerprint = await BiometricService.hasInternetCredentials();
if (
VaultStatusCache.exists === exists &&
VaultStatusCache.biometryEnrolled === fingerprint &&
VaultStatusCache.isBiometryAvailable === available
)
return;
setVaultStatus({
exists: exists,
biometryEnrolled: fingerprint,

View File

@@ -34,7 +34,6 @@
"katex": "0.16.2",
"@readme/data-urls": "3.0.0",
"react-native-wheel-color-picker": "^1.3.1",
"tinycolor2": "1.6.0",
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "10.45.2",

View File

@@ -146,15 +146,16 @@ const Editor = React.memo(
onRenderProcessGone={onError}
nestedScrollEnabled
onError={onError}
injectedJavaScriptBeforeContentLoaded={`
globalThis.LINGUI_LOCALE = "${i18n.locale}";
globalThis.LINGUI_LOCALE_DATA = ${JSON.stringify({
[i18n.locale]: i18n.messages
})};
globalThis.__DEV__ = ${__DEV__}
globalThis.readonly=${readonly};
globalThis.noToolbar=${noToolbar};
globalThis.noHeader=${noHeader};
injectedJavaScript={`
globalThis.__DEV__ = ${__DEV__}
globalThis.readonly=${readonly};
globalThis.noToolbar=${noToolbar};
globalThis.noHeader=${noHeader};
globalThis.LINGUI_LOCALE = "${i18n.locale}";
globalThis.LINGUI_LOCALE_DATA = ${JSON.stringify({
[i18n.locale]: i18n.messages
})};
globalThis.loadApp();
`}
useSharedProcessPool={false}
javaScriptEnabled={true}

View File

@@ -34,6 +34,7 @@ import { EditorEvents } from "./tiptap/utils";
import { useThemeColors } from "@notesnook/theme";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { db } from "../../common/database";
import { i18n } from "@lingui/core";
const onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
if (request.url.includes("https")) {
@@ -143,10 +144,17 @@ export function ReadonlyEditor(props: {
ref={editorRef}
key={"readonly-editor:" + props.editorId}
nestedScrollEnabled
injectedJavaScriptBeforeContentLoaded={`globalThis.readonlyEditor=true;`}
injectedJavaScript="globalThis.readonlyEditor=true;"
injectedJavaScript={`
globalThis.__DEV__ = ${__DEV__}
globalThis.readonlyEditor=true;
globalThis.LINGUI_LOCALE = "${i18n.locale}";
globalThis.LINGUI_LOCALE_DATA = ${JSON.stringify({
[i18n.locale]: i18n.messages
})};
globalThis.loadApp();`}
useSharedProcessPool={false}
javaScriptEnabled={true}
webviewDebuggingEnabled={__DEV__}
focusable={true}
setSupportMultipleWindows={false}
overScrollMode="never"

View File

@@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* eslint-disable @typescript-eslint/no-var-requires */
import { ItemReference } from "@notesnook/core";
import type { Attachment } from "@notesnook/editor";
import { getDefaultPresets } from "@notesnook/editor/dist/toolbar/tool-definitions";
import { getDefaultPresets } from "@notesnook/editor/dist/cjs/toolbar/tool-definitions";
import Clipboard from "@react-native-clipboard/clipboard";
import React, { useCallback, useEffect, useRef } from "react";
import {

View File

@@ -78,7 +78,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
placeholder={{
title: route.name?.toLowerCase(),
paragraph: strings.notesEmpty(),
button: strings.newNote(),
button: strings.createNewNote(),
action: openEditor,
loading: strings.loadingNotes()
}}

View File

@@ -76,7 +76,7 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
await results.item(0);
setResults(results);
if (results.placeholders?.length === 0) {
setSearchStatus(`${strings.noResultsFound(query)}`);
setSearchStatus(strings.noResultsFound(query));
} else {
setSearchStatus(undefined);
}
@@ -125,10 +125,8 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
loading={loading}
placeholder={{
title: route.name,
paragraph:
searchStatus ||
`${strings.typeAKeywordToSearchIn(route.params?.title)}`,
loading: `${strings.searchingFor(currentQuery.current as string)}...`
paragraph: searchStatus || strings.searchInRoute(route.params?.title),
loading: strings.searchingFor(currentQuery.current as string)
}}
/>
</>

View File

@@ -17,18 +17,18 @@ 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 { Icons } from "@notesnook/editor/dist/toolbar/icons";
import { Icons } from "@notesnook/editor/dist/cjs/toolbar/icons";
import { ToolbarGroupDefinition } from "@notesnook/editor";
import {
getAllTools,
getDefaultPresets
} from "@notesnook/editor/dist/toolbar/tool-definitions";
} from "@notesnook/editor/dist/cjs/toolbar/tool-definitions";
import { ToolId } from "@notesnook/editor";
export const tools = getAllTools();
export const tools = getAllTools() as any;
export const presets: { [name: string]: ToolbarGroupDefinition[] } = {
default: getDefaultPresets().default,
minimal: getDefaultPresets().minimal,
default: getDefaultPresets().default as any,
minimal: getDefaultPresets().minimal as any,
custom: []
};

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { DATE_FORMATS, TIME_FORMATS } from "@notesnook/core";
import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
import { getFontById, getFonts } from "@notesnook/editor/dist/cjs/utils/font";
import dayjs from "dayjs";
import { createSettingsPicker } from ".";
import { db } from "../../../common/database";

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { formatBytes, getFormattedDate } from "@notesnook/common";
import { LegacyBackupFile } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { ActivityIndicator, FlatList, Platform, View } from "react-native";
@@ -46,7 +47,6 @@ import SettingsService from "../../../services/settings";
import { refreshAllStores } from "../../../stores/create-db-collection-store";
import { useUserStore } from "../../../stores/use-user-store";
import { SIZE } from "../../../utils/size";
import { strings } from "@notesnook/intl";
type PasswordOrKey = { password?: string; encryptionKey?: string };
@@ -153,7 +153,7 @@ const restoreBackup = async (options: {
if (path === ".nnbackup" || path === "attachments") continue;
updateProgress({
progress: `${strings.restoringBackup()}... (${count++}/${
progress: `${strings.restoringBackup()} (${count++}/${
extractedBackupFiles.length
})`
});
@@ -242,7 +242,7 @@ const restoreBackup = async (options: {
await db.transaction(async () => {
updateProgress({
progress: strings.restoringBackup() + "..."
progress: strings.restoringBackup()
});
await db.backup.import(backup, {
encryptionKey,
@@ -533,12 +533,11 @@ const BackupItem = ({
}}
onPress={() => {
presentDialog({
title: `Restore ${itemName}`,
paragraph: `Are you sure you want to restore this backup?`,
positiveText: "Restore",
negativeText: "Cancel",
title: `${strings.restore()} ${itemName}`,
paragraph: strings.restoreBackupConfirm(),
positiveText: strings.restore(),
negativeText: strings.cancel(),
positivePress: async () => {
console.log("file path", (item as ScopedStorage.FileType).uri);
restoreBackup({
uri:
Platform.OS === "android"

View File

@@ -32,7 +32,12 @@ import SettingsService from "../../services/settings";
import { HostId, HostIds } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
export const ServerIds = ["notesnook-sync", "auth", "sse"] as const;
export const ServerIds = [
"notesnook-sync",
"auth",
"sse",
"monograph"
] as const;
export type ServerId = (typeof ServerIds)[number];
type Server = {
id: ServerId;
@@ -40,6 +45,7 @@ type Server = {
title: string;
example: string;
description: string;
versionEndpoint: string;
};
type VersionResponse = {
version: number;
@@ -52,21 +58,32 @@ const SERVERS: Server[] = [
host: "API_HOST",
title: strings.syncServer(),
example: "http://localhost:4326",
description: strings.syncServerDesc()
description: strings.syncServerDesc(),
versionEndpoint: "/version"
},
{
id: "auth",
host: "AUTH_HOST",
title: strings.authServer(),
example: "http://localhost:5326",
description: strings.authServerDesc()
description: strings.authServerDesc(),
versionEndpoint: "/version"
},
{
id: "sse",
host: "SSE_HOST",
title: strings.sseServer(),
example: "http://localhost:7326",
description: strings.sseServerDesc()
description: strings.sseServerDesc(),
versionEndpoint: "/version"
},
{
id: "monograph",
host: "MONOGRAPH_HOST",
title: strings.monographServer(),
example: "http://localhost:6326",
description: strings.monographServerDesc(),
versionEndpoint: "/api/version"
}
];
export function ServersConfiguration() {
@@ -135,28 +152,31 @@ export function ServersConfiguration() {
type="secondary"
width="100%"
onPress={async () => {
setError(undefined);
try {
for (const host of HostIds) {
const url = urls[host];
const server = SERVERS.find((s) => s.host === host)!;
if (!server) throw new Error(strings.serverNotFound(host));
if (!url) throw new Error(strings.allServerUrlsRequired());
const version = await fetch(`${url}/version`)
const version = await fetch(`${url}${server.versionEndpoint}`)
.then((r) => r.json() as Promise<VersionResponse>)
.catch(() => undefined);
if (!version)
throw new Error(
`${strings.couldNotConnectTo()} ${server.title}.`
`${strings.couldNotConnectTo(server.title)}`
);
if (version.id !== server.id)
throw new Error(
`${strings.incorrectServerUrl(url)} ${server.title}.`
`${strings.incorrectServerUrl(url, server.title)}.`
);
if (!isServerCompatible(version.version)) {
throw new Error(strings.serverVersionMismatch());
throw new Error(
strings.serverVersionMismatch(server.title, url)
);
}
}
setSuccess(true);
setError(undefined);
} catch (e) {
setError((e as Error).message);
}

View File

@@ -77,6 +77,7 @@ import { useDragState } from "./editor/state";
import { verifyUser, verifyUserWithApplock } from "./functions";
import { SettingSection } from "./types";
import { getTimeLeft } from "./user-section";
import Clipboard from "@react-native-clipboard/clipboard";
type User = any;
@@ -339,21 +340,19 @@ export const settingsGroups: SettingSection[] = [
presentDialog({
title: strings.clearCacheConfirm(),
paragraph: strings.clearCacheConfirmDesc(),
positiveText: "Clear",
positiveText: strings.clear(),
positivePress: async () => {
filesystem.clearCache();
ToastManager.show({
heading: "Cache cleared",
message: "All cached attachments have been removed",
heading: strings.cacheCleared(),
message: strings.cacheClearedDesc(),
type: "success"
});
}
});
},
description(current) {
return `${strings.clearCacheDesc()}. Current cache size: ${
current as number
}`;
return strings.clearCacheDesc(current as number);
},
useHook: () => {
const [cacheSize, setCacheSize] = React.useState(0);
@@ -380,7 +379,7 @@ export const settingsGroups: SettingSection[] = [
presentDialog({
title: strings.logout(),
paragraph: strings.logoutConfirmation(),
positiveText: "Logout",
positiveText: strings.logout(),
check: {
info: strings.backupDataBeforeLogout(),
defaultValue: true
@@ -444,7 +443,7 @@ export const settingsGroups: SettingSection[] = [
}
updateProgress({
progress: `${strings.loggingOut()}... ${strings.pleaseWait()}`
progress: strings.loggingOut()
});
await db.user?.logout();
@@ -804,8 +803,8 @@ export const settingsGroups: SettingSection[] = [
{
id: "servers",
type: "screen",
name: "Servers",
description: "Configure server URLs for Notesnook",
name: strings.servers(),
description: strings.serversConfigurationDesc(),
icon: "server",
component: "server-config"
}
@@ -1385,7 +1384,15 @@ export const settingsGroups: SettingSection[] = [
name: strings.emailSupport(),
icon: "mail",
modifer: () => {
Linking.openURL("mailto:support@streetwriters.co");
Clipboard.setString("support@streetwriters.co");
ToastManager.show({
heading: strings.emailCopied(),
type: "success",
icon: "content-copy"
});
setTimeout(() => {
Linking.openURL("mailto:support@streetwriters.co");
}, 1000);
},
description: strings.emailSupportDesc()
},

View File

@@ -17,8 +17,10 @@ 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 { Linking, Platform, View } from "react-native";
import Config from "react-native-config";
import { Button } from "../../components/ui/button";
import { usePricing } from "../../hooks/use-pricing";
import {
@@ -28,14 +30,9 @@ import {
} from "../../services/event-manager";
import PremiumService from "../../services/premium";
import { useUserStore } from "../../stores/use-user-store";
import {
SUBSCRIPTION_PROVIDER,
SUBSCRIPTION_STATUS
} from "../../utils/constants";
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
import { eOpenPremiumDialog } from "../../utils/events";
import { SIZE } from "../../utils/size";
import Config from "react-native-config";
import { strings } from "@notesnook/intl";
export const Subscription = () => {
const user = useUserStore((state) => state.user);
const monthlyPlan = usePricing("monthly");

View File

@@ -148,28 +148,31 @@ export const ToastManager = {
type = "error",
context = "global",
func,
actionText
actionText,
icon,
duration = 3000
}: ToastOptions) => {
if (Config.isTesting) return;
eSendEvent(eShowToast, {
heading: heading,
message: message,
type: type,
context: context,
duration: 3000,
func: func,
actionText: actionText
heading,
message,
type,
context,
func,
actionText,
icon,
duration
});
},
hide: () => eSendEvent(eHideToast),
error: (e: Error, title?: string, context?: any, duration = 5000) => {
error: (e: Error, title?: string, context?: any, duration = 6000) => {
ToastManager.show({
heading: title,
message: e?.message || "",
type: "error",
context: context || "global",
actionText: "Copy logs",
duration: 6000,
duration: duration,
func: () => {
Clipboard.setString(e?.stack || "");
ToastManager.show({

View File

@@ -25,7 +25,12 @@ import { FileType } from "react-native-scoped-storage";
import create, { State } from "zustand";
import { ThemeDark, ThemeLight, ThemeDefinition } from "@notesnook/theme";
import { Reminder } from "@notesnook/core";
export const HostIds = ["API_HOST", "AUTH_HOST", "SSE_HOST"] as const;
export const HostIds = [
"API_HOST",
"AUTH_HOST",
"SSE_HOST",
"MONOGRAPH_HOST"
] as const;
export type HostId = (typeof HostIds)[number];
export type Settings = {

View File

@@ -33,11 +33,9 @@ import { strings } from "@notesnook/intl";
function confirmDeleteAllNotes(items, type, context) {
return new Promise((resolve) => {
presentDialog({
title: `Delete ${
items.length > 1 ? `${items.length} ${type}s` : `${type}`
}?`,
positiveText: "Delete",
negativeText: "Cancel",
title: strings.doActions.delete[type](items.length),
positiveText: strings.delete(),
negativeText: strings.cancel(),
positivePress: (_inputValue, value) => {
setTimeout(() => {
resolve({ delete: true, deleteNotes: value });
@@ -114,10 +112,10 @@ export const deleteItems = async (items, type, context) => {
}
} else if (type === "tag") {
presentDialog({
title: strings.deleteTags(ids.length),
title: strings.doActions.delete.tag(ids.length),
positiveText: strings.delete(),
negativeText: strings.cancel(),
paragraph: strings.deleteTagsConfirm(),
paragraph: strings.actionConfirmations.delete.tag(2),
positivePress: async () => {
await db.tags.remove(...ids);
useTagStore.getState().refresh();
@@ -130,12 +128,12 @@ export const deleteItems = async (items, type, context) => {
let deletedIds = [...ids];
if (type === "notebook" || type === "note") {
let message = strings.movedToTrash(type, ids.length);
let message = strings.actions.movedToTrash[type](ids.length);
ToastManager.show({
heading: message,
type: "success",
func: async () => {
await db.trash.restore(...deletedIds);
if ((await db.trash.restore(...deletedIds)) === false) return;
Navigation.queueRoutesForUpdate();
useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes();

View File

@@ -34,7 +34,6 @@ export async function unlockVault({
paragraph: string;
}) {
if (unlockPromise) {
console.log("Unlocking.... waiting for unlock promise");
return unlockPromise;
}
unlockPromise = new Promise(async (resolve) => {
@@ -58,10 +57,10 @@ export async function unlockVault({
context: context,
input: true,
secureTextEntry: true,
positiveText: "Unlock",
positiveText: strings.unlock(),
title: title,
paragraph: paragraph,
inputPlaceholder: "Enter password",
inputPlaceholder: strings.enterPassword(),
positivePress: async (value) => {
const unlocked = await db.vault.unlock(value);
if (!unlocked) {

View File

@@ -0,0 +1,29 @@
import { execSync } from "child_process";
import { pathExists, ensureDir } from "fs-extra";
import { resolveConfig } from "detox/internals";
import { globalSetup } from "detox/runners/jest";
export default async function customGlobalSetup() {
const config = await resolveConfig();
if (config.device.type === "android.emulator") {
await downloadTestButlerAPK();
}
await globalSetup();
}
async function downloadTestButlerAPK() {
const version = "2.2.1";
const artifactUrl = `https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/${version}/test-butler-app-${version}.apk`;
const filePath = `cache/test-butler-app.apk`;
await ensureDir("cache");
if (!(await pathExists(filePath))) {
console.log(`\nDownloading Test-Butler APK v${version}...`);
execSync(`curl -f -o ${filePath} ${artifactUrl}`);
}
}
module.exports = customGlobalSetup;

View File

@@ -1,20 +1,21 @@
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
rootDir: "..",
testMatch: ["<rootDir>/e2e/**/*.e2e.js"],
testTimeout: 120000,
maxWorkers: 1,
setupFilesAfterEnv: ["./e2e/setup.js"],
globalSetup: "detox/runners/jest/globalSetup",
testTimeout: 120000,
globalSetup: "./e2e/globalSetup.ts",
globalTeardown: "detox/runners/jest/globalTeardown",
reporters: ["detox/runners/jest/reporter"],
setupFilesAfterEnv: ["./e2e/setup.ts"],
testEnvironment: "detox/runners/jest/testEnvironment",
verbose: true,
reporters: ["detox/runners/jest/reporter"],
testRunner: "jest-circus/runner",
testMatch: ["<rootDir>/e2e/**/*.e2e.(js|ts)"],
transform: {
"^.+\\.(js|jsx|ts|tsx)$": [
"\\.tsx?$": "ts-jest",
"^.+\\.(js|jsx)$": [
"babel-jest",
{ configFile: "../native/babel.config.js" }
]
},
transformIgnorePatterns: ["<rootDir>/../node_modules/"]
verbose: true
};

View File

@@ -74,7 +74,7 @@ async function login() {
// }
describe("AUTH", () => {
it("Login", async () => {
it.skip("Login", async () => {
await prepare();
await openSideMenu();
await login();

View File

@@ -17,23 +17,22 @@ 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 { navigate, tapByText, prepare, sleep } from "./utils";
import { Tests } from "./utils";
describe("APP LAUNCH AND NAVIGATION", () => {
it("App should launch successfully & hide welcome screen", async () => {
await prepare();
await Tests.prepare();
});
it("Basic navigation should work", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await navigate("Favorites");
await navigate("Trash");
await navigate("Tags");
await navigate("Settings");
await navigate("Monographs");
await navigate("Reminders");
await navigate("Notes");
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.navigate("Favorites");
await Tests.navigate("Trash");
await Tests.navigate("Tags");
await Tests.navigate("Settings");
await Tests.navigate("Monographs");
await Tests.navigate("Reminders");
await Tests.navigate("Notes");
});
});

View File

@@ -1,140 +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 { notesnook } from "../test.ids";
import {
navigate,
tapById,
visibleByText,
createNote,
prepare,
visibleById,
notVisibleById,
sleep,
exitEditor,
tapByText,
elementByText,
elementById
} from "./utils";
describe("NOTE TESTS", () => {
it("Create a note in editor", async () => {
await prepare();
await createNote();
});
it("Open and close a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.ids.note.get(0));
await exitEditor();
});
it.only("Notes properties should show", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await waitFor(elementByText("Created at:")).toBeVisible().withTimeout(500);
});
it("Favorite and unfavorite a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-favorite");
await visibleById("icon-star");
await navigate("Favorites");
await visibleByText(note.body);
await sleep(500);
await tapById(notesnook.listitem.menu);
await tapById("icon-favorite");
await expect(element(by.text(note.body))).not.toBeVisible();
await navigate("Notes");
});
it("Pin a note to top", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-pin");
await visibleByText("Pinned");
await visibleById("icon-pinned");
await tapById(notesnook.listitem.menu);
await tapById("icon-pin");
expect(element(by.id("icon-pinned"))).not.toBeVisible();
});
it("Pin a note in notifications", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-pin-to-notifications");
await visibleByText("Unpin from notifications");
await sleep(500);
await tapById("icon-pin-to-notifications");
await sleep(500);
await visibleByText("Pin to notifications");
});
it("Copy note", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await sleep(1000);
await waitFor(elementById("icon-copy")).toBeVisible().withTimeout(500);
await tapById("icon-copy");
});
it("Assign colors to a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await sleep(1000);
await tapByText("Add color");
await sleep(500);
await elementById("color-title-input").typeText("Test color");
await tapByText("Add color");
await sleep(3000);
await visibleById("icon-check");
await tapById("icon-color-#efefef");
await notVisibleById("icon-check");
await tapById("icon-color-#efefef");
await device.pressBack();
await navigate("Test color");
await visibleByText(note.body);
});
it("Delete & restore a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await sleep(500);
await tapById("icon-trash");
await navigate("Trash");
await sleep(500);
await tapById(notesnook.listitem.menu);
await sleep(500);
await tapByText("Restore note");
await device.pressBack();
await sleep(500);
await visibleByText(
"Test note description that is very long and should not fit in text."
);
});
});

View File

@@ -0,0 +1,116 @@
/*
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 { notesnook } from "../test.ids";
import { Tests } from "./utils";
describe("NOTE TESTS", () => {
it("Create a note in editor", async () => {
await Tests.prepare();
await Tests.createNote();
});
it("Open and close a note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
await Tests.exitEditor();
});
it("Notes properties should show", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromText("Created at").isVisible();
});
it("Favorite and unfavorite a note", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-favorite").waitAndTap();
await Tests.fromId("icon-star").isVisible();
await Tests.navigate("Favorites");
await Tests.fromText(note.body).isVisible();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-favorite").waitAndTap();
await Tests.fromText(note.body).isNotVisible();
await Tests.navigate("Notes");
});
it("Pin a note to top", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-pin").waitAndTap();
await Tests.fromText("Pinned").isVisible();
await Tests.fromId("icon-pinned").isVisible();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-pin").waitAndTap();
await Tests.fromText("icon-pinned").isNotVisible();
});
it.skip("Pin a note in notifications", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-pin-to-notifications").waitAndTap();
await Tests.fromText("Unpin from notifications").isVisible();
await Tests.fromId("icon-pin-to-notifications").waitAndTap();
await Tests.fromText("Pin to notifications").isVisible();
});
it("Copy note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-copy").isVisible();
await Tests.fromId("icon-copy").waitAndTap();
});
it("Assign colors to a note", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromText("Add color").waitAndTap();
await Tests.fromId("color-title-input").element.typeText("Test color");
await Tests.fromText("Add color").waitAndTap();
await Tests.fromId("icon-check").isVisible();
await Tests.fromId("icon-color-#efefef").waitAndTap();
await Tests.fromId("icon-check").isNotVisible();
await Tests.fromId("icon-color-#efefef").waitAndTap();
await device.pressBack();
await Tests.navigate("Test color");
await Tests.fromText(note.body).isVisible();
});
it("Delete & restore a note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-trash").waitAndTap();
await Tests.navigate("Trash");
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromText("Restore").waitAndTap();
await device.pressBack();
await Tests.fromText(
"Test note description that is very long and should not fit in text."
).isVisible();
});
});

View File

@@ -1,309 +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 { notesnook } from "../test.ids";
import {
tapById,
elementById,
visibleByText,
tapByText,
createNote,
prepare,
notVisibleById,
navigate,
elementByText,
sleep,
notVisibleByText,
visibleById,
createNotebook
} from "./utils";
// async function addTopic(title = "Topic") {
// await tapById(notesnook.buttons.add);
// await elementById("input-title").typeText(title);
// await tapByText("Add");
// await sleep(500);
// }
describe("NOTEBOOKS", () => {
it("Create a notebook with title only", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
});
it("Create a notebook title & description", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
});
it("Create a notebook, move notes", async () => {
await prepare();
let note = await createNote();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, true);
await sleep(500);
await tapById("listitem.select");
await tapByText("Move selected notes");
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await visibleByText(note.body);
});
it("Add a sub notebook to a notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await tapById("add-notebook-button");
await createNotebook("Sub notebook", true, true);
await sleep(500);
await device.pressBack();
await visibleByText("Sub notebook");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(300);
await tapByText("Delete");
await notVisibleByText("Sub notebook");
});
it("Remove a sub notebook from notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await tapById("add-notebook-button");
await createNotebook("Sub notebook", true, true);
await sleep(500);
await device.pressBack();
await visibleByText("Sub notebook");
});
it("Edit notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapById(notesnook.buttons.add);
await createNotebook("Notebook 1", true);
await sleep(500);
await device.pressBack();
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit notebook");
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
" (edited)"
);
await tapByText("Save");
await visibleByText("Notebook 1 (edited)");
});
it("Edit a sub notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapById(notesnook.buttons.add);
await createNotebook("Notebook 1", true);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await tapById("add-notebook-button");
await createNotebook("Sub notebook", true, true);
await device.pressBack();
await sleep(500);
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit notebook");
await sleep(500);
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
" (edited)"
);
await tapByText("Save");
await sleep(500);
await visibleByText("Sub notebook (edited)");
});
it("Add a note to notebook", async () => {
await prepare();
await navigate("Notebooks");
await tapByText("Add your first notebook");
await sleep(500);
await createNotebook("Notebook 1", true, true);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await createNote();
});
it("Remove note from Notebook", async () => {
await prepare();
await navigate("Notebooks");
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, true);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
let note = await createNote();
await elementByText(note.body).longPress();
await tapById("select-minus");
await notVisibleById(note.title);
});
it("Add/Remove note to notebook from home", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await navigate("Notes");
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-notebooks");
await sleep(500);
await tapByText("Notebook 1");
await tapByText("Save");
await sleep(300);
await visibleByText("Notebook 1");
});
it("Edit notebook title, description", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook();
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit notebook");
await sleep(500);
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
" (Edited)"
);
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).clearText();
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).typeText("Description of Notebook 1 (Edited)");
await tapByText("Save");
await sleep(500);
await visibleByText("Notebook 1 (Edited)");
await visibleByText("Description of Notebook 1 (Edited)");
});
it.skip("Move notebook to trash", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(2000);
await tapByText("Delete");
await sleep(4000);
await navigate("Trash");
await visibleByText("Notebook 1");
});
it("Move notebook to trash with notes", async () => {
await prepare();
let note = await createNote();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, true);
await sleep(500);
await tapById("listitem.select");
await tapByText("Move selected notes");
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(2000);
await tapByText("Move all notes in this notebook to trash");
await sleep(500);
await tapByText("Delete");
await sleep(4000);
await navigate("Trash");
await visibleByText("Notebook 1");
await visibleByText(note.body);
});
it("Pin notebook to side menu", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Add Shortcut");
let menu = elementById(notesnook.ids.default.header.buttons.left);
await menu.tap();
await visibleByText("Notebook 1");
});
});

View File

@@ -0,0 +1,233 @@
/*
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 { notesnook } from "../test.ids";
import { Tests } from "./utils";
describe("NOTEBOOKS", () => {
it("Create a notebook with title only", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
});
it("Create a notebook title & description", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
});
it("Create a notebook, move notes", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await Tests.fromId("listitem.select").waitAndTap();
await Tests.fromText("Move selected notes").waitAndTap();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromText(note.body).isVisible();
});
it("Add a sub notebook to a notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
await Tests.fromId("add-notebook-button").waitAndTap();
await Tests.createNotebook("Sub notebook", true);
await Tests.fromText("Sub notebook").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.fromText("Sub notebook").isNotVisible();
});
it("Remove a sub notebook from notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
await Tests.fromId("add-notebook-button").waitAndTap();
await Tests.createNotebook("Sub notebook", true);
await Tests.fromText("Sub notebook").isVisible();
});
it("Edit notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromId(notesnook.buttons.add).waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
).element.typeText(" (edited)");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1 (edited)").isVisible();
});
it("Edit a sub notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromId(notesnook.buttons.add).waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
await Tests.fromId("add-notebook-button").waitAndTap();
await Tests.createNotebook("Sub notebook", true);
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
).element.typeText(" (edited)");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Sub notebook (edited)").isVisible();
});
it("Add a note to notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.createNote();
});
it.skip("Remove note from Notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
let note = await Tests.createNote();
await Tests.fromText(note.body).element.longPress();
await Tests.fromId("select-minus").waitAndTap();
await Tests.fromId(note.title).isNotVisible();
});
it("Add/Remove note to notebook from home", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.navigate("Notes");
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-notebooks").waitAndTap();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
});
it("Edit notebook title, description", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook();
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
).element.typeText(" (Edited)");
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.description
).element.clearText();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.description
).element.typeText("Description of Notebook 1 (Edited)");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1 (Edited)").isVisible();
await Tests.fromText("Description of Notebook 1 (Edited)").isVisible();
});
it.skip("Move notebook to trash", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.navigate("Trash");
await Tests.fromText("Notebook 1").isVisible();
});
it("Move notebook to trash with notes", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await Tests.fromId("listitem.select").waitAndTap();
await Tests.fromText("Move selected notes").waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText(
"Move all notes in this notebook to trash"
).waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.navigate("Trash");
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromText(note.body).isVisible();
});
it.skip("Pin notebook to side menu", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add Shortcut").waitAndTap();
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
});
});

View File

@@ -17,29 +17,17 @@ 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 { notesnook } from "../test.ids";
import {
tapById,
visibleByText,
createNote,
prepare,
elementById,
sleep,
tapByText
} from "./utils";
import { Tests } from "./utils";
describe("Search", () => {
it("Search for a note", async () => {
await prepare();
let note = await createNote();
await tapById("icon-search");
await sleep(300);
await elementById("search-input").typeText("Test");
await sleep(1000);
await tapByText(note.body);
await sleep(1000);
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId("icon-search").waitAndTap();
await Tests.fromId("search-input").element.typeText("Test");
await Tests.fromText(note.body).waitAndTap();
await device.pressBack();
await device.pressBack();
await visibleByText(note.body);
await Tests.fromText(note.body).isVisible();
});
});

View File

@@ -1,121 +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 { notesnook } from "../test.ids";
import {
tapById,
visibleByText,
createNote,
prepare,
tapByText,
notVisibleByText,
sleep,
navigate,
createNotebook
} from "./utils";
async function sortBy(sorting, elementText = "Default") {
await tapByText(elementText);
await tapByText(sorting);
await device.pressBack();
}
describe("Sort & filter", () => {
it("Sort by date-edited/date-created", async () => {
await prepare();
let webview = web(by.id(notesnook.editor.id));
await createNote("Note 1", "Note 1");
await sleep(300);
await createNote("Note 2", "Note 2");
await sleep(300);
await tapByText("Note 1");
await sleep(500);
await expect(webview.element(by.web.className("ProseMirror"))).toExist();
await webview.element(by.web.className("ProseMirror")).tap();
await webview
.element(by.web.className("ProseMirror"))
.typeText("Edited ", true);
await device.pressBack();
await device.pressBack();
await sortBy("Date created");
await tapById(notesnook.listitem.menu);
//await visibleByText("Note 2");
await device.pressBack();
await sortBy("Date edited");
await tapById(notesnook.listitem.menu);
//await visibleByText("Edited Note 1");
await device.pressBack();
});
it("Disable grouping", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("None");
await sleep(300);
await visibleByText("None");
});
it("Group by Abc", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Abc");
await visibleByText("N");
});
it("Group by Year", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Year");
await sleep(300);
await visibleByText("Year");
});
it("Group by Week", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Week");
await sleep(300);
await visibleByText("Week");
});
it("Group by Month", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Month");
await sleep(300);
await visibleByText("Month");
});
it("Compact mode", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await tapById("icon-compact-mode");
await sleep(300);
await notVisibleByText("Note 1");
await tapById("icon-compact-mode");
await sleep(300);
await visibleByText("Note 1");
});
});

View File

@@ -0,0 +1,93 @@
/*
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 { notesnook } from "../test.ids";
import { Tests } from "./utils";
async function sortBy(sorting: string, elementText = "Default") {
await Tests.fromText(elementText).waitAndTap();
await Tests.fromText(sorting).waitAndTap();
await device.pressBack();
}
describe("Sort & filter", () => {
it("Sort by date-edited/date-created", async () => {
await Tests.prepare();
let webview = web();
await Tests.createNote("Note 1", "Note 1");
await Tests.createNote("Note 2", "Note 2");
await Tests.fromText("Note 1").waitAndTap();
await webview.element(by.web.className("ProseMirror")).tap();
await webview
.element(by.web.className("ProseMirror"))
.typeText("Edited ", true);
await device.pressBack();
await device.pressBack();
await sortBy("Date created");
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await device.pressBack();
await sortBy("Date edited");
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await device.pressBack();
});
it("Disable grouping", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("None");
await Tests.fromText("Default").isNotVisible();
});
it("Group by Abc", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Abc");
await Tests.fromText("N").isVisible();
});
it("Group by Year", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Year");
await Tests.fromText("Year").isVisible();
});
it("Group by Week", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Week");
await Tests.fromText("Week").isVisible();
});
it("Group by Month", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Month");
await Tests.fromText("Month").isVisible();
});
it("Compact mode", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await Tests.fromId("icon-compact-mode").waitAndTap();
await Tests.fromText("Note 1").isNotVisible();
await Tests.fromId("icon-compact-mode").waitAndTap();
await Tests.fromText("Note 1").isVisible();
});
});

View File

@@ -1,103 +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 { notesnook } from "../test.ids";
import {
navigate,
tapById,
visibleByText,
createNote,
prepare,
elementById,
tapByText,
notVisibleByText,
sleep
} from "./utils";
describe("Tags", () => {
it("Tag a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await device.pressBack();
await device.pressBack();
await navigate("Tags");
await tapByText("#testtag");
await visibleByText(note.body);
});
it("Untag a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await tapByText("#testtag");
await device.pressBack();
await device.pressBack();
await notVisibleByText("#testtag");
});
it("Creat shortcut of a tag", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await device.pressBack();
await device.pressBack();
await navigate("Tags");
await sleep(500);
await tapById(notesnook.ids.tag.menu);
await sleep(500);
await tapByText("Add Shortcut");
let menu = elementById(notesnook.ids.default.header.buttons.left);
await menu.tap();
await visibleByText("#testtag");
});
it("Rename a tag", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await device.pressBack();
await device.pressBack();
await navigate("Tags");
await tapById(notesnook.ids.tag.menu);
await sleep(500);
await tapByText("Rename tag");
await sleep(500);
await elementById("input-value").clearText();
await elementById("input-value").typeText("testtag_edited");
await tapByText("Save");
await visibleByText("#testtag_edited");
});
});

View File

@@ -0,0 +1,95 @@
/*
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 { notesnook } from "../test.ids";
import { Tests } from "./utils";
describe("Tags", () => {
it("Tag a note", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.navigate("Tags");
await Tests.fromText("#testtag").waitAndTap();
await Tests.fromText(note.body).isVisible();
});
it("Untag a note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await Tests.fromText("#testtag").waitAndTap();
await device.pressBack();
await device.pressBack();
await Tests.fromText("#testtag").isNotVisible();
});
it("Create shortcut of a tag", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.navigate("Tags");
await Tests.fromId(notesnook.ids.tag.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add shortcut").waitAndTap();
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
await Tests.fromText("testtag").isVisible();
});
it("Rename a tag", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.navigate("Tags");
await Tests.fromId(notesnook.ids.tag.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Rename").waitAndTap();
await Tests.sleep(100);
await Tests.fromId("input-value").element.clearText();
await Tests.fromId("input-value").element.typeText("testtag_edited");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("#testtag_edited").isVisible();
});
});

View File

@@ -1,166 +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 { notesnook } from "../test.ids";
import { readFileSync } from "fs";
import { expect as jestExpect } from "@jest/globals";
import { toMatchImageSnapshot } from "jest-image-snapshot";
import { device as _device } from "detox";
jestExpect.extend({ toMatchImageSnapshot });
const sleep = (duration) =>
new Promise((resolve) =>
setTimeout(() => {
resolve();
}, duration)
);
async function LaunchApp() {
await waitFor(element(by.id(notesnook.ids.default.root)))
.toBeVisible()
.withTimeout(500);
}
function elementById(id) {
return element(by.id(id)).atIndex(0);
}
function elementByText(text) {
return element(by.text(text)).atIndex(0);
}
async function tapById(id) {
await elementById(id).tap();
}
async function tapByText(text) {
await elementByText(text).tap();
}
async function visibleByText(text) {
await expect(elementByText(text)).toBeVisible();
}
async function visibleById(id) {
await expect(elementById(id)).toBeVisible();
}
async function notVisibleById(id) {
await expect(elementById(id)).not.toBeVisible();
}
async function notVisibleByText(text) {
await expect(elementByText(text)).not.toBeVisible();
}
async function exitEditor() {
await _device.pressBack();
await _device.pressBack();
}
async function createNote(_title, _body) {
let title = _title || "Test note description that ";
let body =
_body ||
"Test note description that is very long and should not fit in text.";
await tapById(notesnook.buttons.add);
let webview = web(by.id(notesnook.editor.id));
await expect(webview.element(by.web.className("ProseMirror"))).toExist();
await webview.element(by.web.className("ProseMirror")).tap();
await webview.element(by.web.className("ProseMirror")).typeText(body, true);
await exitEditor();
await waitFor(element(by.text(body)))
.toBeVisible()
.withTimeout(500);
return { title, body };
}
async function openSideMenu() {
let menu = elementById(notesnook.ids.default.header.buttons.left);
await menu.tap();
}
async function navigate(screen) {
let menu = elementById(notesnook.ids.default.header.buttons.left);
await waitFor(menu).toBeVisible().withTimeout(300);
await menu.tap();
await waitFor(elementByText(screen)).toBeVisible().withTimeout(300);
await elementByText(screen).tap();
}
const testvars = {
isFirstTest: true
};
async function prepare() {
await device.disableSynchronization();
if (testvars.isFirstTest) {
testvars.isFirstTest = false;
return await LaunchApp();
}
await device.reverseTcpPort(8081);
await device.uninstallApp();
await device.installApp();
await device.launchApp({ newInstance: true });
await LaunchApp();
}
async function matchSnapshot(element, name) {
let path = await element.takeScreenshot(name);
const bitmapBuffer = readFileSync(path);
jestExpect(bitmapBuffer).toMatchImageSnapshot({
failureThreshold: 200,
failureThresholdType: "pixel"
});
}
async function createNotebook(title = "Notebook 1", description = true) {
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
title
);
if (description) {
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).typeText(`Description of ${title}`);
}
await tapByText("Add");
await sleep(300);
}
export {
matchSnapshot,
createNotebook,
prepare,
LaunchApp,
createNote,
navigate,
openSideMenu,
notVisibleById,
notVisibleByText,
visibleById,
visibleByText,
tapById,
tapByText,
elementByText,
elementById,
sleep,
exitEditor
};

View File

@@ -0,0 +1,149 @@
/*
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 { expect as jestExpect } from "@jest/globals";
import { device as _device, expect } from "detox";
import { readFileSync } from "fs";
import { toMatchImageSnapshot } from "jest-image-snapshot";
import type { RouteName } from "../../app/stores/use-navigation-store";
import { notesnook } from "../test.ids";
jestExpect.extend({ toMatchImageSnapshot });
const testvars = {
isFirstTest: true
};
class Element {
element: Detox.NativeElement;
constructor(public type: "id" | "text", public value: string) {
if (type == "id") {
this.element = element(by.id(value)).atIndex(0);
} else {
this.element = element(by.text(value)).atIndex(0);
}
}
isVisible(timeout?: number) {
return waitFor(this.element)
.toBeVisible()
.withTimeout(timeout || 500);
}
isNotVisible(timeout?: number) {
return waitFor(this.element)
.not.toBeVisible()
.withTimeout(timeout || 500);
}
async waitAndTap(timeout?: number) {
await waitFor(this.element)
.toBeVisible()
.withTimeout(timeout || 500);
await this.element.tap();
}
tap(point?: Detox.Point2D): Promise<void> {
return this.element.tap(point);
}
static fromId(id: string) {
return new Element("id", id);
}
static fromText(text: string) {
return new Element("text", text);
}
}
const Tests = {
awaitLaunch: async () => {
await waitFor(element(by.id(notesnook.ids.default.root)))
.toBeVisible()
.withTimeout(500);
},
sleep: (duration: number) => {
return new Promise((resolve) =>
setTimeout(() => {
resolve(undefined);
}, duration)
);
},
fromId: Element.fromId,
fromText: Element.fromText,
async exitEditor() {
await _device.pressBack();
await _device.pressBack();
},
async createNote(_title?: string, _body?: string) {
let title = _title || "Test note description that ";
let body =
_body ||
"Test note description that is very long and should not fit in text.";
await Tests.fromId(notesnook.buttons.add).tap();
await expect(web().element(by.web.className("ProseMirror"))).toExist();
// await web().element(by.web.className("ProseMirror")).tap();
await web().element(by.web.className("ProseMirror")).typeText(body, true);
await Tests.exitEditor();
await Tests.fromText(body).isVisible();
return { title, body };
},
async navigate(screen: RouteName | ({} & string)) {
let menu = Tests.fromId(notesnook.ids.default.header.buttons.left);
await menu.waitAndTap();
await Tests.fromText(screen).waitAndTap();
},
async openSideMenu() {
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
},
async prepare() {
await device.disableSynchronization();
if (testvars.isFirstTest) {
testvars.isFirstTest = false;
return await Tests.awaitLaunch();
}
await device.reverseTcpPort(8081);
await device.uninstallApp();
await device.installApp();
await device.launchApp({ newInstance: true });
await Tests.awaitLaunch();
},
async createNotebook(title = "Notebook 1", description = true) {
await Tests.sleep(1000);
const titleInput = Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
);
await titleInput.isVisible();
await titleInput.element.typeText(title);
if (description) {
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.description
).element.typeText(`Description of ${title}`);
}
await Tests.fromText("Add").waitAndTap();
},
async matchSnapshot(element: Element, name: string) {
let path = await element.element.takeScreenshot(name);
const bitmapBuffer = readFileSync(path);
(jestExpect(bitmapBuffer) as any).toMatchImageSnapshot({
failureThreshold: 200,
failureThresholdType: "pixel"
});
}
};
export { Element, Tests };

View File

@@ -1,145 +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 { notesnook } from "../test.ids";
import {
tapById,
elementById,
visibleByText,
tapByText,
createNote,
prepare,
visibleById,
matchSnapshot,
notVisibleById,
navigate
} from "./utils";
import { sleep } from "./utils";
async function lockNote() {
await tapById(notesnook.listitem.menu);
await tapById("icon-lock-unlock");
await sleep(1000);
await visibleByText("Lock");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await elementById(notesnook.ids.dialogs.vault.pwdAlt).typeText("1234");
await tapByText("Lock");
await sleep(500);
await visibleById("note-locked-icon");
}
async function removeFromVault() {
await tapById(notesnook.listitem.menu);
await tapById("icon-lock-unlock");
await sleep(1000);
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await tapByText("Unlock");
await sleep(1000);
await notVisibleById("note-locked-icon");
}
async function openLockedNote(pwd) {
await tapById(notesnook.ids.note.get(1));
await sleep(1000);
await visibleByText("Open");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText(pwd || "1234");
await tapByText("Open");
await sleep(3000);
await matchSnapshot(elementById("editor-wrapper"), "note-after-vault-unlock");
}
async function goToPrivacySecuritySettings() {
await navigate("Settings");
await tapByText("Vault");
await sleep(500);
}
describe("VAULT", () => {
it("Create vault from settings", async () => {
await prepare();
await goToPrivacySecuritySettings();
await tapByText("Create vault");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await elementById(notesnook.ids.dialogs.vault.pwdAlt).typeText("1234");
await tapByText("Create");
await sleep(500);
await visibleByText("Clear vault");
});
it("Change vault password", async () => {
await prepare();
await createNote();
await lockNote();
await goToPrivacySecuritySettings();
await tapByText("Change vault password");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await elementById(notesnook.ids.dialogs.vault.changePwd).typeText("2362");
await tapByText("Change");
await sleep(2000);
await device.pressBack();
await device.pressBack();
await device.pressBack();
await openLockedNote("2362");
});
it("Delete vault", async () => {
await prepare();
await createNote();
await lockNote();
await goToPrivacySecuritySettings();
await tapByText("Delete vault");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await tapByText("Delete");
await sleep(500);
await visibleByText("Create vault");
await device.pressBack();
await device.pressBack();
await visibleById(notesnook.listitem.menu);
});
it("Delete vault with locked notes", async () => {
await prepare();
await createNote();
await lockNote();
await goToPrivacySecuritySettings();
await tapByText("Delete vault");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await tapByText("Delete all notes");
await tapByText("Delete");
await sleep(500);
await visibleByText("Create vault");
await device.pressBack();
await device.pressBack();
await notVisibleById(notesnook.listitem.menu);
});
it("Add a note to vault", async () => {
await prepare();
await createNote();
await lockNote();
await openLockedNote();
});
it("Remove note from vault", async () => {
await prepare();
await createNote();
await lockNote();
await removeFromVault();
});
});

View File

@@ -0,0 +1,146 @@
/*
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 { expect } from "detox";
import { notesnook } from "../test.ids";
import { Tests } from "./utils";
async function lockNote() {
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-lock-unlock").waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Lock").isVisible();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
"1234"
);
await Tests.fromText("Lock").waitAndTap();
await Tests.fromId("note-locked-icon").isVisible();
}
async function removeFromVault() {
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-lock-unlock").waitAndTap();
await Tests.sleep(500);
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
await Tests.fromText("Unlock").waitAndTap();
await Tests.fromId("note-locked-icon").isNotVisible();
}
async function openLockedNote(pwd?: string) {
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
await Tests.sleep(500);
await web()
.element(by.web.name("password"))
.typeText(pwd || "1234", false);
await web().element(by.web.className("unlock-note")).tap();
await Tests.sleep(500);
await expect(web().element(by.web.className("unlock-note"))).not.toExist();
}
async function goToPrivacySecuritySettings() {
await Tests.navigate("Settings");
await Tests.sleep(300);
await Tests.fromText("Vault").waitAndTap();
}
describe("VAULT", () => {
it("Create vault from settings", async () => {
await Tests.prepare();
await goToPrivacySecuritySettings();
await Tests.fromText("Create vault").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
"1234"
);
await Tests.fromText("Create").waitAndTap();
await Tests.fromText("Clear vault").isVisible();
});
it("Change vault password", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await goToPrivacySecuritySettings();
await Tests.fromText("Change vault password").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromId(notesnook.ids.dialogs.vault.changePwd).element.typeText(
"2362"
);
await Tests.fromText("Change").waitAndTap();
await device.pressBack();
await device.pressBack();
await Tests.sleep(500);
await openLockedNote("2362");
});
it("Delete vault", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await goToPrivacySecuritySettings();
await Tests.fromText("Delete vault").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromText("Delete").waitAndTap();
await Tests.sleep(300);
await Tests.fromText("Create vault").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.fromId(notesnook.listitem.menu).isVisible();
});
it("Delete vault with locked notes", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await goToPrivacySecuritySettings();
await Tests.fromText("Delete vault").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromText("Delete notes in this vault").waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.sleep(300);
await Tests.fromText("Create vault").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.fromId(notesnook.listitem.menu).isNotVisible();
});
it("Add a note to vault", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await openLockedNote();
});
it("Remove note from vault", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await removeFromVault();
});
});

View File

@@ -52,7 +52,7 @@ module.exports = {
emulator: {
type: 'android.emulator',
device: {
avdName: 'Pixel_5_API_31'
avdName: 'Pixel_5_API_34'
}
}
},

View File

@@ -116,7 +116,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3028
versionCode 3031
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
@@ -164,10 +164,7 @@ android {
}
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst '**/libc++_shared.so'
jniLibs {
useLegacyPackaging isBuildingAAB == false
}

View File

@@ -12,7 +12,7 @@ buildscript {
androidXCoreVersion = "1.7.0"
androidXCore = "1.7.0"
androidXBrowser = "1.0.0"
ndkVersion = "23.1.7779620"
ndkVersion = "23.2.8568313"
}
repositories {

View File

@@ -16,6 +16,7 @@ org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# org.gradle.configuration-cache=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK

View File

@@ -1,8 +1,9 @@
- Fix random crashes on system startup and when app enter background
- Attachments manager now shows progress for uploading/downloading/checking attachments
- Attachments manager has two new tabs, Errors and Orphaned, to help you manage your attachments
- New and improved toast message design
- Fix statusbar flicker on app launch
- Many other bug fixes and small improvements
- Added full support for localization in Notesnook
- Improved search experience
- Allow user to cancel logging in
- Fixed scrolling focused line into view
- Support self hosted monograph server
- Fixed markdown link pasting in editor
- Many other bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -18,10 +18,10 @@ import { i18n } from "@lingui/core";
import { ScriptManager, Script } from '@callstack/repack/client';
import {
messages as $en
} from "@notesnook/intl/locales/$en.json";
} from "@notesnook/intl/dist/locales/$en.json";
import {
messages as $pseudo
} from "@notesnook/intl/locales/$pseudo-LOCALE.json";
} from "@notesnook/intl/dist/locales/$pseudo-LOCALE.json";
i18n.load({
en: __DEV__ ? $pseudo : $en
@@ -30,6 +30,17 @@ setI18nGlobal(i18n);
i18n.activate("en");
setI18nGlobal(i18n);
if (global.__DEV__) {
const err = console.error;
console.error = function (message) {
if (typeof message === 'string' && message.includes('VirtualizedLists should never be')) {
err.apply(console, ["VirtualizedLists should never be nested inside plain ScrollViews"]);
return;
}
err.apply(console, arguments);
};
}
try {
ScriptManager.shared.addResolver(async (scriptId) => {

View File

@@ -112,7 +112,7 @@
2D02E4901E0B4A5D006451C7 /* Notesnook-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Notesnook-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
41D57E9831C724F59F18FBAE /* libPods-Notesnook-Add to Notes.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Notesnook-Add to Notes.a"; sourceTree = BUILT_PRODUCTS_DIR; };
47AE097A087A651BD60CAD7B /* Pods-Notesnook-Make Note.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Notesnook-Make Note.release.xcconfig"; path = "Target Support Files/Pods-Notesnook-Make Note/Pods-Notesnook-Make Note.release.xcconfig"; sourceTree = "<group>"; };
4B679E87FDFB1312CAD6CF45 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Notesnook/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
4B679E87FDFB1312CAD6CF45 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Notesnook/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
4BD34AE0C0FD2416E48803B8 /* libPods-Notesnook.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Notesnook.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4C4AF691C324D95337F2FB0A /* libPods-Make Note.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Make Note.a"; sourceTree = BUILT_PRODUCTS_DIR; };
6510E6D62877215700DACAA9 /* build.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = build.bundle; path = "../../../../packages/editor-mobile/build.bundle"; sourceTree = "<group>"; };
@@ -124,6 +124,7 @@
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; };
@@ -152,7 +153,7 @@
88211800FA8BF60638327ADE /* Pods-Make Note.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Make Note.debug.xcconfig"; path = "Target Support Files/Pods-Make Note/Pods-Make Note.debug.xcconfig"; sourceTree = "<group>"; };
A03E3F5851D27269540E147C /* libPods-Notesnook-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Notesnook-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A84F3F0D50641D1CD4569B0E /* Pods-Make Note.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Make Note.release.xcconfig"; path = "Target Support Files/Pods-Make Note/Pods-Make Note.release.xcconfig"; sourceTree = "<group>"; };
B5216B66C0B4A91C0A69128B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Notesnook/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
B5216B66C0B4A91C0A69128B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Notesnook/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
B9989E5C777BB11343A62D09 /* Pods-Notesnook-NotesnookTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Notesnook-NotesnookTests.debug.xcconfig"; path = "Target Support Files/Pods-Notesnook-NotesnookTests/Pods-Notesnook-NotesnookTests.debug.xcconfig"; sourceTree = "<group>"; };
D1710CA05FED221CC56D2BE1 /* Pods-Notesnook-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Notesnook-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Notesnook-tvOSTests/Pods-Notesnook-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@@ -255,6 +256,7 @@
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */,
6515C42E2580AA2F00E83E39 /* StoreKit.framework */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
@@ -1061,7 +1063,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2115;
CURRENT_PROJECT_VERSION = 2118;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1135,7 +1137,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.18;
MARKETING_VERSION = 3.0.21;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1166,7 +1168,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2115;
CURRENT_PROJECT_VERSION = 2118;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1240,7 +1242,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.18;
MARKETING_VERSION = 3.0.21;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1399,7 +1401,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2115;
CURRENT_PROJECT_VERSION = 2118;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1411,7 +1413,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.18;
MARKETING_VERSION = 3.0.21;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1442,7 +1444,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2115;
CURRENT_PROJECT_VERSION = 2118;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1455,7 +1457,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.18;
MARKETING_VERSION = 3.0.21;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1485,7 +1487,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2115;
CURRENT_PROJECT_VERSION = 2118;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1559,7 +1561,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.18;
MARKETING_VERSION = 3.0.21;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1590,7 +1592,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2115;
CURRENT_PROJECT_VERSION = 2118;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1665,7 +1667,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.18;
MARKETING_VERSION = 3.0.21;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1742,10 +1744,7 @@
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
@@ -1809,10 +1808,7 @@
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;

View File

@@ -113,7 +113,7 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "react-native-quick-sqlite" then
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = 'SQLITE_ENABLE_FTS5=1 SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT=1 HAVE_CIPHER_AES_128_CBC=0 HAVE_CIPHER_AES_256_CBC=0 HAVE_CIPHER_SQLCIPHER=0 HAVE_CIPHER_RC4=0 HAVE_CIPHER_CHACHA20=1 SQLITE_OMIT_PROGRESS_CALLBACK=1 SQLITE_MAX_EXPR_DEPTH=0 SQLITE_OMIT_DEPRECATED=1 SQLITE_LIKE_DOESNT_MATCH_BLOBS=1 SQLITE_DQS=0 SQLITE_DEFAULT_MEMSTATUS=0 SQLITE_USE_ALLOCA=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = 'SQLITE_CORE=1 SQLITE_ENABLE_FTS5=1 SQLITE3MC_OMIT_AES_HARDWARE_SUPPORT=1 HAVE_CIPHER_AES_128_CBC=0 HAVE_CIPHER_AES_256_CBC=0 HAVE_CIPHER_SQLCIPHER=0 HAVE_CIPHER_RC4=0 HAVE_CIPHER_CHACHA20=1 SQLITE_OMIT_PROGRESS_CALLBACK=1 SQLITE_MAX_EXPR_DEPTH=0 SQLITE_OMIT_DEPRECATED=1 SQLITE_LIKE_DOESNT_MATCH_BLOBS=1 SQLITE_DQS=0 SQLITE_DEFAULT_MEMSTATUS=0 SQLITE_USE_ALLOCA=1'
end
end
end

View File

@@ -1017,9 +1017,9 @@ PODS:
- react-native-screenguard (1.0.0):
- React-Core
- SDWebImage (~> 5.11.1)
- react-native-share-extension (3.0.0):
- react-native-share-extension (2.6.0):
- React
- react-native-sodium (1.5.4):
- react-native-sodium (1.5.6):
- React
- react-native-theme-switch-animation (0.6.0):
- DoubleConversion
@@ -1303,7 +1303,7 @@ PODS:
- React-Core
- RNCMaskedView (0.2.9):
- React-Core
- RNDateTimePicker (6.6.0):
- RNDateTimePicker (8.2.0):
- React-Core
- RNDeviceInfo (8.7.1):
- React-Core
@@ -1849,11 +1849,11 @@ SPEC CHECKSUMS:
react-native-notification-sounds: da78c828fe1bcbb92d8b505d5261890ed315ff39
react-native-orientation: f1caf84d65f1a4fd4511a18f2b924e634ad7a628
react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa
react-native-quick-sqlite: e0e23b749382a85e4b57146f753de737a6c3a9e1
react-native-quick-sqlite: 18e1367c34faac90e37f6eb3e78c196e9b674b5d
react-native-safe-area-context: b7daa1a8df36095a032dff095a1ea8963cb48371
react-native-screenguard: 8b36a3df84c76cd2b82c477f71c26fa1c8cc14a0
react-native-share-extension: faed334b1ddf165f1e576fcabd3dc1c9e748bfa9
react-native-sodium: 955bb0dc3ea05f8ea06d5e96cb89d1be7b5d7681
react-native-share-extension: 25437eb1039f7409be6e80a7edf8d02b42e1dc99
react-native-sodium: 605c1523ec8ff5fbff5e9e7769bbacceb571a3c6
react-native-theme-switch-animation: d3eb50365a3829ce5572628888fa514752703f61
react-native-webview: 553abd09f58e340fdc7746c9e2ae096839e99911
React-nativeconfig: ba9a2e54e2f0882cf7882698825052793ed4c851
@@ -1884,7 +1884,7 @@ SPEC CHECKSUMS:
RNCCheckbox: 43bcc6493611468af0e19f19f029dab3da8561c4
RNCClipboard: 3f0451a8100393908bea5c5c5b16f96d45f30bfc
RNCMaskedView: 949696f25ec596bfc697fc88e6f95cf0c79669b6
RNDateTimePicker: 818672afa85519722533d017b832ed09539d9ddb
RNDateTimePicker: 40ffda97d071a98a10fdca4fa97e3977102ccd14
RNDeviceInfo: aad3c663b25752a52bf8fce93f2354001dd185aa
RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
@@ -1901,7 +1901,7 @@ SPEC CHECKSUMS:
RNShare: a5dc3b9c53ddc73e155b8cd9a94c70c91913c43c
RNSVG: d7d7bc8229af3842c9cfc3a723c815a52cdd1105
RNTooltips: 5424d4bf0b3d441104127943b1115cc7f0616b1f
RNZipArchive: 68a0c6db4b1c103f846f1559622050df254a3ade
RNZipArchive: 9d90367b1573903b5561a8c0b911e5cc12626e74
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
@@ -1911,6 +1911,6 @@ SPEC CHECKSUMS:
toolbar-android: 2a73856e98b750d7e71ce4644d3f41cc98211719
Yoga: 2246eea72aaf1b816a68a35e6e4b74563653ae09
PODFILE CHECKSUM: 98f600b3236e314680588e77819982573fb4d9f7
PODFILE CHECKSUM: aa07aca16c1d92394d8ce12f2c5fcaa8a3ae4997
COCOAPODS: 1.15.2

View File

@@ -10,13 +10,10 @@
},
"dependencies": {
"@ammarahmed/notifee-react-native": "7.4.7",
"@ammarahmed/react-native-share-extension": "^3.0.0",
"@ammarahmed/react-native-sodium": "1.5.4",
"@bam.tech/react-native-image-resizer": "3.0.5",
"@callstack/repack": "^4.1.1",
"@react-native-clipboard/clipboard": "^1.9.0",
"@react-native-community/checkbox": "^0.5.8",
"@react-native-community/datetimepicker": "6.6.0",
"@react-native-community/netinfo": "^9.3.7",
"@react-native-community/toolbar-android": "^0.2.1",
"@react-native-masked-view/masked-view": "^0.2.6",
@@ -52,7 +49,6 @@
"react-native-iap": "12.15.1",
"react-native-in-app-review": "4.3.3",
"react-native-keychain": "4.0.5",
"react-native-mmkv-storage": "^0.10.2",
"react-native-modal-datetime-picker": "14.0.0",
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
@@ -73,7 +69,12 @@
"react-native-url-polyfill": "^2.0.0",
"react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14"
"@formatjs/intl-pluralrules": "5.2.14",
"detox": "^20.27.6",
"@ammarahmed/react-native-share-extension": "^2.6.0",
"@ammarahmed/react-native-sodium": "1.5.6",
"react-native-mmkv-storage": "^0.10.2",
"@react-native-community/datetimepicker": "^8.2.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
@@ -86,7 +87,6 @@
"@react-native/metro-config": "0.74.86",
"@tsconfig/react-native": "^3.0.2",
"@types/html-to-text": "^8.0.1",
"@types/jest": "^29.2.1",
"@types/metro-config": "^0.76.3",
"@types/react": "^18.2.6",
"@types/react-native": "^0.69.1",
@@ -98,19 +98,13 @@
"babel-loader": "^8.2.5",
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-transform-remove-console": "6.9.4",
"detox": "^20.11.2",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-native": "^4.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"expect": "^29.6.2",
"jest": "^29.6.3",
"jest-circus": "^29.6.2",
"jest-image-snapshot": "^6.2.0",
"@react-native/babel-preset": "0.74.86",
"pixelmatch": "^5.3.0",
"prettier": "2.8.8",
"react-native-actions-shortcuts": "^1.0.1",
"react-native-bundle-visualizer": "^3.1.1",
@@ -118,10 +112,16 @@
"react-refresh": "0.14.0",
"react-test-renderer": "18.2.0",
"terser-webpack-plugin": "^5.3.5",
"ts-jest": "^29.1.1",
"webpack-cli": "^5.1.4",
"webpack": "^5.88.2",
"metro-react-native-babel-preset": "0.77.0",
"acorn-import-attributes": "1.9.5"
"acorn-import-attributes": "1.9.5",
"expect": "^29.7.0",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
"jest-image-snapshot": "^6.4.0",
"ts-jest": "^29.2.5",
"pixelmatch": "^6.0.0",
"@types/jest": "^29.5.14"
}
}

View File

@@ -2,7 +2,7 @@ const PerformanceNow =
(global.performance && global.performance.now) ||
global.performanceNow ||
global.nativePerformanceNow || (() => { try {
var now = require('fbjs/lib/performanceNow')
var now = __DEV__ ? require('fbjs/lib/performanceNow') : () => Date.now()
} finally { return now }})();
const DEFAULT_LABEL = 'default';

View File

@@ -217,6 +217,7 @@ module.exports = (env) => {
/node_modules(.*[/\\])+@tanstack[/\\]react-query/,
/node_modules(.*[/\\])+@trpc[/\\]react-query/,
/node_modules(.*[/\\])+katex/,
/node_modules(.*[/\\])+mime/,
/node_modules(.*[/\\])+@notesnook[/\\]core/,
/node_modules(.*[/\\])+whatwg-url-without-unicode/,
/node_modules(.*[/\\])+whatwg-url/,

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.0.18",
"version": "3.0.21",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -46,7 +46,6 @@
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-actions-sheet": "^0.9.7",
"react-native-mmkv-storage": "^0.10.2"
"react-native-actions-sheet": "^0.9.7"
}
}

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