Compare commits

...

122 Commits

Author SHA1 Message Date
Ammar Ahmed
8006914cd8 Merge branch 'master' into fix-db-init-background
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2023-04-15 23:22:20 +05:00
Muhammad Ali
d99ccbc243 editor: fix jumping text/bg color popup (#2306)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-04-15 17:53:01 +05:00
Abdulrehman-Jafer
0f191eba5c editor: fix code block highlighting not updating in some cases (#2267)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
2023-04-15 17:50:03 +05:00
Ammar Ahmed
26657c7ee3 mobile: fix app font sizes (#2355) 2023-04-15 17:01:49 +05:00
Abdulrehman-Jafer
a12749eb82 core: disable input sanitization on color/tag rename (#2274)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-04-15 16:42:25 +05:00
Abdulrehman-Jafer
1a56d38116 web: migrate use-database.js to TypeScript (#2287)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
2023-04-15 16:41:14 +05:00
Ammar Ahmed
0b9025af11 mobile: redesign reminder/notebook sheets (#2321) 2023-04-15 16:40:36 +05:00
Muhammad Ali
6f5a44cd11 web: do not show success toast if notebook failed to create (#2307)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-04-15 16:37:32 +05:00
Abdulrehman-Jafer
5e1cc307b1 web: migrate use-media-query.js to Typescript (#2326)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-04-15 16:35:03 +05:00
Ammar Ahmed
2197f1ec5e mobile: fix tag suggestions in share widget (#2354) 2023-04-15 16:31:59 +05:00
NedimHodzic
1da5fd3661 docs: fix grammar issues in mobile/web READMEs (#2345)
Signed-off-by: Nedim Hodzic hodzicnedim11@gmail.com
2023-04-15 05:22:18 +05:00
ammarahm-ed
49e79b52b2 core: add remote note if local note does not exist 2023-04-15 02:17:56 +05:00
ammarahm-ed
c9a657298c mobile: fix crash when data is null in list 2023-04-15 02:03:36 +05:00
Nick Thomas
cfd7caf16c Updated grammar readme
docs: Updated grammar readme
Signed-off-by: Nick Thomas nick02@iastate.edu

Signed-off-by: Nick Thomas <73144044+NickThomas20@users.noreply.github.com>
2023-04-14 06:42:57 +05:00
ammarahm-ed
cc2706a39e mobile: cleanup 2023-04-11 00:38:17 +05:00
ammarahm-ed
ea3c480edb mobile: improve db init in app/share extension 2023-04-11 00:37:50 +05:00
ammarahm-ed
abbcc1263a mobile: don't call db.init on logout 2023-04-11 00:18:50 +05:00
ammarahm-ed
c77e29d7aa mobile: fix db init in background 2023-04-10 15:21:26 +05:00
ammarahm-ed
ba68adbeb1 core: init collections separately 2023-04-10 15:20:27 +05:00
ammarahm-ed
efad6520a2 mobile: release 2.4.12 2023-04-07 22:46:56 +05:00
ammarahm-ed
cc58351731 core: fix notes marked as local only get deleted
on sync
2023-04-07 22:40:10 +05:00
ammarahm-ed
cf2f4c4bbb mobile: fix tag not updated on list item on rename 2023-04-07 14:46:28 +05:00
Abdulrehman-Jafer
cc161b844c web: migrate use-is-user-premium to ts (#2297)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-04-07 14:10:52 +05:00
ammarahm-ed
45ebf0670f mobile: show error when note is locked 2023-04-07 02:22:49 +05:00
ammarahm-ed
9cfaa90232 mobile: use text exporter from core 2023-04-07 02:22:49 +05:00
ammarahm-ed
edf4a248ed web: remove unnecessary param passed to export 2023-04-07 02:22:49 +05:00
ammarahm-ed
14d7ec6b4e core: allow exports with custom content 2023-04-07 02:22:49 +05:00
Ammar Ahmed
cd2ca94c3d mobile: Fix crash when leaving note after search (#2289) 2023-04-07 01:10:52 +05:00
Abdullah Atta
f1609aaa46 editor: do not convert \n characters to paragraphs 2023-04-06 01:40:55 +05:00
ammarahm-ed
851364c0bf mobile: release 2.4.11 2023-04-05 02:45:02 +05:00
ammarahm-ed
913a1743b5 mobile: update deps 2023-04-05 02:13:49 +05:00
ammarahm-ed
dc5572d4b9 editor: enable passing custom sessionId 2023-04-05 02:13:49 +05:00
ammarahm-ed
554560903c mobile: improve share extension search 2023-04-05 02:13:49 +05:00
ammarahm-ed
885e4a325c mobile: fix snap points for sheet on android 2023-04-05 02:13:49 +05:00
ammarahm-ed
156ce7e711 mobile: ensure note is saved when change note 2023-04-05 02:13:49 +05:00
Abdullah Atta
71ecc05bd6 web: bump version to v2.4.7 2023-04-05 01:54:51 +05:00
Abdullah Atta
98b93d42c1 theme: publish 2023-04-05 01:53:29 +05:00
Abdullah Atta
2e7935cacc editor: publish 2023-04-05 01:53:16 +05:00
Abdullah Atta
50d64af7f6 web: update export html test snapshots 2023-04-04 23:35:35 +05:00
Abdullah Atta
e452fd3d2f core: update export tests snapshots 2023-04-04 23:16:04 +05:00
Abdullah Atta
64dd481402 web: fix cannot read property id of undefined 2023-04-04 22:04:46 +05:00
Abdullah Atta
28f647fa06 web: fix a couple of state mgmt issues with move note dialog 2023-04-04 22:04:14 +05:00
ammarahm-ed
9eb1061cd1 mobile: refactor 2023-04-04 22:04:14 +05:00
ammarahm-ed
3cab264213 mobile: increase the timeout 2023-04-04 22:04:14 +05:00
ammarahm-ed
fb56e7c866 mobile: exit current topic if it is deleted. 2023-04-04 22:04:14 +05:00
ammarahm-ed
234e368c4d mobile: cleanup 2023-04-04 22:04:14 +05:00
ammarahm-ed
4de4693505 mobile: fix topic sheet snaps to top in topics screen 2023-04-04 22:04:14 +05:00
ammarahm-ed
5563c7d5f8 mobile: remove floating button on topic screen 2023-04-04 22:04:14 +05:00
ammarahm-ed
93225e5a30 mobile: fix topic sheet related state issues 2023-04-04 22:04:14 +05:00
ammarahm-ed
0ab9846732 mobile: fix edit notebook sheet does not close after saving edits 2023-04-04 22:04:14 +05:00
ammarahm-ed
f6f1cf0ae7 mobile: fix edit notebook button not working 2023-04-04 22:04:14 +05:00
ammarahm-ed
69615d54db mobile: remember last position of sheet in notebook 2023-04-04 22:04:14 +05:00
ammarahm-ed
a990d0f404 mobile: fix move note sheet not opening 2023-04-04 22:04:14 +05:00
ammarahm-ed
4aeff48e74 mobile: cleanup 2023-04-04 22:04:14 +05:00
ammarahm-ed
78c1a75ec9 mobile: improve opening sheet 2023-04-04 22:04:14 +05:00
ammarahm-ed
9e6664a80e mobile: improve topics-sheet ux 2023-04-04 22:04:14 +05:00
ammarahm-ed
b306a09747 editor: refresh editor on line spacing changed 2023-04-04 17:23:27 +05:00
ammarahm-ed
fab52012ad mobile: fix loading indicator color 2023-04-04 16:26:07 +05:00
ammarahm-ed
fc39c714b6 mobile: update project pods 2023-04-04 16:26:07 +05:00
ammarahm-ed
5fff4bbb0e mobile: fix font sizes 2023-04-04 16:26:07 +05:00
ammarahm-ed
0a29571ad8 mobile: upgrade share extension to support organization 2023-04-04 16:26:07 +05:00
ammarahm-ed
275fa1b3ca mobile: fix loading editor screen on app launch 2023-04-04 16:25:07 +05:00
ammarahm-ed
79889665e5 mobile: cleanup deps 2023-04-04 16:25:07 +05:00
ammarahm-ed
9888ad327c mobile: fix editor hidden when changing orientation
from landscape to
portrait
2023-04-04 16:25:07 +05:00
ammarahm-ed
77ab2d7740 mobile: skip showing loading overlay for relatively smaller
notes
2023-04-04 16:25:07 +05:00
ammarahm-ed
509dc4b541 editor: scroll to top on note change 2023-04-04 16:25:07 +05:00
ammarahm-ed
ff84ee2d42 mobile: improve note saving reliability &
performance
2023-04-04 16:25:07 +05:00
ammarahm-ed
d838c2e8ad editor: update word count immediately after note is loaded
Previously when opening note, the word count on mobile would
be 0, then update to actual word count. This has been fixed so
correct word count shows when opening not without any
delay.
2023-04-04 16:25:07 +05:00
ammarahm-ed
28d718acf6 mobile: show no loading for relatively smaller notes
Previously if a note had smaller content, loading would still take
quite a bit long which seemed unnecessary. Now loading will show
only  if note takes long enough to load which should make the app
feel much more snappier than before when opening notes.
2023-04-04 16:25:07 +05:00
Muhammad Ali
adf546f29c web: disable deleting locked notes (#2244) 2023-04-04 16:24:28 +05:00
Abdulrehman-Jafer
b18cf277f6 core: fix code block overflow in PDF export (#2245)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-04-04 16:19:02 +05:00
ammarahm-ed
8ab26eb852 mobile: fix crash where notebook is not present 2023-03-31 12:56:20 +05:00
ammarahm-ed
aac43274a0 mobile: release 2.4.10 2023-03-30 17:22:00 +05:00
Muhammad Ali
2fb2c4247a web: do not add "none" tool in Configure Toolbar dialog (#2193)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-03-30 17:10:43 +05:00
Abdulrehman-Jafer
cb1d32d177 core: fix txt, pdf & html export line spacing (#2215)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
Co-authored-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
2023-03-30 17:09:00 +05:00
ammarahm-ed
75e1985a6f mobile: save value on change 2023-03-30 16:48:55 +05:00
ammarahm-ed
0093b39f64 mobile: fix nav bar does not follow app theme 2023-03-30 16:07:58 +05:00
ammarahm-ed
d19c2ef735 mobile: add mastodon social link 2023-03-30 15:30:55 +05:00
ammarahm-ed
db527cfbf3 mobile: fix splashscreen logo quality 2023-03-30 15:04:08 +05:00
ammarahm-ed
7da8ddb24f mobile: topic title should to single line 2023-03-30 15:02:01 +05:00
ammarahm-ed
5d401ac9ac mobile: use diff sheet size on each platform 2023-03-30 13:54:21 +05:00
ammarahm-ed
9605eafa00 mobile: add support for sorting topics in sheet 2023-03-30 13:54:21 +05:00
Ammar Ahmed
783460c685 Merge pull request #2218 from streetwriters/fix-reminder-date-set
mobile: fix reminder date picker not going in past
2023-03-29 16:54:24 +05:00
ammarahm-ed
af906d8ace mobile: add some null checks 2023-03-29 16:54:04 +05:00
ammarahm-ed
fca26a8793 mobile: fix reminder date picker not going in past 2023-03-29 16:29:40 +05:00
Muhammad Ali
27863def9c web: fix RTL support for outline lists (#2175)
Co-authored-by: Muhammad Ali <alihamuh@gmail.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-03-24 12:51:00 +05:00
Abdullah Atta
f1d4fd1d5d editor: fix crash when theme.colors is undefined 2023-03-24 12:49:26 +05:00
Abdullah Atta
61b7e43d08 web: do not interrupt importer if notebook is undefined 2023-03-24 11:43:37 +05:00
Abdullah Atta
87ebe1918c web: use debounce instead of inlineDebounce for selection updates in editor 2023-03-24 11:41:03 +05:00
ammarahm-ed
4c5253c267 mobile: release v2.4.9 2023-03-24 02:54:36 +05:00
Abdulrehman-Jafer
f5be8a0c86 web: migrate keyboard.js to typescript (#2160)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-03-24 00:57:55 +05:00
Abdullah Atta
5392582819 web: fix crash on opening an empty notebook 2023-03-24 00:35:29 +05:00
Abdullah Atta
651ae4cdaa web: bump version to 2.4.6 2023-03-24 00:28:19 +05:00
Abdullah Atta
d87b5c5386 desktop: don't show tray menu icons on flatpak builds 2023-03-24 00:28:19 +05:00
ammarahm-ed
d5331b1997 mobile: release v2.4.8 2023-03-24 00:18:12 +05:00
ammarahm-ed
0a6f8fa18e mobile: improve publish note sheet ui 2023-03-24 00:12:18 +05:00
Abdullah Atta
c1ba16eb80 web: group top level notebook notes 2023-03-23 23:58:17 +05:00
Ammar Ahmed
0cd01b25bd Merge pull request #2187 from streetwriters/fix/publish-note-mobile
Fix publish note with password on mobile
2023-03-23 23:52:50 +05:00
Abdullah Atta
f0c1300ec8 web: hyper optimize move note dialog 2023-03-23 23:48:49 +05:00
ammarahm-ed
70eee2a224 mobile: cleanup logs 2023-03-23 23:48:49 +05:00
ammarahm-ed
42470d1d67 mobile: fix add button color in dark mode 2023-03-23 23:48:49 +05:00
ammarahm-ed
7fc58a3fa5 mobile: make bottom padding optional in sheet 2023-03-23 23:48:49 +05:00
ammarahm-ed
e186037c03 mobile: fix bottom padding on sheet 2023-03-23 23:48:49 +05:00
ammarahm-ed
732a0ffea4 mobile: fix duplicate key warning 2023-03-23 23:48:49 +05:00
ammarahm-ed
89f2e9fde1 mobile: show selected indicator on notebook 2023-03-23 23:48:49 +05:00
Abdullah Atta
c369541a7f web: indicate which notebooks have selected topics 2023-03-23 23:48:49 +05:00
Abdullah Atta
0b7a157ab1 web: enable multiselect mode if note is in > 1 nb/topic 2023-03-23 23:48:49 +05:00
ammarahm-ed
9b86c6ebb7 mobile: fix multiselection bugs 2023-03-23 23:48:49 +05:00
ammarahm-ed
22cd07e414 mobile: improve selection performance 2023-03-23 23:48:49 +05:00
ammarahm-ed
47f919cb61 mobile: fix reset select does not reset selected topics 2023-03-23 23:48:49 +05:00
Abdulrehman-Jafer
5b3c889b7c web: migrate css.js to typescript (#2165)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-03-23 17:03:23 +05:00
Abdullah Atta
77ac53215c web: remove document as any in page-visibility 2023-03-23 13:55:52 +05:00
Abdulrehman-Jafer
167c7125d2 web: migrate page-visibility.js to typescript (#2166)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-03-23 13:48:57 +05:00
Abdullah Atta
ba56fd0279 web: don't reset reminders before getting the user 2023-03-23 13:46:54 +05:00
Abdullah Atta
8e795b729a web: read trash cleanup interval from db on startup 2023-03-23 13:46:54 +05:00
Abdullah Atta
a4eb597f35 web: improve notebook vertical pane resizing 2023-03-23 13:46:44 +05:00
Abdullah Atta
0aeade69da web: fix topic search 2023-03-23 13:43:45 +05:00
Abdullah Atta
90a3bd3307 web: fix crash on creating a topic 2023-03-23 13:43:45 +05:00
ammarahm-ed
7a61e3558e mobile: ui cleanup 2023-03-23 13:39:14 +05:00
Abdulrehman-Jafer
dd9c555bff web: migrate parse.js to typescript (#2162)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-03-23 13:36:31 +05:00
ammarahm-ed
50f84780bb mobile: fix publish note with password not working 2023-03-23 13:23:04 +05:00
Abdulrehman-Jafer
e12372364d web: add support for realtime search (#2171)
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
2023-03-23 12:45:33 +05:00
147 changed files with 11087 additions and 1827 deletions

View File

@@ -10,11 +10,11 @@
## Overview
Notesnook is a free (as in speech) & open source note taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using `XChaCha20-Poly1305` & `Argon2`.
Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using `XChaCha20-Poly1305` & `Argon2`.
Notesnook is our **proof** that privacy does _not_ (always) have to come at the cost of convenience. Our goal is to provide users peace of mind & 100% confidence that their notes are safe and secure. The decision to go fully open source is one of the most crucial steps towards that.
Notesnook is our **proof** that privacy does _not_ (always) have to come at the cost of convenience. We aim to provide users peace of mind & 100% confidence that their notes are safe and secure. The decision to go fully open source is one of the most crucial steps towards that.
This repository contains all the code required to build & use the Notesnook web, desktop & mobile clients. If you are looking for a full features list or screenshots, please check the [website](https://notesnook.com/).
This repository contains all the code required to build & use the Notesnook web, desktop & mobile clients. If you are looking for a full feature list or screenshots, please check the [website](https://notesnook.com/).
## Developer guide

View File

@@ -19,9 +19,9 @@
## Build instructions
> **Before you start it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
> **Before you start, it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
### Setting up development environment
### Setting up the development environment
Requirements:
@@ -57,7 +57,7 @@ npm install
### Running the app on Android
[Setup an Android emulator from Android Studio](https://developer.android.com/studio/run/managing-avds) if you haven't already and then run the following command to start the app in the Emulator:
[Setup an Android emulator from Android Studio](https://developer.android.com/studio/run/managing-avds) if you haven't already, and then run the following command to start the app in the Emulator:
```bash
npm run start:android
@@ -78,11 +78,11 @@ npm run start:ios
## Developer guide
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript so if you can help with that, it'd be great!
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript, so if you can help with that, it'd be great!
### The tech stack
We try to keep the stack as lean as possible
We try to keep the stack as lean as possible:
1. React Native
2. Typescript/Javascript
@@ -95,17 +95,17 @@ We try to keep the stack as lean as possible
The app codebase is distributed over two primary directories. `native/` and `app/`.
- `native/`: Includes `android/` and `ios/` folders and everything related to react native core functionality like bundling, development and packaging. Any react-native dependency that has native code i.e android & ios folders, is installed here.
- `native/`: Includes `android/` and `ios/` folders and everything related to react native core functionality like bundling, development, and packaging. Any react-native dependency with native code, i.e., android & ios folders, is installed here.
- `app/`: Includes all the app code other than the native part. All JS only dependencies are installed here.
- `components/`: Each component serves a specific purpose in the app UI, for example the `Paragraph` component is used to render paragraphs in the app and a `Header` component is used to render a `header` on all screens.
- `common/`: Features that have integral role in app functionality, for example, notesnook core is initialized here.
- `app/`: Includes all the app code other than the native part. All JS-only dependencies are installed here.
- `components/`: Each component serves a specific purpose in the app UI. For example, the `Paragraph` component is used to render paragraphs in the app, and a `Header` component is used to render a `header` on all screens.
- `common/`: Features that are integral to the app's functionality. For example, the notesnook core is initialized here.
- `hooks/`: Hooks for different app logic
- `navigation/`: Includes app navigation specific code. Here the app navigation, editor & side menu are rendered side by side in fluid tabs.
- `navigation/`: Includes app navigation-specific code. Here the app navigation, editor & side menu are rendered side by side in fluid tabs.
- `screens`: Navigator screens.
- `services`: Parts of code that do a specific function, for example, the `sync` service is responsibe for running Sync from anywhere in the app.
- `stores`: We use `zustand` for global state management in the app. There are multiple stores that provide the state for different parts of the app.
- `utils`: General purpose stuff such as constant values, utility functions etc.
- `services`: Parts of code that do a specific function. For example, the `sync` service runs Sync from anywhere in the app.
- `stores`: We use `zustand` for global state management in the app. Multiple stores provide the state for different parts of the app.
- `utils`: General purpose stuff such as constant values, utility functions, etc.
There are several other folders at the root:
@@ -115,11 +115,11 @@ There are several other folders at the root:
### Running the tests
When you are done making the required changes, you will need to run the tests to make sure you didn't break anything. We use Detox as the testing framework & the tests can be started as follows:
When you are done making the required changes, you must run the tests to ensure you didn't break anything. We use Detox as the testing framework & the tests can be started as follows:
### Android
To run the tests on android, you will need to create an emulator device on your system:
To run the tests on Android, you will need to create an emulator device on your system:
```
$ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_5_API_31 -d pixel --package "system-images;android-31;default;x86_64"
@@ -127,13 +127,13 @@ $ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_5_API_31 -d pixel --packa
If you face problems, follow the detailed guide in [Detox documentation](https://wix.github.io/Detox/docs/introduction/android-dev-env). Keep the emulator name set to `Pixel_5_API_31`.
Once you have created an emulator device, build the android apks
Once you have created an emulator device, build the Android apks:
```
npm run build:android
```
Finally run the tests
Finally, run the tests:
```
npm run test:android
@@ -141,9 +141,9 @@ npm run test:android
### iOS
To run e2e tests on iOS simulator, you must be on a Mac with XCode installed.
To run e2e tests on the iOS simulator, you must be on a Mac with XCode installed.
First install [AppleSimulatorUtils](https://github.com/wix/AppleSimulatorUtils)
First, install [AppleSimulatorUtils](https://github.com/wix/AppleSimulatorUtils):
```
brew tap wix/brew
@@ -156,7 +156,7 @@ Now build the iOS app for testing:
npm run build:ios
```
Finally run the tests:
Finally, run the tests:
```
npm run test:ios

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -68,14 +68,3 @@ db.host(
ISSUES_HOST: "https://issues.streetwriters.co"
}
);
export async function loadDatabase() {
// if (!DB) {
// let module = await import(/* webpackChunkName: "notes-core" */ 'notes-core/api/index');
// DB = module.default;
// }
// db = new DB(Storage, Platform.OS === 'ios' ? EventSource : AndroidEventSource, filesystem);
// if (DOMParser) {
// await DOMParser.prepare();
// }
}

View File

@@ -22,6 +22,7 @@ import { View } from "react-native";
import { useMenuStore } from "../../../stores/use-menu-store";
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent,
ToastEvent
@@ -30,6 +31,7 @@ import Navigation from "../../../services/navigation";
import { db } from "../../../common/database";
import {
eCloseAddTopicDialog,
eOnTopicSheetUpdate,
eOpenAddTopicDialog
} from "../../../utils/events";
import { sleep } from "../../../utils/time";
@@ -40,6 +42,7 @@ import DialogHeader from "../../dialog/dialog-header";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import { Toast } from "../../toast";
import { useRelationStore } from "../../../stores/use-relation-store";
export class AddTopicDialog extends React.Component {
constructor(props) {
@@ -78,9 +81,11 @@ export class AddTopicDialog extends React.Component {
}
this.close();
setTimeout(() => {
Navigation.queueRoutesForUpdate("Notebooks", "Notebook", "TopicNotes");
Navigation.queueRoutesForUpdate();
useMenuStore.getState().setMenuPins();
});
eSendEvent(eOnTopicSheetUpdate);
useRelationStore.getState().update();
} catch (e) {
console.error(e);
}

View File

@@ -135,7 +135,6 @@ const ResultDialog = () => {
paddingHorizontal: 12
}}
onPress={close}
height={50}
fontSize={SIZE.md + 2}
/>
</View>

View File

@@ -207,13 +207,7 @@ export class VaultDialog extends Component {
return;
}
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"TopicNotes",
"TaggedNotes",
"ColoredNotes"
);
Navigation.queueRoutesForUpdate();
this.password = null;
this.confirmPassword = null;

View File

@@ -109,7 +109,7 @@ export const Title = () => {
{!hide && !isHidden ? (
<Heading
onPress={navigateToNotebook}
numberOfLines={isTopic ? 2 : 1}
numberOfLines={1}
size={SIZE.xl}
style={{
flexWrap: "wrap",

View File

@@ -17,12 +17,14 @@ 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 notifee from "@notifee/react-native";
import React, { useCallback, useEffect, useRef } from "react";
import { Platform, View } from "react-native";
import RNBootSplash from "react-native-bootsplash";
import { checkVersion } from "react-native-check-version";
import Config from "react-native-config";
import { enabled } from "react-native-privacy-snapshot";
import { DatabaseLogger, db, loadDatabase } from "../../common/database";
import { DatabaseLogger, db } from "../../common/database";
import { useAppState } from "../../hooks/use-app-state";
import BiometricService from "../../services/biometrics";
import { eSendEvent, presentSheet } from "../../services/event-manager";
@@ -36,6 +38,7 @@ import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { eOpenAnnouncementDialog } from "../../utils/events";
import { getGithubVersion } from "../../utils/github-version";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { SVG } from "../auth/background";
@@ -50,9 +53,6 @@ import { SvgView } from "../ui/svg";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { Walkthrough } from "../walkthroughs";
import Config from "react-native-config";
import { getGithubVersion } from "../../utils/github-version";
import notifee from "@notifee/react-native";
const Launcher = React.memo(
function Launcher() {
@@ -69,7 +69,7 @@ const Launcher = React.memo(
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const dbInitCompleted = useRef(false);
const loadNotes = useCallback(async () => {
if (verifyUser) {
return;
@@ -80,28 +80,25 @@ const Launcher = React.memo(
initialize();
setImmediate(() => {
setLoading(false);
if (!dbInitCompleted.current) {
setImmediate(() => doAppLoadActions());
}
});
});
});
}, [doAppLoadActions, setLoading, verifyUser]);
}, [setLoading, verifyUser]);
const init = useCallback(async () => {
if (!dbInitCompleted.current) {
if (!db.isInitialized) {
await RNBootSplash.hide({ fade: true });
await loadDatabase();
DatabaseLogger.info("Initializing database");
await db.init();
dbInitCompleted.current = true;
}
if (db.migrations.required() && !verifyUser) {
presentSheet({
component: <Migrate />,
onClose: async () => {
await db.init();
if (!db.isInitialized) {
await db.init();
}
loadNotes();
},
disableClosing: true
@@ -120,9 +117,6 @@ const Launcher = React.memo(
if (!loading) {
doAppLoadActions();
}
return () => {
dbInitCompleted.current = false;
};
}, [doAppLoadActions, loading]);
const doAppLoadActions = useCallback(async () => {

View File

@@ -27,6 +27,7 @@ import { db } from "../../../common/database";
import Notebook from "../../../screens/notebook";
import { TaggedNotes } from "../../../screens/notes/tagged";
import { TopicNotes } from "../../../screens/notes/topic-notes";
import useNavigationStore from "../../../stores/use-navigation-store";
import { useRelationStore } from "../../../stores/use-relation-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import { useThemeStore } from "../../../stores/use-theme-store";
@@ -52,12 +53,14 @@ const showActionSheet = (item) => {
function getNotebook(item) {
const isTrash = item.type === "trash";
const currentId = useNavigationStore.getState().currentScreen.id;
if (isTrash) return [];
const items = [];
const notebooks = db.relations.to(item, "notebook") || [];
for (let notebook of notebooks) {
if (items.length > 1) break;
if (notebook.id === currentId) continue;
items.push(notebook);
}
@@ -68,6 +71,7 @@ function getNotebook(item) {
if (!notebook) continue;
for (let topicId of nb.topics) {
if (items.length > 1) break;
if (topicId === currentId) continue;
const topic = notebook.topics.find((t) => t.id === topicId);
if (!topic) continue;
items.push(topic);
@@ -77,10 +81,20 @@ function getNotebook(item) {
return items;
}
function getTags(item) {
const noteTags = item.tags?.slice(0, 3) || [];
const tags = [];
for (const tagName of noteTags) {
const tag = db.tags.tag(tagName);
if (!tag) continue;
tags.push(tag);
}
return tags;
}
const NoteItem = ({
item,
isTrash,
tags,
dateBy = "dateCreated",
noOpen = false
}) => {
@@ -96,6 +110,7 @@ const NoteItem = ({
const reminders = db.relations.from(item, "reminder");
const reminder = getUpcomingReminder(reminders);
const noteColor = COLORS_NOTE[item.color?.toLowerCase()];
const tags = getTags(item);
return (
<>
<View
@@ -117,8 +132,13 @@ const NoteItem = ({
>
{notebooks?.map((item) => (
<Button
title={item.title}
key={item}
title={
item.title.length > 25
? item.title.slice(0, 25) + "..."
: item.title
}
tooltipText={item.title}
key={item.id}
height={25}
icon={item.type === "topic" ? "bookmark" : "book-outline"}
type="grayBg"

View File

@@ -100,7 +100,7 @@ export const openNote = async (item, isTrash, setSelectedItem, isSheet) => {
};
export const NoteWrapper = React.memo(
function NoteWrapper({ item, index, tags, dateBy, isSheet }) {
function NoteWrapper({ item, index, dateBy, isSheet }) {
const isTrash = item.type === "trash";
const setSelectedItem = useSelectionStore((state) => state.setSelectedItem);
@@ -113,7 +113,7 @@ export const NoteWrapper = React.memo(
isSheet={isSheet}
item={item}
>
<NoteItem item={item} dateBy={dateBy} tags={tags} isTrash={isTrash} />
<NoteItem item={item} dateBy={dateBy} isTrash={isTrash} />
</SelectionWrapper>
);
},
@@ -125,10 +125,6 @@ export const NoteWrapper = React.memo(
return false;
}
if (JSON.stringify(prev.tags) !== JSON.stringify(next.tags)) {
return false;
}
if (prev.item !== next.item) {
return false;
}

View File

@@ -64,16 +64,7 @@ export const openNotebookTopic = (item) => {
negativeText: "Delete",
positivePress: async () => {
await db.trash.restore(item.id);
Navigation.queueRoutesForUpdate(
"Tags",
"Notes",
"Notebooks",
"Favorites",
"Trash",
"TaggedNotes",
"ColoredNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(false);
ToastEvent.show({
heading: "Restore successful",

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/>.
*/
import React, { useRef } from "react";
import React, { useEffect, useRef } from "react";
import { RefreshControl, View } from "react-native";
import { FlashList } from "@shopify/flash-list";
import Animated, { FadeInDown } from "react-native-reanimated";
@@ -57,25 +57,10 @@ const RenderItem = ({ item, index, type, ...restArgs }) => {
const dateBy =
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
const totalNotes = getTotalNotes(item);
const tags =
item.tags
?.slice(0, 3)
?.map((item) => {
let tag = db.tags.tag(item);
if (!tag) return null;
return {
title: tag.title,
id: tag.id,
alias: tag.alias
};
})
.filter((t) => t !== null) || [];
return (
<Item
item={item}
tags={tags}
dateBy={dateBy}
index={index}
type={type}
@@ -154,13 +139,20 @@ const List = ({
[screen]
);
useEffect(() => {
eSendEvent(eScrollEvent, {
y: 0,
screen
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
let styles = {
width: "100%",
minHeight: 1,
minWidth: 1
};
const _keyExtractor = (item) => item.id || item.title;
const ListView = ScrollComponent ? ScrollComponent : FlashList;
return (
<>
@@ -226,12 +218,14 @@ const List = ({
}
/>
</Animated.View>
<JumpToSectionDialog
screen={screen}
data={listData}
type={screen === "Notes" ? "home" : type}
scrollRef={scrollRef}
/>
{listData ? (
<JumpToSectionDialog
screen={screen}
data={listData}
type={screen === "Notes" ? "home" : type}
scrollRef={scrollRef}
/>
) : null}
</>
);
};

View File

@@ -85,13 +85,7 @@ const MergeConflicts = () => {
}
});
}
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
if (editorController.current?.note?.id === note.id) {
// reload the note in editor
eSendEvent(eOnLoadNote, {

View File

@@ -44,16 +44,7 @@ export default function NotePreview({ session, content, note }) {
async function restore() {
if (note && note.type === "trash") {
await db.trash.restore(note.id);
Navigation.queueRoutesForUpdate(
"Tags",
"Notes",
"Notebooks",
"Favorites",
"Trash",
"TaggedNotes",
"ColoredNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(false);
ToastEvent.show({
heading: "Restore successful",
@@ -73,13 +64,7 @@ export default function NotePreview({ session, content, note }) {
}
eSendEvent(eCloseSheet, "note_history");
eSendEvent(eCloseSheet);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
ToastEvent.show({
heading: "Note restored successfully",

View File

@@ -43,13 +43,7 @@ export const ColorTags = ({ item }) => {
let _note = db.notes.note(note.id).data;
setNote({ ..._note });
setColorNotes();
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
eSendEvent(refreshNotesPage);
};

View File

@@ -212,7 +212,6 @@ Properties.present = (item, buttons = [], isSheet) => {
if (!props[0]) return;
presentSheet({
context: isSheet ? "local" : undefined,
enableGesturesInScrollView: true,
component: (ref, close) => (
<Properties
close={() => {

View File

@@ -145,13 +145,7 @@ export default function Notebooks({ note, close, full }) {
note
);
useNotebookStore.getState().setNotebooks();
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
ToastEvent.show({
heading: "Note removed from topic",
context: "local",

View File

@@ -94,7 +94,7 @@ export const Synced = ({ item, close }) => {
fontSize={SIZE.xs + 1}
title="Learn more"
height={30}
type="transparent"
type="grayAccent"
/>
</View>
) : null;

View File

@@ -21,18 +21,18 @@ import React, { useCallback, useEffect } from "react";
import { BackHandler, Platform, View } from "react-native";
import { db } from "../../common/database";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { eSendEvent, ToastEvent } from "../../services/event-manager";
import { ToastEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import SearchService from "../../services/search";
import useNavigationStore from "../../stores/use-navigation-store";
import { useSelectionStore } from "../../stores/use-selection-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { eOpenMoveNoteDialog } from "../../utils/events";
import { deleteItems } from "../../utils/functions";
import { tabBarRef } from "../../utils/global-refs";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { presentDialog } from "../dialog/functions";
import MoveNoteSheet from "../sheets/add-to";
import ExportNotesSheet from "../sheets/export-notes";
import { IconButton } from "../ui/icon-button";
import Heading from "../ui/typography/heading";
@@ -64,13 +64,7 @@ export const SelectionHeader = React.memo(() => {
selectedItemsList.forEach((item) => {
db.notes.note(item.id).favorite();
});
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
clearSelection();
}
};
@@ -82,16 +76,7 @@ export const SelectionHeader = React.memo(() => {
noteIds.push(item.id);
});
await db.trash.restore(...noteIds);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Trash",
"Notebooks",
"Tags"
);
Navigation.queueRoutesForUpdate();
clearSelection();
ToastEvent.show({
@@ -118,16 +103,7 @@ export const SelectionHeader = React.memo(() => {
noteIds.push(item.id);
});
await db.trash.delete(...noteIds);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Trash",
"Notebooks",
"Tags"
);
Navigation.queueRoutesForUpdate();
clearSelection();
}
},
@@ -237,7 +213,7 @@ export const SelectionHeader = React.memo(() => {
onPress={async () => {
//setSelectionMode(false);
await sleep(100);
eSendEvent(eOpenMoveNoteDialog);
MoveNoteSheet.present();
}}
customStyle={{
marginLeft: 10
@@ -288,15 +264,7 @@ export const SelectionHeader = React.memo(() => {
);
}
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Notebooks",
"Notebook"
);
Navigation.queueRoutesForUpdate();
clearSelection();
}
}}

View File

@@ -101,6 +101,7 @@ const SheetProvider = ({ context = "global" }) => {
setVisible(false);
setData(null);
}}
bottomPadding={!data.noBottomPadding}
enableGesturesInScrollView={data.enableGesturesInScrollView}
>
<View
@@ -212,7 +213,6 @@ const SheetProvider = ({ context = "global" }) => {
title={item.actionText}
icon={item.icon && item.icon}
type={item.type || "accent"}
height={50}
style={{
marginBottom: 10
}}

View File

@@ -25,22 +25,22 @@ import {
TouchableOpacity,
View
} from "react-native";
import { FlatList } from "react-native-actions-sheet";
import { notesnook } from "../../../../e2e/test.ids";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import { presentSheet, ToastEvent } from "../../../services/event-manager";
import { ToastEvent, presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { useMenuStore } from "../../../stores/use-menu-store";
import { useRelationStore } from "../../../stores/use-relation-store";
import { ph, pv, SIZE } from "../../../utils/size";
import { SIZE, ph, pv } from "../../../utils/size";
import { sleep } from "../../../utils/time";
import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import { MoveNotes } from "../move-notes/movenote";
import { FlatList } from "react-native-actions-sheet";
let refs = [];
export class AddNotebookSheet extends React.Component {
@@ -92,7 +92,7 @@ export class AddNotebookSheet extends React.Component {
close = () => {
refs = [];
this.props.close();
this.props.close(true);
};
onDelete = (index) => {
@@ -182,6 +182,7 @@ export class AddNotebookSheet extends React.Component {
});
await db.notebooks.notebook(toEdit.id).topics.add(...nextTopics);
this.close();
} else {
newNotebookId = await db.notebooks.add({
title: this.title,
@@ -191,14 +192,7 @@ export class AddNotebookSheet extends React.Component {
});
}
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate(
"Notes",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Notebooks",
"Notebook"
);
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
MoveNotes.present(db.notebooks.notebook(newNotebookId).data);
};
@@ -251,13 +245,34 @@ export class AddNotebookSheet extends React.Component {
willFocus && this.topicInputRef.current?.focus();
};
renderTopicItem = ({ item, index }) => (
<TopicItem
item={item}
onPress={(item, index) => {
this.prevIndex = index;
this.prevItem = item;
this.topicInputRef.current?.setNativeProps({
text: item
});
this.topicInputRef.current?.focus();
this.currentInputValue = item;
this.setState({
editTopic: true
});
}}
onDelete={this.onDelete}
index={index}
colors={this.props.colors}
/>
);
render() {
const { colors } = this.props;
const { topics, topicInputFocused, notebook } = this.state;
return (
<View
style={{
maxHeight: DDS.isTab ? "90%" : "96%",
maxHeight: DDS.isTab ? "90%" : "97%",
borderRadius: DDS.isTab ? 5 : 0,
paddingHorizontal: 12
}}
@@ -272,17 +287,34 @@ export class AddNotebookSheet extends React.Component {
}}
blurOnSubmit={false}
/>
<DialogHeader
title={
notebook && notebook.dateCreated ? "Edit Notebook" : "New Notebook"
}
paragraph={
notebook && notebook.dateCreated
? "You are editing " + this.title + " notebook."
: "Notebooks are the best way to organize your notes."
}
/>
<Seperator half />
<View
style={{
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Heading size={SIZE.lg}>
{notebook && notebook.dateCreated
? "Edit Notebook"
: "New Notebook"}
</Heading>
<Button
title="Save"
type="accent"
height={40}
style={{
borderRadius: 100,
paddingHorizontal: 24
}}
fontSize={SIZE.md}
onPress={this.addNewNotebook}
/>
</View>
<Seperator />
<Input
fwdRef={(ref) => (this.titleRef = ref)}
@@ -350,49 +382,11 @@ export class AddNotebookSheet extends React.Component {
keyExtractor={(item, index) => item + index.toString()}
keyboardShouldPersistTaps="always"
keyboardDismissMode="interactive"
ListFooterComponent={<View style={{ height: 50 }} />}
renderItem={({ item, index }) => (
<TopicItem
item={item}
onPress={(item, index) => {
this.prevIndex = index;
this.prevItem = item;
this.topicInputRef.current?.setNativeProps({
text: item
});
this.topicInputRef.current?.focus();
this.currentInputValue = item;
this.setState({
editTopic: true
});
}}
onDelete={this.onDelete}
index={index}
colors={colors}
/>
)}
/>
<Seperator />
<Button
width="100%"
height={50}
fontSize={SIZE.md}
title={
notebook && notebook.dateCreated
? "Save changes"
: "Create notebook"
ListFooterComponent={
topics.length === 0 ? null : <View style={{ height: 50 }} />
}
type="accent"
onPress={this.addNewNotebook}
renderItem={this.renderTopicItem}
/>
{/*
{Platform.OS === 'ios' && (
<View
style={{
height: 40
}}
/>
)} */}
</View>
);
}

View File

@@ -20,13 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createContext, useContext } from "react";
export const SelectionContext = createContext({
enabled: false,
selected: [],
toggleSelection: (item) => null,
deselect: (item) => null,
select: (item) => null,
isSelected: (item) => null,
setMultiSelect: () => null,
deselectAll: () => null
});
export const SelectionProvider = SelectionContext.Provider;

View File

@@ -71,7 +71,6 @@ export const FilteredList = ({
}
keyboardShouldPersistTaps="always"
keyboardDismissMode="none"
nestedScrollEnabled
/>
);
};

View File

@@ -17,16 +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 React, { useCallback, useEffect, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo } from "react";
import { Keyboard, TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../../common/database";
import { presentSheet, ToastEvent } from "../../../services/event-manager";
import {
eSendEvent,
presentSheet,
ToastEvent
} from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import SearchService from "../../../services/search";
import { useNotebookStore } from "../../../stores/use-notebook-store";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import { useThemeStore } from "../../../stores/use-theme-store";
import { eOnTopicSheetUpdate } from "../../../utils/events";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
import { presentDialog } from "../../dialog/functions";
@@ -35,19 +41,21 @@ import Paragraph from "../../ui/typography/paragraph";
import { SelectionProvider } from "./context";
import { FilteredList } from "./filtered-list";
import { ListItem } from "./list-item";
import { useItemSelectionStore } from "./store";
const MoveNoteSheet = ({ note, actionSheetRef }) => {
const colors = useThemeStore((state) => state.colors);
const [multiSelect, setMultiSelect] = useState(false);
const notebooks = useNotebookStore((state) =>
state.notebooks.filter((n) => n?.type === "notebook")
);
const dimensions = useSettingStore((state) => state.dimensions);
const selectedItemsList = useSelectionStore(
(state) => state.selectedItemsList
);
const setNotebooks = useNotebookStore((state) => state.setNotebooks);
const [itemState, setItemState] = useState({});
const multiSelect = useItemSelectionStore((state) => state.multiSelect);
const onAddNotebook = async (title) => {
if (!title || title.trim().length === 0) {
ToastEvent.show({
@@ -130,41 +138,55 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
useEffect(() => {
resetItemState();
return () => {
useItemSelectionStore.getState().setMultiSelect(false);
useItemSelectionStore.getState().setItemState({});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const resetItemState = useCallback(
(state) => {
setItemState(() => {
const itemState = {};
const notebooks = db.notebooks.all;
for (let notebook of notebooks) {
itemState[notebook.id] = state
const itemState = {};
const notebooks = db.notebooks.all;
let count = 0;
for (let notebook of notebooks) {
itemState[notebook.id] = state
? state
: areAllSelectedItemsInNotebook(notebook, selectedItemsList)
? "selected"
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
? "intermediate"
: "deselected";
if (itemState[notebook.id] === "selected") {
count++;
contextValue.select(notebook);
} else {
contextValue.deselect(notebook);
}
for (let topic of notebook.topics) {
itemState[topic.id] = state
? state
: areAllSelectedItemsInNotebook(notebook, selectedItemsList)
: areAllSelectedItemsInTopic(topic, selectedItemsList) &&
getSelectedNotesCountInItem(topic, selectedItemsList)
? "selected"
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
: getSelectedNotesCountInItem(topic, selectedItemsList) > 0
? "intermediate"
: "deselected";
if (itemState[notebook.id] === "selected") {
contextValue.select(notebook);
}
for (let topic of notebook.topics) {
itemState[topic.id] = state
? state
: areAllSelectedItemsInTopic(topic, selectedItemsList) &&
getSelectedNotesCountInItem(topic, selectedItemsList)
? "selected"
: getSelectedNotesCountInItem(topic, selectedItemsList) > 0
? "intermediate"
: "deselected";
if (itemState[topic.id] === "selected") {
contextValue.select(topic);
}
if (itemState[topic.id] === "selected") {
count++;
contextValue.select(topic);
} else {
contextValue.deselect(topic);
}
}
return itemState;
});
}
if (count > 1) {
useItemSelectionStore.getState().setMultiSelect(true);
} else {
useItemSelectionStore.getState().setMultiSelect(false);
}
useItemSelectionStore.getState().setItemState(itemState);
},
[contextValue, getSelectedNotesCountInItem, selectedItemsList]
);
@@ -191,32 +213,26 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
const updateItemState = useCallback(function (item, state) {
setItemState((itemState) => {
const mergeState = {
[item.id]: state
};
return {
...itemState,
...mergeState
};
const itemState = useItemSelectionStore.getState().itemState;
const mergeState = {
[item.id]: state
};
useItemSelectionStore.getState().setItemState({
...itemState,
...mergeState
});
}, []);
const contextValue = useMemo(
() => ({
enabled: multiSelect,
toggleSelection: (item) => {
setItemState((itemState) => {
if (itemState[item.id] === "selected") {
updateItemState(item, "deselected");
} else {
updateItemState(item, "selected");
}
return itemState;
});
const itemState = useItemSelectionStore.getState().itemState;
if (itemState[item.id] === "selected") {
updateItemState(item, "deselected");
} else {
updateItemState(item, "selected");
}
},
setMultiSelect: setMultiSelect,
deselect: (item) => {
updateItemState(item, "deselected");
},
@@ -227,7 +243,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
resetItemState(state);
}
}),
[multiSelect, resetItemState, updateItemState]
[resetItemState, updateItemState]
);
const getItemFromId = (id) => {
@@ -241,6 +257,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
const onSave = async () => {
const noteIds = note ? [note.id] : selectedItemsList.map((n) => n.id);
const itemState = useItemSelectionStore.getState().itemState;
for (const id in itemState) {
const item = getItemFromId(id);
if (itemState[id] === "selected") {
@@ -275,15 +292,9 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
}
}
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Notebook"
);
Navigation.queueRoutesForUpdate();
setNotebooks();
eSendEvent(eOnTopicSheetUpdate);
SearchService.updateAndSearch();
actionSheetRef.current?.hide();
};
@@ -335,31 +346,32 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
/>
</View>
{multiSelect ? (
<View
<View
style={{
paddingHorizontal: 12
}}
>
<Button
title="Reset selection"
height={30}
style={{
paddingHorizontal: 12
alignSelf: "flex-start",
paddingHorizontal: 0,
width: "100%",
marginTop: 6
}}
>
<Button
title="Reset selection"
height={30}
style={{
alignSelf: "flex-start",
paddingHorizontal: 0
}}
onPress={() => {
resetItemState();
setMultiSelect(false);
}}
/>
</View>
) : null}
type="grayAccent"
onPress={() => {
resetItemState();
}}
/>
</View>
<SelectionProvider value={contextValue}>
<FilteredList
style={{
paddingHorizontal: 12
paddingHorizontal: 12,
maxHeight: dimensions.height * 0.85
}}
ListEmptyComponent={
notebooks.length > 0 ? null : (
@@ -385,13 +397,8 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
item={item}
key={item.id}
index={index}
intermediate={itemState[item.id] === "intermediate"}
removed={
itemState[item.id] === "deselected" &&
getSelectedNotesCountInItem(item) > 0
}
hasNotes={getSelectedNotesCountInItem(item) > 0}
sheetRef={actionSheetRef}
isSelected={itemState[item.id] === "selected"}
infoText={
<>
{item.topics.length === 1
@@ -401,17 +408,14 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
getListItems={getItemsForItem}
getSublistItemProps={(topic) => ({
selected: itemState[topic.id] === "selected",
intermediate: itemState[topic.id] === "intermediate",
isSelected: itemState[topic.id] === "selected",
removed:
itemState[topic.id] === "deselected" &&
getSelectedNotesCountInItem(topic) > 0,
hasNotes: getSelectedNotesCountInItem(topic) > 0,
style: {
marginBottom: 0,
height: 40
},
onPress: (item) => {
const itemState =
useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
if (currentState !== "selected") {
resetItemState("deselected");
@@ -440,19 +444,23 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
onAddSublistItem={(item) => {
openAddTopicDialog(item);
}}
onPress={(item) => {
const itemState = useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
if (currentState !== "selected") {
resetItemState("deselected");
contextValue.select(item);
} else {
contextValue.deselect(item);
}
}}
/>
)}
itemType="notebook"
onAddItem={async (title) => {
return await onAddNotebook(title);
}}
// ListFooterComponent={
// <View
// style={{
// height: 200
// }}
// />
// }
ListFooterComponent={<View style={{ height: 20 }} />}
/>
</SelectionProvider>
</View>
@@ -463,7 +471,8 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
MoveNoteSheet.present = (note) => {
presentSheet({
component: (ref) => <MoveNoteSheet actionSheetRef={ref} note={note} />,
enableGesturesInScrollView: false
enableGesturesInScrollView: false,
noBottomPadding: true
});
};
export default MoveNoteSheet;

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 React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import { db } from "../../../common/database";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { useThemeStore } from "../../../stores/use-theme-store";
import { SIZE } from "../../../utils/size";
import { IconButton } from "../../ui/icon-button";
@@ -27,13 +29,78 @@ import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { useSelectionContext } from "./context";
import { FilteredList } from "./filtered-list";
import { useItemSelectionStore } from "./store";
const _ListItem = ({
const SelectionIndicator = ({
item,
hasNotes,
selectItem,
onPress,
onChange
}) => {
const itemState = useItemSelectionStore((state) => state.itemState[item.id]);
const multiSelect = useItemSelectionStore((state) => state.multiSelect);
const isSelected = itemState === "selected";
const isIntermediate = itemState === "intermediate";
const isRemoved = !isSelected && hasNotes;
const colors = useThemeStore((state) => state.colors);
useEffect(() => {
onChange?.();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [itemState]);
return (
<IconButton
size={22}
customStyle={{
marginRight: 5,
width: 23,
height: 23
}}
color={
isRemoved
? colors.red
: isIntermediate || isSelected
? colors.accent
: colors.icon
}
onPress={() => {
if (multiSelect) return selectItem();
onPress?.(item);
}}
onLongPress={() => {
useItemSelectionStore.getState().setMultiSelect(true);
selectItem();
}}
testID={
isRemoved
? "close-circle-outline"
: isSelected
? "check-circle-outline"
: isIntermediate
? "minus-circle-outline"
: "checkbox-blank-circle-outline"
}
name={
isRemoved
? "close-circle-outline"
: isSelected
? "check-circle-outline"
: isIntermediate
? "minus-circle-outline"
: "checkbox-blank-circle-outline"
}
/>
);
};
export const ListItem = ({
item,
index,
icon,
infoText,
intermediate,
hasSubList,
onPress,
onScrollEnd,
@@ -43,19 +110,49 @@ const _ListItem = ({
sublistItemType,
onAddItem,
getSublistItemProps,
removed,
isSelected,
hasHeaderSearch,
onAddSublistItem,
hasNotes,
onChange,
sheetRef
}) => {
const { enabled, toggleSelection, setMultiSelect } = useSelectionContext();
const { toggleSelection } = useSelectionContext();
const multiSelect = useItemSelectionStore((state) => state.multiSelect);
const [showSelectedIndicator, setShowSelectedIndicator] = useState(false);
const colors = useThemeStore((state) => state.colors);
const [expanded, setExpanded] = useState(false);
function selectItem() {
toggleSelection(item);
}
const getSelectedNotesCountInNotebookTopics = (item) => {
if (item.type === "topic") return;
let count = 0;
const noteIds = [];
for (let topic of item.topics) {
noteIds.push(...(db.notes?.topicReferences.get(topic.id) || []));
if (useItemSelectionStore.getState().itemState[topic.id] === "selected") {
count++;
}
}
useSelectionStore.getState().selectedItemsList.forEach((item) => {
if (noteIds.indexOf(item.id) > -1) {
count++;
}
});
return count;
};
useEffect(() => {
setShowSelectedIndicator(getSelectedNotesCountInNotebookTopics(item) > 0);
}, [item]);
const onChangeSubItem = () => {
setShowSelectedIndicator(getSelectedNotesCountInNotebookTopics(item) > 0);
};
return (
<View
style={{
@@ -67,12 +164,12 @@ const _ListItem = ({
<PressableButton
onPress={() => {
if (hasSubList) return setExpanded(!expanded);
if (enabled) return selectItem();
if (multiSelect) return selectItem();
onPress?.(item);
}}
type={type}
onLongPress={() => {
setMultiSelect(true);
useItemSelectionStore.getState().setMultiSelect(true);
selectItem();
}}
customStyle={{
@@ -97,43 +194,12 @@ const _ListItem = ({
alignItems: "center"
}}
>
<IconButton
size={22}
customStyle={{
marginRight: 5,
width: 23,
height: 23
}}
color={
removed
? colors.red
: intermediate || isSelected
? colors.accent
: colors.icon
}
onPress={() => {
selectItem();
if (enabled) return;
onPress?.(item);
}}
testID={
removed
? "close-circle-outline"
: isSelected
? "check-circle-outline"
: intermediate
? "minus-circle-outline"
: "checkbox-blank-circle-outline"
}
name={
removed
? "close-circle-outline"
: isSelected
? "check-circle-outline"
: intermediate
? "minus-circle-outline"
: "checkbox-blank-circle-outline"
}
<SelectionIndicator
hasNotes={hasNotes}
onPress={onPress}
item={item}
onChange={onChange}
selectItem={selectItem}
/>
<View>
{hasSubList && expanded ? (
@@ -152,14 +218,27 @@ const _ListItem = ({
<View
style={{
flexDirection: "row"
flexDirection: "row",
alignItems: "center"
}}
>
{showSelectedIndicator ? (
<View
style={{
backgroundColor: colors.accent,
width: 7,
height: 7,
borderRadius: 100,
marginRight: 12
}}
/>
) : null}
{onAddSublistItem ? (
<IconButton
name={"plus"}
testID="add-item-icon"
color={colors}
color={colors.pri}
size={SIZE.xl}
onPress={() => {
onAddSublistItem(item);
@@ -201,6 +280,7 @@ const _ListItem = ({
item={item}
{...getSublistItemProps(item)}
index={index}
onChange={onChangeSubItem}
onScrollEnd={onScrollEnd}
/>
)}
@@ -210,11 +290,3 @@ const _ListItem = ({
</View>
);
};
export const ListItem = React.memo(_ListItem, (prev, next) => {
if (prev.selected === undefined) return false;
if (prev.isSelected !== next.isSelected) return false;
if (prev.selected !== next.selected) return false;
if (prev.intermediate !== next.intermediate) return false;
return true;
});

View File

@@ -0,0 +1,42 @@
/*
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 create, { State } from "zustand";
type SelectionItemState = Record<
string,
"intermediate" | "selected" | "deselected"
>;
export interface SelectionStore extends State {
itemState: SelectionItemState;
setItemState: (state: SelectionItemState) => void;
multiSelect: boolean;
setMultiSelect: (multiSelect: boolean) => void;
}
export const useItemSelectionStore = create<SelectionStore>((set) => ({
itemState: {},
setItemState: (itemState) => {
set({
itemState
});
},
multiSelect: false,
setMultiSelect: (multiSelect) => set({ multiSelect })
}));

View File

@@ -277,7 +277,6 @@ const ExportNotesSheet = ({ notes, update }) => {
});
});
}}
height={50}
/>
<Button
title="Share"
@@ -301,7 +300,6 @@ const ExportNotesSheet = ({ notes, update }) => {
}).catch(console.log);
}
}}
height={50}
/>
<Button
title="Export in another format"
@@ -317,7 +315,6 @@ const ExportNotesSheet = ({ notes, update }) => {
setResult(null);
setExporting(false);
}}
height={50}
/>
</>
)}

View File

@@ -48,7 +48,6 @@ export const ShareComponent = ({ uri, name, padding }) => {
});
});
}}
height={50}
/>
<Button
title="Share"
@@ -65,7 +64,6 @@ export const ShareComponent = ({ uri, name, padding }) => {
shareFile: true
}).catch(console.log);
}}
height={50}
/>
</View>
);

View File

@@ -196,7 +196,6 @@ For example:
onPress={onPress}
title={loading ? null : "Submit"}
loading={loading}
height={50}
width="100%"
type="accent"
/>

View File

@@ -103,15 +103,7 @@ const ManageTagsSheet = (props) => {
});
}
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Notebooks",
"Notebook"
);
Navigation.queueRoutesForUpdate();
};
return (
@@ -142,7 +134,6 @@ const ManageTagsSheet = (props) => {
setFocus(false);
}}
onSubmit={onSubmit}
height={50}
placeholder="Search or add a tag"
/>
@@ -225,13 +216,7 @@ const TagItem = ({ tag, note, setNote }) => {
console.error(e);
}
setTimeout(() => {
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
}, 1);
};

View File

@@ -122,15 +122,7 @@ export const MoveNotes = ({
db.notebooks?.notebook(currentNotebook.id).data as NotebookType
);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Notebook",
"Notebooks"
);
Navigation.queueRoutesForUpdate();
return true;
};
@@ -318,15 +310,7 @@ export const MoveNotes = ({
},
...selectedNoteIds
);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes",
"Notebook",
"Notebooks"
);
Navigation.queueRoutesForUpdate();
SearchService.updateAndSearch();
eSendEvent(eCloseSheet);
}}

View File

@@ -60,16 +60,10 @@ const PublishNoteSheet = ({ note: item, update }) => {
if (isLocked && !passwordValue) return;
await db.monographs.publish(note.id, {
selfDestruct: selfDestruct,
password: isLocked && passwordValue
password: isLocked && passwordValue.current
});
setNote(db.notes.note(note.id)?.data);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
setPublishLoading(false);
}
} catch (e) {
@@ -86,9 +80,6 @@ const PublishNoteSheet = ({ note: item, update }) => {
const setPublishLoading = (value) => {
setPublishing(value);
update({
progress: value
});
};
const deletePublishedNote = async () => {
@@ -98,13 +89,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
if (note?.id) {
await db.monographs.unpublish(note.id);
setNote(db.notes.note(note.id)?.data);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
"ColoredNotes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
setPublishLoading(false);
}
} catch (e) {
@@ -164,7 +149,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 15,
marginTop: 10,
backgroundColor: colors.nav,
padding: 12,
borderRadius: 5
@@ -176,8 +161,8 @@ const PublishNoteSheet = ({ note: item, update }) => {
flexShrink: 1
}}
>
<Heading size={SIZE.sm}>Published at:</Heading>
<Paragraph size={SIZE.xs} numberOfLines={1}>
<Heading size={SIZE.md}>Published at:</Heading>
<Paragraph size={SIZE.sm} numberOfLines={1}>
{publishUrl}
</Paragraph>
<Paragraph
@@ -214,7 +199,6 @@ const PublishNoteSheet = ({ note: item, update }) => {
/>
</View>
)}
<Seperator />
<TouchableOpacity
onPress={() => {
@@ -225,7 +209,11 @@ const PublishNoteSheet = ({ note: item, update }) => {
style={{
flexDirection: "row",
alignItems: "center",
marginBottom: 10
marginBottom: 10,
backgroundColor: colors.nav,
paddingVertical: 12,
borderRadius: 5,
marginTop: 10
}}
>
<IconButton
@@ -234,7 +222,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
setIsLocked(!isLocked);
}}
color={isLocked ? colors.accent : colors.icon}
size={SIZE.lg}
size={SIZE.xl}
name={
isLocked
? "check-circle-outline"
@@ -262,7 +250,10 @@ const PublishNoteSheet = ({ note: item, update }) => {
activeOpacity={0.9}
style={{
flexDirection: "row",
alignItems: "center"
alignItems: "center",
backgroundColor: colors.nav,
paddingVertical: 12,
borderRadius: 5
}}
>
<IconButton
@@ -270,7 +261,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
setSelfDestruct(!selfDestruct);
}}
color={selfDestruct ? colors.accent : colors.icon}
size={SIZE.lg}
size={SIZE.xl}
name={
selfDestruct
? "check-circle-outline"
@@ -313,31 +304,38 @@ const PublishNoteSheet = ({ note: item, update }) => {
</>
) : null}
<Button
onPress={publishNote}
fontSize={SIZE.md}
width="100%"
<View
style={{
marginTop: 10
flexDirection: "row",
width: "100%",
justifyContent: "center"
}}
height={50}
type="accent"
title={isPublished ? "Update published note" : "Publish note"}
/>
{isPublished && (
<>
<Seperator half />
<Button
onPress={deletePublishedNote}
fontSize={SIZE.md}
width="100%"
height={50}
type="error"
title="Unpublish note"
/>
</>
)}
>
{isPublished && (
<>
<Button
onPress={deletePublishedNote}
fontSize={SIZE.md}
type="error"
title="Unpublish"
style={{
width: "49%"
}}
/>
</>
)}
<Seperator half />
<Button
onPress={publishNote}
fontSize={SIZE.md}
style={{
width: isPublished ? "49%" : 250,
borderRadius: isPublished ? 5 : 100
}}
type="accent"
title={isPublished ? "Update" : "Publish"}
/>
</View>
</View>
</>
)}
@@ -347,7 +345,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
size={SIZE.xs}
style={{
textAlign: "center",
marginTop: 5,
marginTop: 10,
textDecorationLine: "underline"
}}
onPress={async () => {

View File

@@ -96,7 +96,6 @@ const RateAppSheet = () => {
onPress={rateApp}
fontSize={SIZE.md}
width="100%"
height={50}
type="accent"
title="Rate now (It takes only a second)"
/>
@@ -121,14 +120,12 @@ const RateAppSheet = () => {
fontSize={SIZE.md}
type="error"
width="48%"
height={50}
title="Never"
/>
<Button
onPress={onClose}
fontSize={SIZE.md}
width="48%"
height={50}
type="grayBg"
title="Later"
/>

View File

@@ -298,7 +298,6 @@ class RecoveryKeySheet extends React.Component {
width="100%"
type="grayAccent"
fontSize={SIZE.md}
height={50}
/>
<Seperator />
<Button
@@ -308,7 +307,6 @@ class RecoveryKeySheet extends React.Component {
type="grayAccent"
fontSize={SIZE.md}
icon="qrcode"
height={50}
/>
<Seperator />
<Button
@@ -318,7 +316,6 @@ class RecoveryKeySheet extends React.Component {
type="grayAccent"
icon="text"
fontSize={SIZE.md}
height={50}
/>
<Seperator />
@@ -329,7 +326,6 @@ class RecoveryKeySheet extends React.Component {
type="grayAccent"
icon="cloud"
fontSize={SIZE.md}
height={50}
/>
<Seperator />
@@ -349,7 +345,6 @@ class RecoveryKeySheet extends React.Component {
<Button
title="I have saved the key."
width="100%"
height={50}
type="error"
fontSize={SIZE.md}
onPress={this.close}

View File

@@ -21,9 +21,9 @@ import { Platform, TextInput, View } from "react-native";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
presentSheet,
PresentSheetOptions,
ToastEvent
ToastEvent,
presentSheet
} from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import { SIZE } from "../../../utils/size";
@@ -33,6 +33,7 @@ import Input from "../../ui/input";
import dayjs from "dayjs";
import DatePicker from "react-native-date-picker";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import Navigation from "../../../services/navigation";
import Notifications, { Reminder } from "../../../services/notifications";
import PremiumService from "../../../services/premium";
@@ -41,6 +42,7 @@ import { useRelationStore } from "../../../stores/use-relation-store";
import { NoteType } from "../../../utils/types";
import { Dialog } from "../../dialog";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
type ReminderSheetProps = {
@@ -208,15 +210,7 @@ export default function ReminderSheet({
});
}
Notifications.scheduleNotification(_reminder as Reminder);
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"Notes",
"NotesPage",
"Reminders",
"Favorites",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
close?.();
} catch (e) {
@@ -227,35 +221,57 @@ export default function ReminderSheet({
return (
<View
style={{
paddingHorizontal: 12
paddingHorizontal: 12,
maxHeight: DDS.isTab ? "90%" : "99.99%"
}}
>
<View
style={{
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Heading size={SIZE.lg}>Set reminder</Heading>
<Button
title="Save"
type="accent"
height={40}
style={{
borderRadius: 100,
paddingHorizontal: 24
}}
fontSize={SIZE.md}
onPress={saveReminder}
/>
</View>
<Dialog context="local" />
<ScrollView keyboardShouldPersistTaps="always">
<ScrollView>
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
placeholder="Remind me of..."
onChangeText={(text) => (title.current = text)}
containerStyle={{ borderWidth: 0, borderBottomWidth: 1 }}
wrapperStyle={{
marginTop: 10
}}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
placeholder="Add a quick note"
placeholder="Add a short note"
onChangeText={(text) => (details.current = text)}
containerStyle={{
borderWidth: 0,
borderBottomWidth: 1,
maxHeight: 80,
marginTop: 10
maxHeight: 80
}}
multiline
textAlignVertical="top"
inputStyle={{
height: 80
minHeight: 80,
paddingVertical: 12
}}
height={80}
wrapperStyle={{
@@ -434,17 +450,13 @@ export default function ReminderSheet({
<DatePicker
date={date}
minimumDate={
dayjs(date).subtract(3, "months").isBefore(dayjs())
? dayjs().toDate()
: dayjs(date).subtract(3, "months").toDate()
}
maximumDate={dayjs(date).add(3, "months").toDate()}
onDateChange={handleConfirm}
textColor={colors.night ? "#ffffff" : "#000000"}
fadeToColor={colors.bg}
theme={colors.night ? "dark" : "light"}
is24hourSource="locale"
androidVariant="nativeAndroid"
mode={reminderMode === ReminderModes.Repeat ? "time" : "datetime"}
/>
@@ -561,15 +573,6 @@ export default function ReminderSheet({
alignSelf: "flex-start"
}}
/>
<Button
style={{
width: "100%"
}}
title="Save"
type="accent"
fontSize={SIZE.md}
onPress={saveReminder}
/>
</ScrollView>
</View>
);
@@ -583,6 +586,7 @@ ReminderSheet.present = (
presentSheet({
context: isSheet ? "local" : undefined,
enableGesturesInScrollView: true,
noBottomPadding: true,
component: (ref, close, update) => (
<ReminderSheet
actionSheetRef={ref}

View File

@@ -32,6 +32,7 @@ import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
const Sort = ({ type, screen }) => {
const colors = useThemeStore((state) => state.colors);
const isTopicSheet = screen === "TopicSheet";
const [groupOptions, setGroupOptions] = useState(
db.settings.getGroupOptions(type)
);
@@ -41,7 +42,7 @@ const Sort = ({ type, screen }) => {
layoutmanager.withSpringAnimation(600);
setGroupOptions(_groupOptions);
setTimeout(() => {
Navigation.queueRoutesForUpdate(screen);
if (screen !== "TopicSheet") Navigation.queueRoutesForUpdate(screen);
eSendEvent("groupOptionsUpdate");
eSendEvent(refreshNotesPage);
}, 1);
@@ -52,6 +53,9 @@ const Sort = ({ type, screen }) => {
...groupOptions,
sortDirection: groupOptions.sortDirection === "asc" ? "desc" : "asc"
};
if (type === "topics") {
_groupOptions.groupBy = "none";
}
await updateGroupOptions(_groupOptions);
};
@@ -119,7 +123,7 @@ const Sort = ({ type, screen }) => {
flexDirection: "row",
justifyContent: "flex-start",
flexWrap: "wrap",
borderBottomWidth: 1,
borderBottomWidth: isTopicSheet ? 0 : 1,
borderBottomColor: colors.nav,
marginBottom: 12,
paddingHorizontal: 12,
@@ -166,6 +170,9 @@ const Sort = ({ type, screen }) => {
...groupOptions,
sortBy: type === "trash" ? "dateDeleted" : item
};
if (type === "topics") {
_groupOptions.groupBy = "none";
}
await updateGroupOptions(_groupOptions);
}}
iconSize={SIZE.md}
@@ -175,64 +182,70 @@ const Sort = ({ type, screen }) => {
)}
</View>
<Heading
style={{
marginLeft: 12
}}
size={SIZE.lg}
>
Group by
</Heading>
<Seperator />
<View
style={{
borderRadius: 0,
flexDirection: "row",
flexWrap: "wrap",
paddingHorizontal: 12
}}
>
{Object.keys(GROUP).map((item) => (
<Button
key={item}
testID={"btn-" + item}
type={groupOptions.groupBy === GROUP[item] ? "grayBg" : "gray"}
buttonType={{
text:
groupOptions.groupBy === GROUP[item]
? colors.accent
: colors.icon
}}
onPress={async () => {
let _groupOptions = {
...groupOptions,
groupBy: GROUP[item]
};
if (item === "abc") {
_groupOptions.sortBy = "title";
_groupOptions.sortDirection = "asc";
} else {
if (groupOptions.sortBy === "title") {
_groupOptions.sortBy = "dateEdited";
_groupOptions.sortDirection = "desc";
}
}
updateGroupOptions(_groupOptions);
}}
height={40}
icon={groupOptions.groupBy === GROUP[item] ? "check" : null}
title={item.slice(0, 1).toUpperCase() + item.slice(1, item.length)}
{isTopicSheet ? null : (
<>
<Heading
style={{
paddingHorizontal: 8,
marginBottom: 10,
marginRight: 10
marginLeft: 12
}}
/>
))}
</View>
size={SIZE.lg}
>
Group by
</Heading>
<Seperator />
<View
style={{
borderRadius: 0,
flexDirection: "row",
flexWrap: "wrap",
paddingHorizontal: 12
}}
>
{Object.keys(GROUP).map((item) => (
<Button
key={item}
testID={"btn-" + item}
type={groupOptions.groupBy === GROUP[item] ? "grayBg" : "gray"}
buttonType={{
text:
groupOptions.groupBy === GROUP[item]
? colors.accent
: colors.icon
}}
onPress={async () => {
let _groupOptions = {
...groupOptions,
groupBy: GROUP[item]
};
if (item === "abc") {
_groupOptions.sortBy = "title";
_groupOptions.sortDirection = "asc";
} else {
if (groupOptions.sortBy === "title") {
_groupOptions.sortBy = "dateEdited";
_groupOptions.sortDirection = "desc";
}
}
updateGroupOptions(_groupOptions);
}}
height={40}
icon={groupOptions.groupBy === GROUP[item] ? "check" : null}
title={
item.slice(0, 1).toUpperCase() + item.slice(1, item.length)
}
style={{
paddingHorizontal: 8,
marginBottom: 10,
marginRight: 10
}}
/>
))}
</View>
</>
)}
</View>
);
};

View File

@@ -19,12 +19,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import qclone from "qclone";
import React, {
createContext,
RefObject,
useCallback,
useContext,
useEffect,
useRef,
useState
} from "react";
import { Animated, Dimensions, View, RefreshControl } from "react-native";
import { RefreshControl, View } from "react-native";
import ActionSheet, {
ActionSheetRef,
FlatList
@@ -37,7 +39,8 @@ import { TopicNotes } from "../../../screens/notes/topic-notes";
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent
eUnSubscribeEvent,
presentSheet
} from "../../../services/event-manager";
import useNavigationStore, {
NotebookScreenParams
@@ -49,18 +52,40 @@ import {
eOpenAddTopicDialog
} from "../../../utils/events";
import { normalize, SIZE } from "../../../utils/size";
import { NotebookType, TopicType } from "../../../utils/types";
import { GroupHeader, NotebookType, TopicType } from "../../../utils/types";
import { groupArray } from "@notesnook/core/utils/grouping";
import Config from "react-native-config";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import { MMKV } from "../../../common/database/mmkv";
import { openEditor } from "../../../screens/notes/common";
import { getTotalNotes, history } from "../../../utils";
import { Properties } from "../../properties";
import { deleteItems } from "../../../utils/functions";
import { presentDialog } from "../../dialog/functions";
import Config from "react-native-config";
import { notesnook } from "../../../../e2e/test.ids";
import { Properties } from "../../properties";
import Sort from "../sort";
import Heading from "../../ui/typography/heading";
type ConfigItem = { id: string; type: string };
class TopicSheetConfig {
static storageKey: "$$sp";
static makeId(item: ConfigItem) {
return `${TopicSheetConfig.storageKey}:${item.type}:${item.id}`;
}
static get(item: ConfigItem) {
return MMKV.getInt(TopicSheetConfig.makeId(item)) || 0;
}
static set(item: ConfigItem, index = 0) {
MMKV.setInt(TopicSheetConfig.makeId(item), index);
}
}
export const TopicsSheet = () => {
const [collapsed, setCollapsed] = useState(false);
const currentScreen = useNavigationStore((state) => state.currentScreen);
const canShow =
currentScreen.name === "Notebook" || currentScreen.name === "TopicNotes";
@@ -75,12 +100,19 @@ export const TopicsSheet = () => {
const [enabled, setEnabled] = useState(false);
const colors = useThemeStore((state) => state.colors);
const ref = useRef<ActionSheetRef>(null);
const [topics, setTopics] = useState(notebook ? qclone(notebook.topics) : []);
const [animations] = useState({
translate: new Animated.Value(0),
display: new Animated.Value(-5000),
opacity: new Animated.Value(0)
});
const isTopic = currentScreen.name === "TopicNotes";
const [topics, setTopics] = useState(
notebook
? qclone(
groupArray(notebook.topics, db.settings?.getGroupOptions("topics"))
)
: []
);
const [groupOptions, setGroupOptions] = useState(
db.settings?.getGroupOptions("topics")
);
const onRequestUpdate = React.useCallback(
(data?: NotebookScreenParams) => {
if (!canShow) return;
@@ -89,15 +121,34 @@ export const TopicsSheet = () => {
?.data as NotebookType;
if (_notebook) {
setNotebook(_notebook);
setTopics(qclone(_notebook.topics));
setTopics(
qclone(
groupArray(_notebook.topics, db.settings?.getGroupOptions("topics"))
)
);
}
},
[notebook, canShow]
[canShow, notebook]
);
const onUpdate = useCallback(() => {
setGroupOptions({ ...(db.settings?.getGroupOptions("topics") as any) });
onRequestUpdate();
}, [onRequestUpdate]);
useEffect(() => {
eSubscribeEvent("groupOptionsUpdate", onUpdate);
return () => {
eUnSubscribeEvent("groupOptionsUpdate", onUpdate);
};
}, [onUpdate]);
useEffect(() => {
const onTopicUpdate = () => {
onRequestUpdate();
setTimeout(() => {
onRequestUpdate();
}, 1);
};
eSubscribeEvent(eOnTopicSheetUpdate, onTopicUpdate);
eSubscribeEvent(eOnNewTopicAdded, onRequestUpdate);
@@ -117,9 +168,16 @@ export const TopicsSheet = () => {
loading: "Loading notebook topics"
};
const renderTopic = ({ item, index }: { item: TopicType; index: number }) => (
<TopicItem item={item} index={index} />
);
const renderTopic = ({
item,
index
}: {
item: TopicType | GroupHeader;
index: number;
}) =>
(item as GroupHeader).type === "header" ? null : (
<TopicItem sheetRef={ref} item={item as TopicType} index={index} />
);
const selectionContext = {
selection: selection,
@@ -146,29 +204,37 @@ export const TopicsSheet = () => {
useEffect(() => {
if (canShow) {
const isTopic = currentScreen.name === "TopicNotes";
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
if (!ref.current?.isOpen()) {
animations.display.setValue(5000);
animations.opacity.setValue(0);
}
if (id) {
onRequestUpdate({
item: db.notebooks?.notebook(id).data
} as any);
}
ref.current?.show();
setTimeout(() => {
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
const notebook = db.notebooks?.notebook(id as string)?.data;
const snapPoint = isTopic
? 0
: TopicSheetConfig.get({
type: isTopic ? "topic" : "notebook",
id: currentScreen.id as string
});
if (ref.current?.isOpen()) {
ref.current?.snapToIndex(snapPoint);
} else {
ref.current?.show(snapPoint);
}
if (notebook) {
onRequestUpdate({
item: notebook
} as any);
}
}, 300);
} else {
ref.current?.hide();
}
}, [
animations.display,
animations.opacity,
canShow,
currentScreen?.id,
currentScreen.name,
currentScreen?.notebookId,
onRequestUpdate
onRequestUpdate,
isTopic
]);
return (
@@ -176,14 +242,27 @@ export const TopicsSheet = () => {
ref={ref}
isModal={false}
containerStyle={{
maxHeight: 400,
maxHeight: 300,
borderTopRightRadius: 15,
borderTopLeftRadius: 15,
backgroundColor: colors.bg,
borderWidth: 1,
borderColor: colors.border,
borderColor: colors.nav,
borderBottomWidth: 0
}}
openAnimationConfig={{
friction: 10
}}
onSnapIndexChange={(index) => {
setCollapsed(index === 0);
TopicSheetConfig.set(
{
type: isTopic ? "topic" : "notebook",
id: currentScreen.id as string
},
index
);
}}
closable={!canShow}
elevation={10}
indicatorStyle={{
@@ -191,66 +270,44 @@ export const TopicsSheet = () => {
backgroundColor: colors.nav
}}
keyboardHandlerEnabled={false}
snapPoints={Config.isTesting === "true" ? [100] : [15, 100]}
initialSnapIndex={0}
snapPoints={Config.isTesting === "true" ? [100] : [25, 100]}
initialSnapIndex={1}
backgroundInteractionEnabled
onChange={(position, height) => {
animations.translate.setValue(position - 60);
const h = Dimensions.get("window").height;
const minPos = h - height;
if (position - 100 < minPos || !canShow) {
animations.display.setValue(5000);
animations.opacity.setValue(0);
} else {
animations.display.setValue(0);
setTimeout(() => {
animations.opacity.setValue(1);
}, 300);
}
}}
gestureEnabled
ExtraOverlayComponent={
<Animated.View
style={{
top: animations.translate,
position: "absolute",
right: 12,
opacity: animations.opacity,
transform: [
{
translateY: animations.display
}
]
}}
>
<PressableButton
testID={notesnook.buttons.add}
type="accent"
accentColor={"accent"}
accentText="light"
onPress={openEditor}
customStyle={{
borderRadius: 100
}}
>
<View
style={{
alignItems: "center",
justifyContent: "center",
height: normalize(60),
width: normalize(60)
}}
>
<Icon name="plus" color="white" size={SIZE.xxl} />
</View>
</PressableButton>
</Animated.View>
}
>
<View
style={{
maxHeight: 400,
height: 400,
position: "absolute",
right: 12,
marginTop: -80
}}
>
<PressableButton
testID={notesnook.buttons.add}
type="accent"
accentColor={"accent"}
accentText="light"
onPress={openEditor}
customStyle={{
borderRadius: 100
}}
>
<View
style={{
alignItems: "center",
justifyContent: "center",
height: normalize(60),
width: normalize(60)
}}
>
<Icon name="plus" color="white" size={SIZE.xxl} />
</View>
</PressableButton>
</View>
<View
style={{
maxHeight: 300,
height: 300,
width: "100%"
}}
>
@@ -283,7 +340,7 @@ export const TopicsSheet = () => {
selection.length > 1 ? "topics" : "topics"
}`,
paragraph: `Are you sure you want to delete ${
selection.length > 1 ? "these topicss?" : "this topics?"
selection.length > 1 ? "these topics?" : "this topic?"
}`,
positiveText: "Delete",
negativeText: "Cancel",
@@ -304,17 +361,57 @@ export const TopicsSheet = () => {
size={22}
/>
) : (
<IconButton
name="plus"
onPress={PLACEHOLDER_DATA.action}
testID="add-topic-button"
color={colors.pri}
size={22}
customStyle={{
width: 40,
height: 40
}}
/>
<>
<IconButton
name={
groupOptions?.sortDirection === "asc"
? "sort-ascending"
: "sort-descending"
}
onPress={() => {
presentSheet({
component: <Sort screen="TopicSheet" type="topics" />
});
}}
testID="group-topic-button"
color={colors.pri}
size={22}
customStyle={{
width: 40,
height: 40
}}
/>
<IconButton
name="plus"
onPress={PLACEHOLDER_DATA.action}
testID="add-topic-button"
color={colors.pri}
size={22}
customStyle={{
width: 40,
height: 40
}}
/>
<IconButton
name={collapsed ? "chevron-up" : "chevron-down"}
onPress={() => {
if (ref.current?.currentSnapIndex() !== 0) {
setCollapsed(true);
ref.current?.snapToIndex(0);
} else {
setCollapsed(false);
ref.current?.snapToIndex(1);
}
}}
color={colors.pri}
size={22}
customStyle={{
width: 40,
height: 40
}}
/>
</>
)}
</View>
</View>
@@ -334,7 +431,7 @@ export const TopicsSheet = () => {
progressBackgroundColor={colors.bg}
/>
}
keyExtractor={(item) => item.id}
keyExtractor={(item) => (item as TopicType).id}
renderItem={renderTopic}
ListEmptyComponent={
<View
@@ -342,7 +439,7 @@ export const TopicsSheet = () => {
flex: 1,
justifyContent: "center",
alignItems: "center",
height: 300
height: 200
}}
>
<Paragraph color={colors.icon}>No topics</Paragraph>
@@ -364,12 +461,20 @@ const SelectionContext = createContext<{
}>({
selection: [],
enabled: false,
setEnabled: (value: boolean) => {},
toggleSelection: (item: TopicType) => {}
setEnabled: (_value: boolean) => {},
toggleSelection: (_item: TopicType) => {}
});
const useSelection = () => useContext(SelectionContext);
const TopicItem = ({ item, index }: { item: TopicType; index: number }) => {
const TopicItem = ({
item,
index,
sheetRef
}: {
item: TopicType;
index: number;
sheetRef: RefObject<ActionSheetRef>;
}) => {
const screen = useNavigationStore((state) => state.currentScreen);
const colors = useThemeStore((state) => state.colors);
const selection = useSelection();

View File

@@ -164,7 +164,6 @@ export const PinItem = React.memo(
}}
fontSize={SIZE.md}
width="95%"
height={50}
customStyle={{
marginBottom: 30
}}

View File

@@ -92,7 +92,7 @@ const Input = ({
button,
onBlurInput,
onPress,
height = 50,
height = 45,
fontSize = SIZE.md,
onFocusInput,
buttons,

View File

@@ -38,7 +38,8 @@ const SheetWrapper = ({
keyboardMode,
overlay,
overlayOpacity = 0.3,
enableGesturesInScrollView = false
enableGesturesInScrollView = false,
bottomPadding = true
}) => {
const colors = useThemeStore((state) => state.colors);
const deviceMode = useSettingStore((state) => state.deviceMode);
@@ -119,14 +120,16 @@ const SheetWrapper = ({
>
<BouncingView>
{children}
<View
style={{
height:
Platform.OS === "ios" && insets.bottom !== 0
? insets.bottom + 5
: 20
}}
/>
{bottomPadding ? (
<View
style={{
height:
Platform.OS === "ios" && insets.bottom !== 0
? insets.bottom + 5
: 20
}}
/>
) : null}
</BouncingView>
</ActionSheet>
);

View File

@@ -19,13 +19,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { FeatureType } from "./components/sheets/new-feature";
export const features: FeatureType[] = [
{
title: "Top level notes in notebooks",
body: "Now you can add notes to Notesnooks directly without creating any topics."
},
{
title: "Image preview & download",
body: "Now you can preview images in fullscreen & download them from editor"
}
];
export const features: FeatureType[] = [];

View File

@@ -52,7 +52,11 @@ import { useThemeStore } from "../stores/use-theme-store";
import { useUserStore } from "../stores/use-user-store";
import { toTXT } from "../utils";
import { toggleDarkMode } from "../utils/color-scheme/utils";
import { eOpenAddTopicDialog, eOpenLoginDialog } from "../utils/events";
import {
eOnTopicSheetUpdate,
eOpenAddTopicDialog,
eOpenLoginDialog
} from "../utils/events";
import { deleteItems } from "../utils/functions";
import { sleep } from "../utils/time";
@@ -144,13 +148,7 @@ export const useActions = ({ close = () => null, item }) => {
if (!item.id) return;
close();
await db.notes.note(item.id).favorite();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
Navigation.queueRoutesForUpdate();
}
async function pinItem() {
@@ -158,14 +156,7 @@ export const useActions = ({ close = () => null, item }) => {
close();
let type = item.type;
await db[`${type}s`][type](item.id).pin();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Notebooks"
);
Navigation.queueRoutesForUpdate();
}
async function pinToNotifications() {
@@ -179,9 +170,17 @@ export const useActions = ({ close = () => null, item }) => {
checkNotifPinned();
return;
}
if (item.locked) return;
let html = await db.notes.note(item.id).content();
let text = await toTXT(item);
if (item.locked) {
ToastEvent.show({
heading: "Note is locked",
type: "error",
message: "Locked notes cannot be pinned to notifications",
context: "local"
});
return;
}
let text = await toTXT(item, false);
let html = text.replace(/\n/g, "<br />");
Notifications.displayNotification({
title: item.title,
message: item.headline || text,
@@ -200,15 +199,7 @@ export const useActions = ({ close = () => null, item }) => {
if (!checkNoteSynced()) return;
close();
await db.trash.restore(item.id);
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Notebooks",
"Trash"
);
Navigation.queueRoutesForUpdate();
let type = item.type === "trash" ? item.itemType : item.type;
ToastEvent.show({
heading:
@@ -324,13 +315,7 @@ export const useActions = ({ close = () => null, item }) => {
let note = db.notes.note(item.id).data;
if (note.locked) {
close();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
Navigation.queueRoutesForUpdate();
}
} catch (e) {
close();
@@ -396,16 +381,11 @@ export const useActions = ({ close = () => null, item }) => {
positivePress: async (value) => {
if (!value || value === "" || value.trimStart().length == 0) return;
await db.tags.rename(item.id, db.tags.sanitize(value));
useTagStore.getState().setTags();
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Tags"
);
setImmediate(() => {
useTagStore.getState().setTags();
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate();
});
},
input: true,
defaultValue: alias,
@@ -448,24 +428,16 @@ export const useActions = ({ close = () => null, item }) => {
? "This reminder will be removed"
: "This tag will be removed from all notes.",
positivePress: async () => {
const routes = [];
routes.push(
"TaggedNotes",
"ColoredNotes",
"Notes",
"NotesPage",
"Reminders",
"Favorites"
);
if (item.type === "reminder") {
await db.reminders.remove(item.id);
} else {
await db.tags.remove(item.id);
useTagStore.getState().setTags();
routes.push("Tags");
}
Navigation.queueRoutesForUpdate(...routes);
useRelationStore.getState().update();
setImmediate(() => {
useTagStore.getState().setTags();
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
});
},
positiveText: "Delete",
positiveType: "errorShade"
@@ -503,15 +475,8 @@ export const useActions = ({ close = () => null, item }) => {
},
item.id
);
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Notebook",
"Notebooks"
);
Navigation.queueRoutesForUpdate();
eSendEvent(eOnTopicSheetUpdate);
close();
}
@@ -519,15 +484,7 @@ export const useActions = ({ close = () => null, item }) => {
const currentScreen = useNavigationStore.getState().currentScreen;
if (currentScreen.name !== "Notebook") return;
await db.relations.unlink({ type: "notebook", id: currentScreen.id }, item);
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Notebook",
"Notebooks"
);
Navigation.queueRoutesForUpdate();
close();
}
@@ -542,12 +499,14 @@ export const useActions = ({ close = () => null, item }) => {
negativeText: "Cancel",
positivePress: async () => {
await db.trash.delete(item.id);
Navigation.queueRoutesForUpdate("Trash");
useSelectionStore.getState().setSelectionMode(false);
ToastEvent.show({
heading: "Permanantly deleted items",
type: "success",
context: "local"
setImmediate(() => {
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(false);
ToastEvent.show({
heading: "Permanantly deleted items",
type: "success",
context: "local"
});
});
},
positiveType: "errorShade"
@@ -565,19 +524,22 @@ export const useActions = ({ close = () => null, item }) => {
}
async function exportNote() {
if (item.locked) {
ToastEvent.show({
heading: "Note is locked",
type: "error",
message: "Locked notes cannot be exported",
context: "local"
});
return;
}
ExportNotesSheet.present([item]);
}
async function toggleLocalOnly() {
if (!checkNoteSynced() || !user) return;
db.notes.note(item.id).localOnly();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
Navigation.queueRoutesForUpdate();
close();
}
@@ -588,26 +550,14 @@ export const useActions = ({ close = () => null, item }) => {
useEditorStore.getState().setReadonly(current);
// tiny.call(EditorWebView, tiny.toogleReadMode(current ? 'readonly' : 'design'));
}
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
Navigation.queueRoutesForUpdate();
close();
};
const duplicateNote = async () => {
if (!checkNoteSynced()) return;
await db.notes.note(item.id).duplicate();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
Navigation.queueRoutesForUpdate();
close();
};
const actions = [
@@ -686,7 +636,7 @@ export const useActions = ({ close = () => null, item }) => {
id: "pin-to-notifications",
title:
notifPinned !== null
? "Unpin from Notifications"
? "Unpin from notifications"
: "Pin to notifications",
icon: "message-badge-outline",
on: notifPinned !== null,
@@ -835,15 +785,7 @@ export const useActions = ({ close = () => null, item }) => {
});
Notifications.scheduleNotification(item);
useRelationStore.getState().update();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"NotesPage",
"Reminders"
);
Navigation.queueRoutesForUpdate();
}
},
{

View File

@@ -402,11 +402,9 @@ export const useAppEvents = () => {
const checkAutoBackup = useCallback(async () => {
if (verify || syncing) {
console.log("backup is waiting");
refValues.current.backupDidWait = true;
return;
}
console.log("backup running immediate");
const user = await db.user.getUser();
if (PremiumService.get() && user) {
if (SettingsService.get().reminder === "off") {
@@ -427,7 +425,6 @@ export const useAppEvents = () => {
useEffect(() => {
if (!verify && !syncing && refValues.current.backupDidWait) {
console.log("backup run after wait");
refValues.current.backupDidWait = false;
checkAutoBackup();
}
@@ -561,7 +558,7 @@ export const useAppEvents = () => {
let shareExtensionOpened = MMKV.getString("shareExtensionOpened");
if (notesAddedFromIntent) {
if (Platform.OS === "ios") {
await db.init();
await db.initCollections();
await db.notes.init();
}
useNoteStore.getState().setNotes();

View File

@@ -69,6 +69,7 @@ import {
import { editorRef, tabBarRef } from "../utils/global-refs";
import { sleep } from "../utils/time";
import { NavigationStack } from "./navigation-stack";
import changeNavigationBarColor from "react-native-navigation-bar-color";
const _TabsHolder = () => {
const colors = useThemeStore((state) => state.colors);
@@ -386,6 +387,10 @@ const _TabsHolder = () => {
};
}, []);
useEffect(() => {
changeNavigationBarColor(colors.bg, !colors.night, true);
}, [colors.night, colors.bg]);
return (
<View
onLayout={_onLayout}

View File

@@ -4,8 +4,6 @@
"main": "./App.js",
"license": "GPL-3.0-or-later",
"dependencies": {
"react": "18.0.0",
"react-native": "0.69.7",
"@flyerhq/react-native-link-preview": "^1.6.0",
"@mdi/js": "^6.7.96",
"absolutify": "^0.1.0",
@@ -15,7 +13,7 @@
"html-to-text": "8.1.0",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "^0.9.0-alpha.12",
"react-native-actions-sheet": "0.9.0-alpha.18",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-drax": "^0.10.2",
"react-native-image-zoom-viewer": "^3.0.1",

View File

@@ -28,12 +28,12 @@ import { Button } from "../../components/ui/button";
import { IconButton } from "../../components/ui/icon-button";
import Paragraph from "../../components/ui/typography/paragraph";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { DDS } from "../../services/device-detection";
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { eClearEditor } from "../../utils/events";
import { SIZE } from "../../utils/size";
@@ -42,7 +42,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
const colors = useThemeStore((state) => state.colors);
const [error, setError] = useState(false);
const opacity = useSharedValue(1);
const translateValue = useSharedValue(6000);
const translateValue = useSharedValue(0);
const deviceMode = useSettingStore((state) => state.deviceMode);
const isTablet = deviceMode !== "mobile";
const insets = useGlobalSafeAreaInsets();
const isDefaultEditor = editorId === "";
const timers = useRef({
@@ -50,6 +52,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
error: 0,
closing: 0
});
const loadingState = useRef({
startTime: 0
});
const clearTimers = () => {
clearTimeout(timers.current.loading);
@@ -62,6 +67,7 @@ const EditorOverlay = ({ editorId = "", editor }) => {
editorState().overlay = true;
clearTimers();
if (_loading) {
loadingState.current.startTime = Date.now();
opacity.value = 1;
translateValue.value = 0;
timers.current.error = setTimeout(() => {
@@ -69,7 +75,14 @@ const EditorOverlay = ({ editorId = "", editor }) => {
}, 15 * 1000);
} else {
clearTimers();
setTimeout(() => {
const timeDiffSinceLoadStarted =
Date.now() - loadingState.current.startTime > 300 ? 300 : 0;
if (!timeDiffSinceLoadStarted) {
setError(false);
editorState().overlay = false;
opacity.value = 0;
translateValue.value = 6000;
} else {
setError(false);
editorState().overlay = false;
opacity.value = withTiming(0, {
@@ -78,19 +91,24 @@ const EditorOverlay = ({ editorId = "", editor }) => {
setTimeout(() => {
translateValue.value = 6000;
}, 500);
}, 0);
}
}
},
[opacity, translateValue]
);
useEffect(() => {
setTimeout(() => {
if (!loadingState.current.startTime) {
translateValue.value = 6000;
}
}, 1000);
eSubscribeEvent("loadingNote" + editorId, load);
return () => {
clearTimers();
eUnSubscribeEvent("loadingNote" + editorId, load);
};
}, [editorId, load]);
}, [editorId, load, translateValue]);
const animatedStyle = useAnimatedStyle(() => {
return {
@@ -140,7 +158,7 @@ const EditorOverlay = ({ editorId = "", editor }) => {
paddingRight: 12
}}
>
{DDS.isTablet() ? (
{isTablet ? (
<View />
) : (
<IconButton
@@ -184,7 +202,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
alignItems: "center",
flexDirection: "row",
paddingHorizontal: 10,
marginTop: 10
marginTop: 5,
borderWidth: 1,
borderColor: colors.border
}}
>
<Paragraph color={colors.icon} size={13}>

View File

@@ -129,7 +129,6 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
};
setInsets = async (insets: EdgeInsets) => {
logger.info("setInsets", insets);
await this.doAsync(`
if (typeof safeAreaController !== "undefined") {
safeAreaController.update(${JSON.stringify(insets)})

View File

@@ -189,12 +189,7 @@ export const useEditorEvents = (
setImmediate(() => {
useEditorStore.getState().setCurrentlyEditingNote(null);
setTimeout(() => {
Navigation.queueRoutesForUpdate(
"ColoredNotes",
"Notes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
}, 500);
});
editorState().currentlyEditing = false;
@@ -273,12 +268,28 @@ export const useEditorEvents = (
(event: WebViewMessageEvent) => {
const data = event.nativeEvent.data;
const editorMessage = JSON.parse(data) as EditorMessage;
if (editorMessage.type === EventTypes.content) {
editor.saveContent({
type: editorMessage.type,
content: editorMessage.value as string,
forSessionId: editorMessage.sessionId
});
} else if (editorMessage.type === EventTypes.title) {
editor.saveContent({
type: editorMessage.type,
title: editorMessage.value as string,
forSessionId: editorMessage.sessionId
});
}
if (
editorMessage.sessionId !== editor.sessionId &&
editorMessage.type !== EditorEvents.status
) {
return;
}
switch (editorMessage.type) {
case EventTypes.logger:
logger.info("[WEBVIEW LOG]", editorMessage.value);
@@ -286,21 +297,8 @@ export const useEditorEvents = (
case EventTypes.contentchange:
editor.onContentChanged();
break;
case EventTypes.content:
editor.saveContent({
type: editorMessage.type,
content: editorMessage.value as string
});
break;
case EventTypes.selection:
break;
case EventTypes.title:
editor.saveContent({
type: editorMessage.type,
title: editorMessage.value as string
});
break;
case EventTypes.reminders:
if (!editor.note.current) {
ToastEvent.show({
@@ -337,13 +335,7 @@ export const useEditorEvents = (
.then(async () => {
useTagStore.getState().setTags();
await editor.commands.setTags(editor.note.current);
Navigation.queueRoutesForUpdate(
"ColoredNotes",
"Notes",
"TaggedNotes",
"TopicNotes",
"Tags"
);
Navigation.queueRoutesForUpdate();
});
}
break;

View File

@@ -77,6 +77,7 @@ export const useEditor = (
const lastContentChangeTime = useRef<number>(0);
const lock = useRef(false);
const loadedImages = useRef<{ [name: string]: boolean }>({});
const lockedSessionId = useRef<string>();
const postMessage = useCallback(
async <T>(type: string, data: T) =>
@@ -152,7 +153,7 @@ export const useEditor = (
);
const reset = useCallback(
async (resetState = true) => {
async (resetState = true, resetContent = true) => {
currentNote.current?.id && db.fs.cancel(currentNote.current.id);
currentNote.current = null;
loadedImages.current = {};
@@ -161,10 +162,10 @@ export const useEditor = (
sessionHistoryId.current = undefined;
saveCount.current = 0;
useEditorStore.getState().setReadonly(false);
postMessage(EditorEvents.title, "");
resetContent && postMessage(EditorEvents.title, "");
lastContentChangeTime.current = 0;
await commands.clearContent();
await commands.clearTags();
resetContent && (await commands.clearContent());
resetContent && (await commands.clearTags());
if (resetState) {
isDefaultEditor &&
useEditorStore.getState().setCurrentlyEditingNote(null);
@@ -239,9 +240,15 @@ export const useEditor = (
if (
useEditorStore.getState().currentEditingNote !== id &&
isDefaultEditor
isDefaultEditor &&
state.current.currentlyEditing
) {
setTimeout(() => {
if (
(currentNote.current?.id && currentNote.current?.id !== id) ||
!state.current.currentlyEditing
)
return;
id && useEditorStore.getState().setCurrentlyEditingNote(id);
});
}
@@ -262,12 +269,7 @@ export const useEditor = (
currentNote.current?.headline?.slice(0, 200) !==
note.headline?.slice(0, 200)
) {
Navigation.queueRoutesForUpdate(
"ColoredNotes",
"Notes",
"TaggedNotes",
"TopicNotes"
);
Navigation.queueRoutesForUpdate();
}
}
@@ -367,16 +369,26 @@ export const useEditor = (
useEditorStore.getState().setReadonly(false);
} else {
if (!item.forced && currentNote.current?.id === item.id) return;
state.current.movedAway = false;
state.current.currentlyEditing = true;
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
overlay(true, item);
currentNote.current && (await reset(false));
currentNote.current && (await reset(false, false));
await loadContent(item as NoteType);
if (
!currentContent.current?.data ||
currentContent.current?.data.length < 50000
) {
overlay(false);
} else {
overlay(true);
}
lastContentChangeTime.current = item.dateEdited;
const nextSessionId = makeSessionId(item as NoteType);
lockedSessionId.current = nextSessionId;
sessionHistoryId.current = Date.now();
setSessionId(nextSessionId);
commands.setSessionId(nextSessionId);
sessionIdRef.current = nextSessionId;
await commands.setSessionId(nextSessionId);
currentNote.current = item as NoteType;
await commands.setStatus(timeConverter(item.dateEdited), "Saved");
await postMessage(EditorEvents.title, item.title);
@@ -384,6 +396,11 @@ export const useEditor = (
useEditorStore.getState().setReadonly(item.readonly);
await commands.setTags(currentNote.current);
commands.setSettings();
setTimeout(() => {
if (lockedSessionId.current === nextSessionId) {
lockedSessionId.current = undefined;
}
}, 300);
overlay(false);
loadImages();
}
@@ -490,14 +507,16 @@ export const useEditor = (
({
title,
content,
type
type,
forSessionId
}: {
title?: string;
content?: string;
type: string;
forSessionId: string;
}) => {
if (lock.current || lockedSessionId.current === forSessionId) return;
lastContentChangeTime.current = Date.now();
if (lock.current) return;
if (type === EditorEvents.content) {
currentContent.current = {
data: content,
@@ -505,22 +524,27 @@ export const useEditor = (
noteId: currentNote.current?.id as string
};
}
const noteIdFromSessionId =
!forSessionId || forSessionId.startsWith("session")
? null
: forSessionId.split("_")[0];
const noteId = noteIdFromSessionId || currentNote.current?.id;
const params = {
title,
data: content,
type: "tiptap",
sessionId,
id: currentNote.current?.id,
sessionId: forSessionId,
id: noteId,
sessionHistoryId: sessionHistoryId.current
};
withTimer(
currentNote.current?.id || "newnote",
noteId || "newnote",
() => {
if (
currentNote.current &&
!params.id &&
params.sessionId === sessionId
params.sessionId === forSessionId
) {
params.id = currentNote.current?.id;
}
@@ -530,10 +554,10 @@ export const useEditor = (
}
saveNote(params);
},
500
150
);
},
[sessionId, withTimer, onChange, saveNote]
[withTimer, onChange, saveNote]
);
const restoreEditorState = useCallback(async () => {

View File

@@ -22,9 +22,9 @@ import { db } from "../../common/database";
import DelayLayout from "../../components/delay-layout";
import List from "../../components/list";
import { NotebookHeader } from "../../components/list-items/headers/notebook-header";
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
@@ -33,7 +33,7 @@ import SearchService from "../../services/search";
import useNavigationStore, {
NotebookScreenParams
} from "../../stores/use-navigation-store";
import { eOnNewTopicAdded, eOpenAddNotebookDialog } from "../../utils/events";
import { eOnNewTopicAdded } from "../../utils/events";
import { NotebookType } from "../../utils/types";
import { openEditor, setOnFirstSave } from "../notes/common";
const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
@@ -148,7 +148,7 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
ListHeader={
<NotebookHeader
onEditNotebook={() => {
eSendEvent(eOpenAddNotebookDialog, params.current.item);
AddNotebookSheet.present(params.current.item);
}}
notebook={params.current.item}
/>

View File

@@ -94,15 +94,6 @@ async function onNoteCreated(id: string, params: FirstSaveData) {
{ type: "notebook", id: params.id },
{ type: "note", id: id }
);
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Notebook",
"Notebooks"
);
editorState().onNoteCreated = null;
break;
}
@@ -116,41 +107,18 @@ async function onNoteCreated(id: string, params: FirstSaveData) {
id
);
editorState().onNoteCreated = null;
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Notebook",
"Notebooks"
);
eSendEvent(eOnTopicSheetUpdate);
break;
}
case "tag": {
await db.notes?.note(id).tag(params.id);
editorState().onNoteCreated = null;
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
useTagStore.getState().setTags();
break;
}
case "color": {
await db.notes?.note(id).color(params.color);
editorState().onNoteCreated = null;
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes"
);
useMenuStore.getState().setColorNotes();
break;
}
@@ -158,4 +126,5 @@ async function onNoteCreated(id: string, params: FirstSaveData) {
break;
}
}
Navigation.queueRoutesForUpdate();
}

View File

@@ -18,9 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useRef, useState } from "react";
import { View } from "react-native";
import { db } from "../../common/database";
import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
import List from "../../components/list";
import { IconButton } from "../../components/ui/icon-button";
import Paragraph from "../../components/ui/typography/paragraph";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import {
eSubscribeEvent,
@@ -34,7 +38,9 @@ import useNavigationStore, {
RouteName
} from "../../stores/use-navigation-store";
import { useNoteStore } from "../../stores/use-notes-store";
import { SIZE } from "../../utils/size";
import { NoteType, TopicType } from "../../utils/types";
import Notebook from "../notebook/index";
import {
getAlias,
openEditor,
@@ -42,13 +48,6 @@ import {
setOnFirstSave,
toCamelCase
} from "./common";
import { View } from "react-native";
import { db } from "../../common/database";
import Paragraph from "../../components/ui/typography/paragraph";
import { IconButton } from "../../components/ui/icon-button";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import Notebook from "../notebook/index";
export const WARNING_DATA = {
title: "Some notes in this topic are not synced"
};
@@ -99,7 +98,6 @@ const NotesPage = ({
}: RouteProps<
"NotesPage" | "TaggedNotes" | "Monographs" | "ColoredNotes" | "TopicNotes"
>) => {
const colors = useThemeStore((state) => state.colors);
const params = useRef<NotesScreenParams>(route?.params);
const [notes, setNotes] = useState<NoteType[]>(get(route.params, true));
const loading = useNoteStore((state) => state.loading);
@@ -183,8 +181,9 @@ const NotesPage = ({
if (isNew) setLoadingNotes(true);
const notes = get(params.current, true) as NoteType[];
if (
(item.type === "tag" || item.type === "color") &&
(!notes || notes.length === 0)
((item.type === "tag" || item.type === "color") &&
(!notes || notes.length === 0)) ||
(item.type === "topic" && !notes)
) {
return Navigation.goBack();
}
@@ -245,19 +244,23 @@ const NotesPage = ({
>
Notebooks
</Paragraph>
<IconButton
name="chevron-right"
size={14}
customStyle={{ width: 25, height: 25 }}
/>
<Paragraph
onPress={() => {
Notebook.navigate(notebook, true);
}}
size={SIZE.xs}
>
{notebook.title}
</Paragraph>
{notebook ? (
<>
<IconButton
name="chevron-right"
size={14}
customStyle={{ width: 25, height: 25 }}
/>
<Paragraph
onPress={() => {
Notebook.navigate(notebook, true);
}}
size={SIZE.xs}
>
{notebook.title}
</Paragraph>
</>
) : null}
</View>
) : null}
<List
@@ -276,7 +279,9 @@ const NotesPage = ({
placeholderData={placeholderData}
/>
{notes?.length > 0 || (isFocused && !isMonograph) ? (
{!isMonograph &&
route.name !== "TopicNotes" &&
(notes?.length > 0 || isFocused) ? (
<FloatingButton title="Create a note" onPress={onPressFloatingButton} />
) : null}
</DelayLayout>

View File

@@ -75,7 +75,11 @@ export const TopicNotes = ({
TopicNotes.get = (params: NotesScreenParams, grouped = true) => {
const { id, notebookId } = params.item as TopicType;
const notes = db.notebooks?.notebook(notebookId)?.topics.topic(id)?.all || [];
const topic = db.notebooks?.notebook(notebookId)?.topics.topic(id);
if (!topic) {
return null;
}
const notes = topic?.all || [];
return grouped
? groupArray(notes, db.settings?.getGroupOptions("notes"))
: notes;

View File

@@ -106,12 +106,6 @@ export const useDragState = create<DragState>(
return;
}
const preset = toolbarConfig?.preset as DragState["preset"];
logger.info(
"DragState",
"Init user toolbar config",
preset,
toolbarConfig?.config
);
set({
preset: preset,
data:

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import Animated, { FadeInDown, FadeOutDown } from "react-native-reanimated";
import Animated, { FadeInDown } from "react-native-reanimated";
import DelayLayout from "../../components/delay-layout";
import BaseDialog from "../../components/dialog/base-dialog";
import { ProgressBarComponent } from "../../components/ui/svg/lazy";
@@ -75,57 +75,55 @@ const Home = ({
return (
<DelayLayout delay={300} type="settings">
<View>
{loading && (
//@ts-ignore // Migrate to typescript required.
<BaseDialog animated={false} bounce={false} visible={true}>
{loading && (
//@ts-ignore // Migrate to typescript required.
<BaseDialog animated={false} bounce={false} visible={true}>
<View
style={{
width: "100%",
height: "100%",
backgroundColor: colors.bg,
justifyContent: "center",
alignItems: "center"
}}
>
<Heading color={colors.pri} size={SIZE.lg}>
Logging out
</Heading>
<Paragraph color={colors.icon}>
Please wait while we log out and clear app data.
</Paragraph>
<View
style={{
width: "100%",
height: "100%",
backgroundColor: colors.bg,
justifyContent: "center",
alignItems: "center"
flexDirection: "row",
width: 100,
marginTop: 15
}}
>
<Heading color={colors.pri} size={SIZE.lg}>
Logging out
</Heading>
<Paragraph color={colors.icon}>
Please wait while we log out and clear app data.
</Paragraph>
<View
style={{
flexDirection: "row",
width: 100,
marginTop: 15
}}
>
<ProgressBarComponent
height={5}
width={100}
animated={true}
useNativeDriver
indeterminate
indeterminateAnimationDuration={2000}
unfilledColor={colors.nav}
color={colors.accent}
borderWidth={0}
/>
</View>
<ProgressBarComponent
height={5}
width={100}
animated={true}
useNativeDriver
indeterminate
indeterminateAnimationDuration={2000}
unfilledColor={colors.nav}
color={colors.accent}
borderWidth={0}
/>
</View>
</BaseDialog>
)}
</View>
</BaseDialog>
)}
<Animated.FlatList
entering={FadeInDown}
exiting={FadeOutDown}
data={settingsGroups}
keyExtractor={keyExtractor}
ListFooterComponent={<View style={{ height: 200 }} />}
renderItem={renderItem}
/>
</View>
<Animated.FlatList
entering={FadeInDown}
data={settingsGroups}
windowSize={1}
keyExtractor={keyExtractor}
ListFooterComponent={<View style={{ height: 200 }} />}
renderItem={renderItem}
/>
</DelayLayout>
);
};

View File

@@ -42,12 +42,12 @@ export const SectionGroup = ({ item }: { item: SettingSection }) => {
color={colors.accent}
size={SIZE.xs}
>
{item.name.toUpperCase()}
{(item.name as string).toUpperCase()}
</Heading>
) : null}
{item.sections?.map((item) => (
<SectionItem key={item.name} item={item} />
<SectionItem key={item.name as string} item={item} />
))}
</View>
);

View File

@@ -169,6 +169,14 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
}
item.inputProperties?.onSubmitEditing?.(e);
}}
onChangeText={(text) => {
if (text) {
SettingsService.set({
[item.property as string]: text
});
}
item.inputProperties?.onSubmitEditing?.(text as any);
}}
containerStyle={{ marginTop: 12 }}
fwdRef={inputRef}
onLayout={() => {

View File

@@ -317,7 +317,6 @@ export const settingsGroups: SettingSection[] = [
await PremiumService.setPremiumStatus();
await BiometicService.resetCredentials();
MMKV.clearStore();
await db.init();
await clearAllStores();
SettingsService.init();
setTimeout(() => {
@@ -959,9 +958,7 @@ export const settingsGroups: SettingSection[] = [
onChange: () => {
ToastEvent.show({
heading: "Line spacing changed",
type: "success",
message:
"Close and reopen the current opened note or restart the app for changes to take affect."
type: "success"
});
}
}
@@ -1027,11 +1024,21 @@ export const settingsGroups: SettingSection[] = [
id: "join-telegram",
name: "Join our Telegram group",
description: "We are on telegram, let's talk",
// icon: 'telegram',
modifer: () => {
Linking.openURL("https://t.me/notesnook").catch(console.log);
}
},
{
id: "join-mastodom",
name: "Follow us on Mastodon",
description: "We are on mastodom",
icon: "mastodon",
modifer: () => {
Linking.openURL("https://fosstodon.org/@notesnook").catch(
console.log
);
}
},
{
id: "join-twitter",
name: "Follow us on twitter",

View File

@@ -87,7 +87,7 @@ export const TrashIntervalSelector = () => {
>
{[-1, 7, 30, 365].map((item) => (
<MenuItem
key={item.name}
key={item.toString()}
onPress={async () => {
if (item === -1) {
await PremiumService.verify(() => {

View File

@@ -114,7 +114,8 @@ export type PresentSheetOptions = {
actionsArray: SheetAction[];
learnMore: string;
learnMorePress: () => void;
enableGesturesInScrollView: boolean;
enableGesturesInScrollView?: boolean;
noBottomPadding?: boolean;
};
export function presentSheet(data: Partial<PresentSheetOptions>) {

View File

@@ -35,6 +35,7 @@ import { eOnNewTopicAdded } from "../utils/events";
import { rootNavigatorRef, tabBarRef } from "../utils/global-refs";
import { eSendEvent } from "./event-manager";
import SettingsService from "./settings";
import SearchService from "./search";
/**
* Routes that should be updated on focus
@@ -86,7 +87,8 @@ const routeUpdateFunctions: {
ColoredNotes: (params) => eSendEvent("ColoredNotes", params),
TopicNotes: (params) => eSendEvent("TopicNotes", params),
Monographs: (params) => eSendEvent("Monographs", params),
Reminders: () => useReminderStore.getState().setReminders()
Reminders: () => useReminderStore.getState().setReminders(),
Search: () => SearchService.updateAndSearch()
};
function clearRouteFromQueue(routeName: RouteName) {
@@ -106,10 +108,14 @@ function routeNeedsUpdate(routeName: RouteName, callback: () => void) {
}
}
function queueRoutesForUpdate(...routes: RouteName[]) {
function queueRoutesForUpdate(...routesToUpdate: RouteName[]) {
const routes =
routesToUpdate?.length > 0
? routesToUpdate
: (Object.keys(routeNames) as (keyof RouteParams)[]);
const currentScreen = useNavigationStore.getState().currentScreen;
if (routes.indexOf(currentScreen.name) > -1) {
routeUpdateFunctions[currentScreen.name]();
routeUpdateFunctions[currentScreen.name]?.();
clearRouteFromQueue(currentScreen.name);
// Remove focused screen from queue
routes.splice(routes.indexOf(currentScreen.name), 1);
@@ -125,7 +131,7 @@ function navigate<T extends RouteName>(
useNavigationStore.getState().update(screen, !!params?.canGoBack);
if (screen.name === "Notebook") routeUpdateFunctions["Notebook"](params);
if (screen.name.endsWith("Notes") && screen.name !== "Notes")
routeUpdateFunctions[screen.name](params);
routeUpdateFunctions[screen.name]?.(params);
//@ts-ignore Not sure how to fix this for now ignore it.
rootNavigatorRef.current?.navigate<RouteName>(screen.name, params);
}

View File

@@ -85,13 +85,20 @@ async function getNextMonthlyReminderDate(
return await getNextMonthlyReminderDate(reminder, dayjs().year() + 1);
}
async function initDatabase(notes = true) {
if (db.isInitialized) return;
await db.initCollections();
if (notes) {
await db.notes?.init();
}
}
const onEvent = async ({ type, detail }: Event) => {
const { notification, pressAction, input } = detail;
if (type === EventType.DELIVERED && Platform.OS === "android") {
const reminder = db.reminders?.reminder(notification?.id?.split("_")[0]);
if (reminder && reminder.recurringMode === "month") {
await db.init();
await db.notes?.init();
await initDatabase();
await scheduleNotification(reminder);
}
return;
@@ -100,8 +107,7 @@ const onEvent = async ({ type, detail }: Event) => {
notifee.decrementBadgeCount();
if (notification?.data?.type === "quickNote") return;
MMKV.removeItem("appState");
await db.init();
await db.notes?.init();
await initDatabase();
if (notification?.data?.type === "reminder") {
const reminder = db.reminders?.reminder(notification.id?.split("_")[0]);
await sleep(1000);
@@ -131,8 +137,7 @@ const onEvent = async ({ type, detail }: Event) => {
notifee.decrementBadgeCount();
switch (pressAction?.id) {
case "REMINDER_SNOOZE": {
await db.init();
await db.notes?.init();
await initDatabase();
const reminder = db.reminders?.reminder(
notification?.id?.split("_")[0]
);
@@ -151,8 +156,7 @@ const onEvent = async ({ type, detail }: Event) => {
break;
}
case "REMINDER_DISABLE": {
await db.init();
await db.notes?.init();
await initDatabase();
const reminder = db.reminders?.reminder(
notification?.id?.split("_")[0]
);
@@ -168,8 +172,7 @@ const onEvent = async ({ type, detail }: Event) => {
break;
}
case "UNPIN": {
await db.init();
await db.notes?.init();
await initDatabase();
remove(notification?.id as string);
const reminder = db.reminders?.reminder(
notification?.id?.split("_")[0]
@@ -198,7 +201,7 @@ const onEvent = async ({ type, detail }: Event) => {
reply_button_text: "Take note",
reply_placeholder_text: "Write something..."
});
await db.init();
await initDatabase(false);
await db.notes?.add({
content: {
type: "tiptap",
@@ -367,7 +370,8 @@ async function scheduleNotification(
function loadNote(id: string, jump: boolean) {
if (!id || id === "notesnook_note_input") return;
const note = db.notes?.note(id).data;
const note = db.notes?.note(id)?.data;
if (!note) return;
if (!DDS.isTab && jump) {
tabBarRef.current?.goToPage(1);
}

View File

@@ -26,7 +26,7 @@ let searchInformation = {
placeholder: "Search in all notes",
data: [],
type: "notes",
get: () => null
get: () => []
};
let keyword = null;

View File

@@ -19,22 +19,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { db } from "../common/database";
import Navigation from "../services/navigation";
import Notifications from "../services/notifications";
import { useFavoriteStore } from "./use-favorite-store";
import { useMenuStore } from "./use-menu-store";
import { RouteName } from "./use-navigation-store";
import { useNotebookStore } from "./use-notebook-store";
import { useNoteStore } from "./use-notes-store";
import { useRelationStore } from "./use-relation-store";
import { useReminderStore } from "./use-reminder-store";
import { useTagStore } from "./use-tag-store";
import { useTrashStore } from "./use-trash-store";
import { useReminderStore } from "./use-reminder-store";
import Notifications from "../services/notifications";
import { useRelationStore } from "./use-relation-store";
export function initAfterSync() {
useMenuStore.getState().setColorNotes();
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate(
...(Object.keys(Navigation.routeUpdateFunctions) as unknown as RouteName[])
);
Navigation.queueRoutesForUpdate();
// Whenever sync completes, try to reschedule
// any new/updated reminders.
Notifications.setupReminders(true);

View File

@@ -104,7 +104,7 @@ export const COLOR_SCHEME_PITCH_BLACK = {
light: "#ffffff",
transGray: "#ffffff10",
border: "#383838",
placeholder: "#404040"
placeholder: "#606060"
};
export const COLOR_SCHEME_DARK = {

View File

@@ -25,7 +25,7 @@ import SearchService from "../services/search";
import { useSelectionStore } from "../stores/use-selection-store";
import { useMenuStore } from "../stores/use-menu-store";
import { db } from "../common/database";
import { eClearEditor } from "./events";
import { eClearEditor, eOnTopicSheetUpdate } from "./events";
import { useRelationStore } from "../stores/use-relation-store";
import { presentDialog } from "../components/dialog/functions";
@@ -38,11 +38,14 @@ function deleteConfirmDialog(items, type, context) {
positiveText: "Delete",
negativeText: "Cancel",
positivePress: (value) => {
console.log(value);
resolve({ delete: true, deleteNotes: value });
setTimeout(() => {
resolve({ delete: true, deleteNotes: value });
});
},
onClose: () => {
resolve({ delete: false });
setTimeout(() => {
resolve({ delete: false });
});
},
context: context,
check: {
@@ -158,7 +161,7 @@ export const deleteItems = async (item, context) => {
}
}
Navigation.queueRoutesForUpdate(...routesForUpdate);
Navigation.queueRoutesForUpdate();
let msgPart = history.selectedItemsList.length === 1 ? " item" : " items";
let message = history.selectedItemsList.length + msgPart + " moved to trash.";
@@ -182,7 +185,7 @@ export const deleteItems = async (item, context) => {
}
await db.trash.restore(...ids);
Navigation.queueRoutesForUpdate(routesForUpdate);
Navigation.queueRoutesForUpdate();
useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes();
ToastEvent.hide();
@@ -191,11 +194,12 @@ export const deleteItems = async (item, context) => {
});
}
history.selectedItemsList = [];
Navigation.queueRoutesForUpdate("Trash");
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().clearSelection(true);
useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes();
SearchService.updateAndSearch();
eSendEvent(eOnTopicSheetUpdate);
};
export const openLinkInBrowser = async (link) => {

View File

@@ -145,19 +145,12 @@ export function getTotalNotes(item) {
return db.notebooks.notebook(item.id)?.totalNotes || 0;
}
export async function toTXT(note, notitle) {
export async function toTXT(note, template = true) {
let text;
if (note.locked) {
text = note.content.data;
text = await db.notes.note(note.id).export("txt", note.content, template);
} else {
text = await db.notes.note(note.id).content();
}
htmlToText = htmlToText || require("html-to-text");
text = htmlToText.convert(text, {
selectors: [{ selector: "img", format: "skip" }]
});
if (!notitle) {
text = `${note.title}\n \n ${text}`;
text = await db.notes.note(note.id).export("txt", undefined, template);
}
return text;
}

View File

@@ -80,10 +80,10 @@ export const normalize = (size) => {
}
};
export const SIZE = {
xxs: normalize(10.5) * scale.fontScale,
xs: normalize(12) * scale.fontScale,
sm: normalize(14.5) * scale.fontScale,
md: normalize(16) * scale.fontScale,
xxs: normalize(11) * scale.fontScale,
xs: normalize(12.5) * scale.fontScale,
sm: normalize(15) * scale.fontScale,
md: normalize(16.5) * scale.fontScale,
lg: normalize(22) * scale.fontScale,
xl: normalize(24) * scale.fontScale,
xxl: normalize(28) * scale.fontScale,
@@ -91,10 +91,10 @@ export const SIZE = {
};
export function updateSize() {
SIZE.xxs = normalize(10.5) * scale.fontScale;
SIZE.xs = normalize(12) * scale.fontScale;
SIZE.sm = normalize(14.5) * scale.fontScale;
SIZE.md = normalize(16) * scale.fontScale;
SIZE.xxs = normalize(11) * scale.fontScale;
SIZE.xs = normalize(12.5) * scale.fontScale;
SIZE.sm = normalize(15) * scale.fontScale;
SIZE.md = normalize(16.5) * scale.fontScale;
SIZE.lg = normalize(22) * scale.fontScale;
SIZE.xl = normalize(24) * scale.fontScale;
SIZE.xxl = normalize(28) * scale.fontScale;

View File

@@ -55,12 +55,13 @@ describe("NOTE TESTS", () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-Favorite");
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 tapById("icon-favorite");
await expect(element(by.text(note.body))).not.toBeVisible();
await navigate("Notes");
});
@@ -69,11 +70,11 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-Pin");
await tapById("icon-pin");
await visibleByText("Pinned");
await visibleById("icon-pinned");
await tapById(notesnook.listitem.menu);
await tapById("icon-Pin");
await tapById("icon-pin");
expect(element(by.id("icon-pinned"))).not.toBeVisible();
});
@@ -81,11 +82,12 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-PinToNotif");
await visibleByText("Unpin from Notifications");
await tapById("icon-pin-to-notifications");
await visibleByText("Unpin from notifications");
await sleep(500);
await tapById("icon-PinToNotif");
await visibleByText("Pin to Notifications");
await tapById("icon-pin-to-notifications");
await sleep(500);
await visibleByText("Pin to notifications");
});
// it("Copy note", async () => {
@@ -100,7 +102,7 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-Export");
await tapById("icon-export");
await visibleByText("PDF");
});
@@ -122,7 +124,7 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-Delete");
await tapById("icon-delete");
await navigate("Trash");
await tapById(notesnook.listitem.menu);
await tapByText("Restore note");

View File

@@ -17,13 +17,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import {
tapById,
visibleByText,
createNote,
prepare,
elementById,
sleep
sleep,
tapByText
} from "./utils";
describe("Search", () => {
@@ -34,6 +36,10 @@ describe("Search", () => {
await sleep(300);
await elementById("search-input").typeText("n");
await sleep(1000);
await tapByText(note.body);
await sleep(1000);
await device.pressBack();
await device.pressBack();
await visibleByText(note.body);
});
});

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,6 +1,6 @@
- Top level notes in notebooks
- Image fullscreen preview & download from editor
- Change auto clear trash interval from settings
- Focus cursor on end when tapping bottom empty area in editor.
- Fixed text exports not following line spacing settings
- Fixed tags are not updated in list after renaming
- Fixed a sync issue where notes marked as local only would get deleted sometimes
- Fixed a bug where opening a note from search then closing it would cause a crash
Thank you for using Notesnook!

View File

@@ -33,6 +33,8 @@
65B5014725A672B200E2D264 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 65B5014525A672B200E2D264 /* MainInterface.storyboard */; };
65B5014B25A672B200E2D264 /* Make Note.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 65B5014025A672B200E2D264 /* Make Note.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
65B5020325A6756700E2D264 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65B5020225A6756700E2D264 /* File.swift */; };
65D145C529DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */; };
65D145C629DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */; };
65E0340B257B9FF100793428 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E0340A257B9FF100793428 /* File.swift */; };
7C72B5EA418785B334F3B5EB /* libPods-Notesnook-Make Note.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35547D7C07E83F889159A73D /* libPods-Notesnook-Make Note.a */; };
87CE5B71DC72F3D95B7B1AA5 /* libPods-Notesnook-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A03E3F5851D27269540E147C /* libPods-Notesnook-tvOSTests.a */; };
@@ -129,6 +131,7 @@
65B501AA25A6733400E2D264 /* Make Note.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Make Note.entitlements"; sourceTree = "<group>"; };
65B5020125A6756700E2D264 /* Make Note-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Make Note-Bridging-Header.h"; sourceTree = "<group>"; };
65B5020225A6756700E2D264 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = MaterialCommunityIcons.ttf; path = "../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; };
65E03409257B9FF100793428 /* Notesnook-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Notesnook-Bridging-Header.h"; sourceTree = "<group>"; };
65E0340A257B9FF100793428 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
65EC5B71272A7EE200FB3748 /* NotesWidgetExtensionDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotesWidgetExtensionDebug.entitlements; sourceTree = "<group>"; };
@@ -292,6 +295,7 @@
65EC5B71272A7EE200FB3748 /* NotesWidgetExtensionDebug.entitlements */,
6552012F27019F7700A43C51 /* OpenSans-Regular.ttf */,
6552012E27019F6E00A43C51 /* OpenSans-SemiBold.ttf */,
65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */,
13B07FAE1A68108700A75B9A /* Notesnook */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* NotesnookTests */,
@@ -533,6 +537,7 @@
buildActionMask = 2147483647;
files = (
6510626F27042891009661C3 /* OpenSans-Regular.ttf in Resources */,
65D145C529DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */,
6510E6D72877215700DACAA9 /* build.bundle in Resources */,
@@ -569,6 +574,7 @@
buildActionMask = 2147483647;
files = (
6510E6D82877215700DACAA9 /* build.bundle in Resources */,
65D145C629DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */,
6510627727042896009661C3 /* OpenSans-SemiBold.ttf in Resources */,
6510627527042893009661C3 /* OpenSans-Regular.ttf in Resources */,
65B5014725A672B200E2D264 /* MainInterface.storyboard in Resources */,
@@ -621,42 +627,10 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Notesnook/Pods-Notesnook-resources.sh",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -770,42 +744,10 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Notesnook-NotesnookTests/Pods-Notesnook-NotesnookTests-resources.sh",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -842,42 +784,10 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Notesnook-Make Note/Pods-Notesnook-Make Note-resources.sh",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1083,7 +993,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2030;
CURRENT_PROJECT_VERSION = 2035;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1157,7 +1067,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.7;
MARKETING_VERSION = 2.4.12;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1187,7 +1097,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2030;
CURRENT_PROJECT_VERSION = 2035;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1260,7 +1170,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.7;
MARKETING_VERSION = 2.4.12;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1418,7 +1328,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2030;
CURRENT_PROJECT_VERSION = 2035;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1430,7 +1340,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.7;
MARKETING_VERSION = 2.4.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1460,7 +1370,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2030;
CURRENT_PROJECT_VERSION = 2035;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1472,7 +1382,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.7;
MARKETING_VERSION = 2.4.12;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1501,7 +1411,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2030;
CURRENT_PROJECT_VERSION = 2035;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1575,7 +1485,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.7;
MARKETING_VERSION = 2.4.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1605,7 +1515,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2030;
CURRENT_PROJECT_VERSION = 2035;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1679,7 +1589,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.7;
MARKETING_VERSION = 2.4.12;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -418,8 +418,6 @@ PODS:
- pop (~> 1.0)
- React
- SexyTooltip
- RNVectorIcons (9.2.0):
- React-Core
- SexyTooltip (1.2.5):
- pop (~> 1.0)
- toolbar-android (0.2.1):
@@ -504,7 +502,6 @@ DEPENDENCIES:
- RNShare (from `../../node_modules/react-native-share`)
- RNSVG (from `../../node_modules/react-native-svg`)
- RNTooltips (from `../../node_modules/react-native-tooltips`)
- RNVectorIcons (from `../../node_modules/react-native-vector-icons`)
- SexyTooltip (from `https://github.com/ammarahm-ed/SexyTooltip.git`)
- "toolbar-android (from `../../node_modules/@react-native-community/toolbar-android`)"
- Yoga (from `../../node_modules/react-native/ReactCommon/yoga`)
@@ -667,8 +664,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-svg"
RNTooltips:
:path: "../../node_modules/react-native-tooltips"
RNVectorIcons:
:path: "../../node_modules/react-native-vector-icons"
SexyTooltip:
:git: https://github.com/ammarahm-ed/SexyTooltip.git
toolbar-android:
@@ -762,7 +757,6 @@ SPEC CHECKSUMS:
RNShare: a5dc3b9c53ddc73e155b8cd9a94c70c91913c43c
RNSVG: ecd661f380a07ba690c9c5929c475a44f432d674
RNTooltips: 5424d4bf0b3d441104127943b1115cc7f0616b1f
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
toolbar-android: 2a73856e98b750d7e71ce4644d3f41cc98211719
Yoga: 0b84a956f7393ef1f37f3bb213c516184e4a689d

View File

@@ -57,7 +57,9 @@
"@react-native-community/datetimepicker":"6.6.0",
"react-native-date-picker": "4.2.6",
"react-native-notification-sounds": "0.5.5",
"@bam.tech/react-native-image-resizer": "3.0.5"
"@bam.tech/react-native-image-resizer": "3.0.5",
"react-native-navigation-bar-color": "2.0.2",
"react-native-actions-shortcuts": "^1.0.1"
},
"devDependencies": {
"@babel/core": "^7.12.9",

View File

@@ -9,15 +9,19 @@ const config = {
}
};
if (!config.dependencies) config.dependencies = {};
config.dependencies['react-native-vector-icons'] = {
platforms: {
ios: null,
},
}
if (isGithubRelease) {
config.dependencies = {
"react-native-iap": {
platforms: {
android:null
}
},
config.dependencies["react-native-iap"] = {
platforms: {
android:null
}
}
}
module.exports = config;

View File

@@ -1,9 +1,6 @@
- Top level notes in notebooks
- Image fullscreen preview & download from editor
- Change auto clear trash interval from settings
- Fixed image upload not working since last update
- Fixed adding images to notes via copy/paste from other apps
- Focus cursor on end when tapping bottom empty area in editor.
- Fixed iPad orientation change editor resizing
- Fixed text exports not following line spacing settings
- Fixed tags are not updated in list after renaming
- Fixed a sync issue where notes marked as local only would get deleted sometimes
- Fixed a bug where opening a note from search then closing it would cause a crash
Thank you for using Notesnook!

View File

@@ -1,22 +1,20 @@
{
"name": "@notesnook/mobile",
"version": "2.4.7",
"version": "2.4.12",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "2.4.7",
"version": "2.4.12",
"license": "GPL-3.0-or-later",
"workspaces": [
"native/",
"app/"
],
"dependencies": {
"fflate": "^0.7.3",
"react-native-actions-shortcuts": "^1.0.1",
"react-native-fingerprint-scanner": "https://github.com/ammarahm-ed/react-native-fingerprint-scanner.git",
"react-native-iap": "7.5.6"
"react": "18.0.0",
"react-native": "0.69.7"
},
"devDependencies": {
"otplib": "12.0.1",
@@ -40,9 +38,7 @@
"html-to-text": "8.1.0",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react": "18.0.0",
"react-native": "0.69.7",
"react-native-actions-sheet": "^0.9.0-alpha.12",
"react-native-actions-sheet": "0.9.0-alpha.18",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-drax": "^0.10.2",
"react-native-image-zoom-viewer": "^3.0.1",
@@ -80,6 +76,7 @@
"deprecated-react-native-prop-types": "^2.3.0",
"react": "18.0.0",
"react-native": "0.69.7",
"react-native-actions-shortcuts": "^1.0.1",
"react-native-background-actions": "^2.6.6",
"react-native-begin-background-task": "https://github.com/blockfirm/react-native-begin-background-task.git",
"react-native-bootsplash": "^4.1.4",
@@ -100,6 +97,7 @@
"react-native-keychain": "4.0.5",
"react-native-mmkv-storage": "^0.8.0",
"react-native-modal-datetime-picker": "14.0.0",
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
"react-native-privacy-snapshot": "https://github.com/standardnotes/react-native-privacy-snapshot.git",
@@ -17957,9 +17955,9 @@
}
},
"node_modules/react-native-actions-sheet": {
"version": "0.9.0-alpha.12",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.12.tgz",
"integrity": "sha512-VyXsfOFkKCBWB6jn+IlKUTo0KWhItkRC8f6RMvx/RYc5WRhj4Ka5O7/BZVDs86ELJkUT08D7ufzWPjmOQ8hUKw==",
"version": "0.9.0-alpha.18",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.18.tgz",
"integrity": "sha512-X3PBVXyI7DQMfa1p3TbEFY2UjAUGqwl9ej6HxZ6ohm4KacqmGoDUZKLNz9lKCzw0vYeK8lwxtCC7qDSuEEgGQg==",
"peerDependencies": {
"react-native": "*",
"react-native-gesture-handler": "*"
@@ -17969,6 +17967,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-native-actions-shortcuts/-/react-native-actions-shortcuts-1.0.1.tgz",
"integrity": "sha512-oAVi15d4Y3YygsObRzPVcV6ZYPbPb25iHTeO4UYNwYVnW8VtpguOj6d4u6kZ7dh9c7fQvyCSf1gEyLwqydlYqg==",
"dev": true,
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -18418,6 +18417,11 @@
"react-native": ">=0.65.0"
}
},
"node_modules/react-native-navigation-bar-color": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/react-native-navigation-bar-color/-/react-native-navigation-bar-color-2.0.2.tgz",
"integrity": "sha512-ZmpLWRocyme1au11e5ZuecMS/UCi57nlzgnioi03Q6ERMbeUOqqbWgNBaNB7SsCeqBV6fZPjo3+A64zEIpzw4w=="
},
"node_modules/react-native-notification-sounds": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/react-native-notification-sounds/-/react-native-notification-sounds-0.5.5.tgz",
@@ -24310,9 +24314,7 @@
"html-to-text": "8.1.0",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react": "18.0.0",
"react-native": "0.69.7",
"react-native-actions-sheet": "^0.9.0-alpha.12",
"react-native-actions-sheet": "0.9.0-alpha.18",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-drax": "^0.10.2",
"react-native-image-zoom-viewer": "^3.0.1",
@@ -24401,6 +24403,7 @@
"react-native-keychain": "4.0.5",
"react-native-mmkv-storage": "^0.8.0",
"react-native-modal-datetime-picker": "14.0.0",
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
"react-native-privacy-snapshot": "https://github.com/standardnotes/react-native-privacy-snapshot.git",
@@ -34856,14 +34859,15 @@
}
},
"react-native-actions-sheet": {
"version": "0.9.0-alpha.12",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.12.tgz",
"integrity": "sha512-VyXsfOFkKCBWB6jn+IlKUTo0KWhItkRC8f6RMvx/RYc5WRhj4Ka5O7/BZVDs86ELJkUT08D7ufzWPjmOQ8hUKw=="
"version": "0.9.0-alpha.18",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.18.tgz",
"integrity": "sha512-X3PBVXyI7DQMfa1p3TbEFY2UjAUGqwl9ej6HxZ6ohm4KacqmGoDUZKLNz9lKCzw0vYeK8lwxtCC7qDSuEEgGQg=="
},
"react-native-actions-shortcuts": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-native-actions-shortcuts/-/react-native-actions-shortcuts-1.0.1.tgz",
"integrity": "sha512-oAVi15d4Y3YygsObRzPVcV6ZYPbPb25iHTeO4UYNwYVnW8VtpguOj6d4u6kZ7dh9c7fQvyCSf1gEyLwqydlYqg=="
"integrity": "sha512-oAVi15d4Y3YygsObRzPVcV6ZYPbPb25iHTeO4UYNwYVnW8VtpguOj6d4u6kZ7dh9c7fQvyCSf1gEyLwqydlYqg==",
"dev": true
},
"react-native-background-actions": {
"version": "2.6.7",
@@ -35172,6 +35176,11 @@
"prop-types": "^15.7.2"
}
},
"react-native-navigation-bar-color": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/react-native-navigation-bar-color/-/react-native-navigation-bar-color-2.0.2.tgz",
"integrity": "sha512-ZmpLWRocyme1au11e5ZuecMS/UCi57nlzgnioi03Q6ERMbeUOqqbWgNBaNB7SsCeqBV6fZPjo3+A64zEIpzw4w=="
},
"react-native-notification-sounds": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/react-native-notification-sounds/-/react-native-notification-sounds-0.5.5.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.4.7",
"version": "2.4.12",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -28,12 +28,10 @@
"otplib": "12.0.1"
},
"dependencies": {
"react": "18.0.0",
"react-native": "0.69.7",
"@notesnook/core": "*",
"@notesnook/editor": "*",
"@notesnook/editor-mobile": "*",
"fflate": "^0.7.3",
"react-native-actions-shortcuts": "^1.0.1",
"react-native-fingerprint-scanner": "https://github.com/ammarahm-ed/react-native-fingerprint-scanner.git",
"react-native-iap": "7.5.6"
"@notesnook/editor-mobile": "*"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,53 +17,217 @@ 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 { FlashList } from "@shopify/flash-list";
import React, { useEffect, useRef, useState } from "react";
import {
ActivityIndicator,
FlatList,
Platform,
StatusBar,
Text,
TextInput,
TouchableOpacity,
useWindowDimensions,
View
View,
useWindowDimensions
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../app/common/database";
import { getElevation } from "../app/utils";
import { useShareStore } from "./store";
import { initDatabase, useShareStore } from "./store";
export const Search = ({ close, getKeyboardHeight, quicknote }) => {
const ListItem = ({ item, mode, close }) => {
const colors = useShareStore((state) => state.colors);
const setAppendNote = useShareStore((state) => state.setAppendNote);
const [results, setResults] = useState([]);
const [searching, setSearching] = useState(false);
const searchKeyword = useRef(null);
const selectedNotebooks = useShareStore((state) => state.selectedNotebooks);
const selectedTags = useShareStore((state) => state.selectedTags);
const isSelected =
mode === "appendNote"
? false
: mode === "selectTags"
? selectedTags.indexOf(item.alias) > -1
: selectedNotebooks.findIndex(
(selected) => selected.id === item.id && selected.type === item.type
) > -1;
const SearchSetters = React.useMemo(
() => ({
appendNote: (item) => {
useShareStore.getState().setAppendNote(item);
close();
},
selectNotebooks: (item) => {
const selectedNotebooks = [
...useShareStore.getState().selectedNotebooks
];
const currentIndex = selectedNotebooks.findIndex(
(selected) => selected.id === item.id && selected.type === item.type
);
if (currentIndex === -1) {
selectedNotebooks.push(item);
} else {
selectedNotebooks.splice(currentIndex, 1);
}
useShareStore.getState().setSelectedNotebooks(selectedNotebooks);
},
selectTags: (item) => {
const selectedTags = [...useShareStore.getState().selectedTags];
const currentIndex = selectedTags.indexOf(item.alias);
if (currentIndex === -1) {
selectedTags.push(item.alias);
} else {
selectedTags.splice(currentIndex, 1);
}
useShareStore.getState().setSelectedTags(selectedTags);
}
}),
[close]
);
const set = React.useMemo(() => SearchSetters[mode], [mode, SearchSetters]);
const onSelectItem = async (item) => {
if (item.locked) {
return;
}
set(item);
};
return (
<TouchableOpacity
activeOpacity={0.7}
onPress={() => onSelectItem(item)}
style={{
flexDirection: "column",
borderBottomWidth: item.topics?.length > 0 ? 0 : 1,
borderBottomColor: colors.nav,
justifyContent: "center",
paddingVertical: 12
}}
>
<View
style={{
flexDirection: "row",
paddingHorizontal: 12,
paddingLeft: item.type === "topic" ? 0 : 12
}}
>
{item.type !== "note" ? (
<Icon
name={
!isSelected
? "checkbox-blank-circle-outline"
: "check-circle-outline"
}
style={{
marginRight: 10
}}
size={20}
color={isSelected ? colors.accent : colors.icon}
/>
) : null}
<View
style={{
flexDirection: "column"
}}
>
<Text
numberOfLines={1}
style={{
color: colors.pri,
fontFamily:
item.type === "topic"
? "OpenSans-Regular"
: "OpenSans-SemiBold",
fontSize: 15
}}
>
{item.type === "tag" ? "#" : ""}
{item.alias || item.title}
</Text>
{item.type === "note" ? (
<Text
numberOfLines={1}
style={{
color: colors.icon,
fontSize: 12,
fontFamily: "OpenSans-Regular"
}}
>
{item.headline}
</Text>
) : null}
</View>
</View>
{item.type === "notebook" && item.topics && item.topics.length > 0 ? (
<View
style={{
paddingLeft: 40,
marginTop: 5
}}
>
{item.topics.map((topic) => (
<ListItem key={topic.id} item={topic} mode={mode} close={close} />
))}
</View>
) : null}
</TouchableOpacity>
);
};
const SearchGetters = {
appendNote: () => db.notes.all,
selectNotebooks: () => db.notebooks.all,
selectTags: () => {
const selected = useShareStore.getState().selectedTags;
const tags = [];
if (selected)
tags.push(
...selected.map((t) => ({
alias: t,
type: "tag"
}))
);
for (let tag of db.tags.all) {
const index = tags.findIndex((t) => t.alias === tag.alias);
// Skip selected tags as they are already in the list.
if (index > -1) continue;
tags.push(tag);
}
return tags;
}
};
const SearchLookup = {
appendNote: (items, kwd) => db.lookup.notes(items, kwd),
selectNotebooks: (items, kwd) => db.lookup.notebooks(items, kwd),
selectTags: (items, kwd) => db.lookup.tags(items, kwd)
};
export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
const colors = useShareStore((state) => state.colors);
const { height } = useWindowDimensions();
const notes = useRef(null);
const timer = useRef(null);
const inputRef = useRef();
const [searchResults, setSearchResults] = useState([]);
const searchableItems = useRef(null);
const searchKeyword = useRef(null);
const insets =
Platform.OS === "android"
? { top: StatusBar.currentHeight }
: // eslint-disable-next-line react-hooks/rules-of-hooks
useSafeAreaInsets();
const onSelectItem = async (item) => {
if (item.locked) {
return;
}
setAppendNote(item);
close();
};
const get = SearchGetters[mode];
const lookup = SearchLookup[mode];
const onSearch = async () => {
if (!notes.current) {
await db.init();
await db.notes.init();
notes.current = db.notes.all;
if (!searchableItems.current) {
await initDatabase();
searchableItems.current = get();
}
if (timer.current) {
clearTimeout(timer.current);
@@ -71,61 +235,26 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
}
timer.current = setTimeout(async () => {
if (!searchKeyword.current) {
setResults([]);
setResults(db.notes.all);
setSearchResults([]);
setSearchResults(get());
return;
}
setSearching(true);
setResults(await db.lookup.notes(notes.current, searchKeyword.current));
setSearching(false);
}, 500);
setSearchResults(
await lookup(searchableItems.current, searchKeyword.current)
);
}, 150);
};
useEffect(() => {
(async () => {
await db.init();
await db.notes.init();
notes.current = db.notes.all;
setResults(notes.current);
await initDatabase();
searchableItems.current = get();
setSearchResults(searchableItems.current);
})();
setTimeout(() => {
inputRef.current?.focus();
}, 300);
}, []);
}, [get]);
const renderItem = ({ item }) =>
!item.locked ? (
<TouchableOpacity
activeOpacity={0.7}
onPress={() => onSelectItem(item)}
style={{
height: 50,
paddingHorizontal: 12
}}
>
<Text
numberOfLines={1}
style={{
color: colors.pri,
fontFamily: "OpenSans-SemiBold",
fontSize: 15
}}
>
{item.title}
</Text>
<Text
numberOfLines={1}
style={{
color: colors.icon,
fontSize: 12,
fontFamily: "OpenSans-Regular"
}}
>
{item.headline}
</Text>
</TouchableOpacity>
) : null;
!item.locked ? <ListItem item={item} mode={mode} close={close} /> : null;
let extra = quicknote
? {
@@ -133,6 +262,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
paddingTop: insets.top
}
: {};
const searchHeight = height - getKeyboardHeight();
return (
<View
style={{
@@ -160,7 +290,15 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
height: 50
}}
>
<Icon name="arrow-left" color={colors.pri} size={25} onPress={close} />
<Icon
name="arrow-left"
color={colors.pri}
style={{
marginRight: 10
}}
size={25}
onPress={close}
/>
<TextInput
ref={inputRef}
placeholder="Search for a note"
@@ -168,58 +306,76 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
style={{
fontSize: 15,
fontFamily: "OpenSans-Regular",
width: "85%"
flex: 1
}}
onChangeText={(value) => {
searchKeyword.current = value;
if (mode === "selectTags") {
searchKeyword.current = db.tags.sanitize(value);
} else {
searchKeyword.current = value;
}
onSearch();
}}
onSubmitEditing={onSearch}
/>
{searching ? (
<ActivityIndicator size={25} color={colors.icon} />
) : (
<Icon
name="magnify"
color={colors.pri}
size={25}
onPress={onSearch}
/>
)}
<Icon name="magnify" color={colors.pri} size={25} onPress={onSearch} />
</View>
<FlatList
data={results}
<View
style={{
maxHeight: height - getKeyboardHeight()
maxHeight: searchHeight > 550 ? 550 : searchHeight,
height: searchHeight > 550 ? 550 : searchHeight
}}
keyboardShouldPersistTaps="always"
keyboardDismissMode="none"
keyExtractor={(item) => item.id}
renderItem={renderItem}
ListFooterComponent={<View style={{ height: 200 }} />}
ListEmptyComponent={
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
height: 200
}}
>
<Text
>
<FlashList
data={searchResults}
keyboardShouldPersistTaps="always"
keyboardDismissMode="none"
renderItem={renderItem}
estimatedItemSize={50}
ListHeaderComponent={
mode === "selectTags" &&
(searchResults.length === 0 ||
(searchKeyword.current &&
searchKeyword.current.length > 0 &&
!searchResults.find(
(item) => item.title === searchKeyword.current
))) ? (
<ListItem
item={{
type: "tag",
alias: searchKeyword.current
}}
mode={mode}
close={close}
/>
) : null
}
ListFooterComponent={<View style={{ height: 200 }} />}
ListEmptyComponent={
<View
style={{
fontFamily: "OpenSans-Regular",
color: colors.icon
flex: 1,
justifyContent: "center",
alignItems: "center",
height: 200
}}
>
{searchKeyword.current
? `No results found for "${searchKeyword.current}"`
: "Search for a note to append to it."}
</Text>
</View>
}
/>
<Text
style={{
fontFamily: "OpenSans-Regular",
color: colors.icon
}}
>
{searchKeyword.current
? `No results found for "${searchKeyword.current}"`
: "Search for a note to append to it."}
</Text>
</View>
}
/>
</View>
</View>
);
};

View File

@@ -30,7 +30,8 @@ import {
Text,
TouchableOpacity,
useWindowDimensions,
View
View,
ScrollView
} from "react-native";
import {
SafeAreaProvider,
@@ -46,7 +47,7 @@ import { getElevation } from "../app/utils";
import { eOnLoadNote } from "../app/utils/events";
import { sleep } from "../app/utils/time";
import { Search } from "./search";
import { useShareStore } from "./store";
import { initDatabase, useShareStore } from "./store";
import { Editor } from "./editor";
const getLinkPreview = (url) => {
return getPreviewData(url, 5000);
@@ -194,7 +195,6 @@ const ShareView = ({ quicknote = false }) => {
const appendNote = useShareStore((state) => state.appendNote);
const [note, setNote] = useState({ ...defaultNote });
const noteContent = useRef();
const [loadingIntent, setLoadingIntent] = useState(true);
const [loading, setLoading] = useState(false);
const [loadingExtension, setLoadingExtension] = useState(true);
const [rawData, setRawData] = useState({
@@ -209,9 +209,9 @@ const ShareView = ({ quicknote = false }) => {
? { top: StatusBar.currentHeight }
: // eslint-disable-next-line react-hooks/rules-of-hooks
useSafeAreaInsets();
const [showSearch, setShowSearch] = useState(false);
const [searchMode, setSearchMode] = useState(null);
const [kh, setKh] = useState(0);
const editorRef = useRef();
const onKeyboardDidShow = (event) => {
let kHeight = event.endCoordinates.height;
@@ -261,7 +261,6 @@ const ShareView = ({ quicknote = false }) => {
setRawData({
value: ""
});
setLoadingIntent(false);
return;
}
let note = { ...defaultNote };
@@ -281,7 +280,6 @@ const ShareView = ({ quicknote = false }) => {
} catch (e) {
console.error(e);
}
setLoadingIntent(false);
}, [onLoad]);
const onLoad = useCallback(() => {
@@ -300,13 +298,12 @@ const ShareView = ({ quicknote = false }) => {
//await loadDatabase();
setLoadingExtension(false);
loadData();
useShareStore.getState().restoreAppendNote();
useShareStore.getState().restore();
})();
}, [loadData]);
const close = async () => {
setNote({ ...defaultNote });
setLoadingIntent(true);
setLoadingExtension(true);
if (quicknote) {
ShareExtension.openURL("ShareMedia://MainApp");
@@ -317,8 +314,7 @@ const ShareView = ({ quicknote = false }) => {
const onPress = async () => {
setLoading(true);
await db.init();
await db.notes.init();
await initDatabase();
await sleep(1500);
if (!noteContent.current) return;
if (appendNote && !db.notes.note(appendNote.id)) {
@@ -332,7 +328,7 @@ const ShareView = ({ quicknote = false }) => {
let raw = await db.content.raw(appendNote.contentId);
_note = {
content: {
data: raw.data + "\n" + noteContent.current,
data: raw.data + noteContent.current,
type: "tiptap"
},
id: appendNote.id,
@@ -340,10 +336,28 @@ const ShareView = ({ quicknote = false }) => {
};
} else {
_note = { ...note };
_note.tags = useShareStore.getState().selectedTags || [];
_note.content.data = noteContent.current;
_note.sessionId = Date.now();
}
await db.notes.add(_note);
let id = await db.notes.add(_note);
if (!appendNote) {
for (const item of useShareStore.getState().selectedNotebooks) {
if (item.type === "notebook") {
db.relations.add(item, { id, type: "note" });
} else {
db.notes.addToNotebook(
{
id: item.notebookId,
topic: item.id
},
id
);
}
}
}
await Storage.write("notesAddedFromIntent", "added");
close();
setLoading(false);
@@ -389,6 +403,8 @@ const ShareView = ({ quicknote = false }) => {
loadData();
}, [loadData]);
const WrapperView = Platform.OS === "android" ? View : ScrollView;
return loadingExtension ? null : (
<SafeAreaView
style={{
@@ -398,7 +414,7 @@ const ShareView = ({ quicknote = false }) => {
justifyContent: quicknote ? "flex-start" : "flex-end"
}}
>
{quicknote && !showSearch ? (
{quicknote && !searchMode ? (
<View
style={{
width: "100%",
@@ -417,8 +433,8 @@ const ShareView = ({ quicknote = false }) => {
icon="close"
iconColor={colors.pri}
onPress={() => {
if (showSearch) {
setShowSearch(false);
if (searchMode) {
setSearchMode(null);
} else {
close();
}
@@ -458,8 +474,8 @@ const ShareView = ({ quicknote = false }) => {
<TouchableOpacity
activeOpacity={1}
onPress={() => {
if (showSearch) {
setShowSearch(false);
if (searchMode) {
setSearchMode(null);
} else {
close();
}
@@ -482,28 +498,31 @@ const ShareView = ({ quicknote = false }) => {
</TouchableOpacity>
)}
{showSearch ? (
{searchMode ? (
<Search
quicknote={quicknote}
getKeyboardHeight={() => keyboardHeight.current}
mode={searchMode}
close={() => {
setShowSearch(false);
setSearchMode(null);
}}
/>
) : null}
<View
<WrapperView
style={{
paddingVertical: 25,
backgroundColor: "transparent",
marginBottom: insets.top,
display: showSearch ? "none" : "flex"
paddingVertical: 12,
backgroundColor: colors.bg,
display: searchMode ? "none" : "flex",
borderTopRightRadius: Platform.OS === "ios" ? 10 : 15,
borderTopLeftRadius: Platform.OS === "ios" ? 10 : 15,
maxHeight: Platform.OS === "android" ? undefined : "100%"
}}
>
<View
style={{
maxHeight: "100%",
paddingHorizontal: 12
maxHeight: "100%"
//paddingHorizontal: 12
}}
>
<View
@@ -513,20 +532,56 @@ const ShareView = ({ quicknote = false }) => {
>
<View
style={{
marginTop: 10,
minHeight: 100,
borderRadius: 10,
...getElevation(quicknote ? 1 : 5),
backgroundColor: colors.bg,
overflow: "hidden"
}}
>
<View
style={{
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
borderBottomWidth: 1,
paddingBottom: 12,
borderBottomColor: colors.nav,
paddingHorizontal: 12
}}
>
<Text
style={{
fontSize: 18,
fontFamily: "OpenSans-SemiBold"
}}
>
Save to Notesnook
</Text>
<Button
title="Done"
style={{
backgroundColor: colors.accent,
height: Platform.OS === "ios" ? 35 : 40,
paddingHorizontal: 15,
marginBottom: 0
}}
loading={loading}
iconColor={colors.light}
onPress={onPress}
textStyle={{
fontSize: 16,
marginLeft: 0
}}
/>
</View>
<View
style={{
width: "100%",
height: height * 0.3,
height: 200,
paddingBottom: 15,
borderRadius: 10
marginBottom: 10,
borderBottomColor: colors.nav,
borderBottomWidth: 1
}}
>
<SafeAreaProvider
@@ -574,59 +629,47 @@ const ShareView = ({ quicknote = false }) => {
style={{
flexDirection: "row",
paddingHorizontal: 12,
alignItems: "flex-end",
justifyContent: "space-between",
width: "100%"
width: "100%",
alignItems: "center"
}}
>
<View
<Text
style={{
flexDirection: "row"
color: colors.pri,
marginRight: 10
}}
>
{rawData.value && isURL(rawData.value) ? (
<Button
color={mode == 2 ? colors.shade : colors.nav}
icon={modes[2].icon}
onPress={() => changeMode(2)}
title={modes[2].title}
iconSize={18}
iconColor={mode == 2 ? accent.color : colors.icon}
textColor={mode == 2 ? accent.color : colors.icon}
type="rounded"
style={{ paddingHorizontal: 12 }}
/>
) : null}
Clip Mode:
</Text>
{rawData.value && isURL(rawData.value) ? (
<Button
color={mode == 1 ? colors.shade : colors.nav}
icon={modes[1].icon}
onPress={() => changeMode(1)}
title={modes[1].title}
iconSize={18}
iconColor={mode == 1 ? accent.color : colors.icon}
textColor={mode == 1 ? accent.color : colors.icon}
type="rounded"
style={{ paddingHorizontal: 12 }}
/>
</View>
{!quicknote ? (
<Button
color={accent.color}
onPress={onPress}
loading={loading || loadingIntent}
icon="check"
iconSize={20}
iconColor={colors.light}
color="transparent"
icon={mode === 2 ? "radiobox-marked" : "radiobox-blank"}
onPress={() => changeMode(2)}
title={modes[2].title}
iconSize={16}
fontSize={14}
iconColor={mode == 2 ? accent.color : colors.icon}
textColor={mode == 2 ? accent.color : colors.icon}
style={{
paddingHorizontal: 0,
height: 40,
width: 40,
borderRadius: 100,
minWidth: 0
height: 30,
marginRight: 12,
marginBottom: 0
}}
/>
) : null}
<Button
color="transparent"
icon={mode === 1 ? "radiobox-marked" : "radiobox-blank"}
onPress={() => changeMode(1)}
title={modes[1].title}
iconSize={16}
fontSize={14}
iconColor={mode == 1 ? accent.color : colors.icon}
textColor={mode == 1 ? accent.color : colors.icon}
style={{ paddingHorizontal: 0, height: 30, marginBottom: 0 }}
/>
</View>
</View>
</View>
@@ -638,7 +681,8 @@ const ShareView = ({ quicknote = false }) => {
flexDirection: "column",
marginTop: 10,
alignSelf: "center",
alignItems: "center"
alignItems: "center",
paddingHorizontal: 12
}}
>
<Button
@@ -656,38 +700,56 @@ const ShareView = ({ quicknote = false }) => {
fontSize: 15
}}
style={{
paddingHorizontal: 0,
...getElevation(1),
paddingHorizontal: 12,
height: 45,
width: "100%",
marginRight: 0
marginRight: 0,
borderWidth: 1,
borderColor: colors.nav,
justifyContent: "flex-start"
}}
/>
<Button
color={colors.bg}
onPress={() => {
setShowSearch(true);
setSearchMode("appendNote");
}}
icon="text-short"
iconSize={18}
iconColor={appendNote ? accent.color : colors.icon}
title={`${
appendNote ? appendNote.title.slice(0, 25) : "Append to note"
}`}
textColor={appendNote ? accent.color : colors.icon}
title={`Append to a note`}
textColor={colors.icon}
type="button"
textStyle={{
fontSize: 15
}}
style={{
paddingHorizontal: 0,
...getElevation(1),
paddingHorizontal: 12,
height: 45,
width: "100%",
marginRight: 0
marginRight: 0,
borderWidth: 1,
borderColor: colors.nav,
justifyContent: "flex-start"
}}
/>
{!appendNote ? (
<AddTags
onPress={() => {
setSearchMode("selectTags");
}}
/>
) : null}
{!appendNote ? (
<AddNotebooks
onPress={() => {
setSearchMode("selectNotebooks");
}}
/>
) : null}
</View>
<View
@@ -696,11 +758,222 @@ const ShareView = ({ quicknote = false }) => {
}}
/>
</View>
</View>
</WrapperView>
</SafeAreaView>
);
};
const AddNotebooks = ({ onPress }) => {
const colors = useShareStore((state) => state.colors);
const notebooks = useShareStore((state) => state.selectedNotebooks);
return (
<TouchableOpacity
onPress={onPress}
activeOpacity={0.7}
style={{
paddingHorizontal: 12,
width: "100%",
marginRight: 0,
borderWidth: 1,
borderColor: colors.nav,
justifyContent: "center",
borderRadius: 5,
marginBottom: 10,
paddingVertical: 10,
paddingTop: 5
}}
>
{!notebooks || !notebooks.length ? (
<>
<View
style={{
width: "100%",
flexDirection: "row",
marginTop: 5
}}
>
<Icon
name="notebook-outline"
size={20}
style={{
marginRight: 10
}}
color={colors.icon}
/>
<Text
style={{
color: colors.icon,
fontSize: 15
}}
>
Add to notebook
</Text>
</View>
</>
) : (
<View
style={{
flexWrap: "wrap",
width: "100%",
flexDirection: "row"
}}
>
{notebooks.map((item) => (
<Text
style={{
color: colors.icon,
marginRight: 5,
fontSize: 14,
borderRadius: 4,
paddingHorizontal: 8,
backgroundColor: colors.nav,
paddingVertical: 5,
marginTop: 5
}}
onPress={() => {
const index = notebooks.findIndex(
(nb) => nb.id === item.id && nb.type === item.type
);
const selectedNotebooks = [...notebooks];
selectedNotebooks.splice(index, 1);
useShareStore
.getState()
.setSelectedNotebooks(selectedNotebooks);
}}
key={item.id}
>
<Icon
name={item.type === "topic" ? "bookmark" : "notebook-outline"}
size={15}
/>{" "}
{item.title}
</Text>
))}
<Text
style={{
color: colors.accent,
marginRight: 5,
fontSize: 14,
borderRadius: 4,
paddingHorizontal: 8,
backgroundColor: colors.nav,
paddingVertical: 5,
marginTop: 5
}}
onPress={() => {
onPress();
}}
key="$add-more"
>
<Icon name="plus" size={16} /> Add more
</Text>
</View>
)}
</TouchableOpacity>
);
};
const AddTags = ({ onPress }) => {
const colors = useShareStore((state) => state.colors);
const tags = useShareStore((state) => state.selectedTags);
return (
<TouchableOpacity
onPress={onPress}
activeOpacity={0.7}
style={{
paddingHorizontal: 12,
height: 45,
width: "100%",
marginRight: 0,
borderWidth: 1,
borderColor: colors.nav,
justifyContent: "center",
borderRadius: 5,
marginBottom: 10
}}
>
{!tags || !tags.length ? (
<>
<View
style={{
width: "100%",
flexDirection: "row"
}}
>
<Icon
name="pound"
size={20}
style={{
marginRight: 10
}}
color={colors.icon}
/>
<Text
style={{
color: colors.icon,
fontSize: 15
}}
>
Add tags
</Text>
</View>
</>
) : (
<View
style={{
flexWrap: "wrap",
width: "100%",
flexDirection: "row"
}}
>
{tags.map((tag) => (
<Text
style={{
color: colors.icon,
marginRight: 5,
fontSize: 14,
borderRadius: 4,
paddingHorizontal: 8,
backgroundColor: colors.nav,
paddingVertical: 5
}}
onPress={() => {
const index = tags.indexOf(tag);
const selectedTags = [...tags];
selectedTags.splice(index, 1);
useShareStore.getState().setSelectedTags(selectedTags);
}}
key={tag}
>
#{tag}
</Text>
))}
<Text
style={{
color: colors.accent,
marginRight: 5,
fontSize: 14,
borderRadius: 4,
paddingHorizontal: 8,
backgroundColor: colors.nav,
paddingVertical: 5
}}
onPress={() => {
onPress();
}}
key="$add-tag"
>
<Icon name="plus" size={17} />
Add tag
</Text>
</View>
)}
</TouchableOpacity>
);
};
const Button = ({
title,
onPress,
@@ -734,7 +1007,6 @@ const Button = ({
alignItems: "center",
flexDirection: "row",
marginBottom: 10,
minWidth: 80,
paddingHorizontal: 20
},
textStyle: {}
@@ -767,11 +1039,9 @@ const Button = ({
alignItems: "center",
flexDirection: "row",
marginBottom: 10,
minWidth: 80,
paddingHorizontal: 20
},
types[type].style,
style
{ ...types[type].style, ...style }
]}
>
{loading ? <ActivityIndicator color={iconColor} /> : null}

View File

@@ -25,6 +25,22 @@ import {
COLOR_SCHEME_LIGHT
} from "../app/utils/color-scheme";
import { MMKV } from "../app/common/database/mmkv";
import { db } from "../app/common/database";
export async function initDatabase() {
if (!db.isInitialized) {
// Only load collections in database.
await db.initCollections();
await db.notes.init();
}
}
const StorageKeys = {
appendNote: "shareMenuAppendNote",
selectedNotebooks: "shareMenuSelectedNotebooks",
selectedTag: "shareMenuSelectedTag",
appSettings: "appSettings"
};
export const useShareStore = create((set) => ({
colors:
@@ -33,7 +49,7 @@ export const useShareStore = create((set) => ({
: COLOR_SCHEME_LIGHT,
accent: ACCENT,
setAccent: async () => {
let appSettings = MMKV.getString("appSettings");
let appSettings = MMKV.getString(StorageKeys.appSettings);
if (appSettings) {
appSettings = JSON.parse(appSettings);
@@ -56,14 +72,31 @@ export const useShareStore = create((set) => ({
},
appendNote: null,
setAppendNote: (note) => {
MMKV.setItem("shareMenuAppendNote", JSON.stringify(note));
MMKV.setItem(StorageKeys.appendNote, JSON.stringify(note));
set({ appendNote: note });
},
restoreAppendNote: async () => {
let note = MMKV.getString("shareMenuAppendNote");
if (note) {
note = JSON.parse(note);
set({ appendNote: note });
}
restore: () => {
let appendNote = MMKV.getString(StorageKeys.appendNote);
let selectedNotebooks = MMKV.getString(StorageKeys.selectedNotebooks);
let selectedTags = MMKV.getString(StorageKeys.selectedTag);
appendNote = JSON.parse(appendNote);
set({
appendNote: appendNote ? JSON.parse(appendNote) : null,
selectedNotebooks: selectedNotebooks ? JSON.parse(selectedNotebooks) : [],
selectedTag: selectedTags ? JSON.parse(selectedTags) : []
});
},
selectedTags: [],
selectedNotebooks: [],
setSelectedNotebooks: (selectedNotebooks) => {
MMKV.setItem(
StorageKeys.selectedNotebooks,
JSON.stringify(selectedNotebooks)
);
set({ selectedNotebooks });
},
setSelectedTags: (selectedTags) => {
MMKV.setItem(StorageKeys.selectedTag, JSON.stringify(selectedTags));
set({ selectedTags });
}
}));

View File

@@ -12,7 +12,7 @@
### The desktop app?
You can find all the desktop related code in [the `desktop/` directory](./desktop/). Since it uses the web app directly, we are keeping both together. (We should probably move it to it's own project at some point.)
You can find all the desktop-related code in [the `desktop/` directory](./desktop/). Since it uses the web app directly, we are keeping both together. (We should probably move it to its own project at some point.)
### Downloads & releases
@@ -21,7 +21,7 @@ You can find all the desktop related code in [the `desktop/` directory](./deskto
## Build instructions
**Before you start it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
> **Before you start, it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
### Setting up the development environment
@@ -66,11 +66,11 @@ npx serve apps/web/build
## Developer guide
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript so if you can help with that, it'd be great!
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript, so if you can help with that, it'd be great!
### The tech stack
We try to keep the stack as lean as possible
We try to keep the stack as lean as possible:
1. React v17: UI framework
2. Typescript/Javascript: The logical side of the app
@@ -82,21 +82,21 @@ We try to keep the stack as lean as possible
### Project structure
1. `src/`: 99% of the source code lives here & this is also where you'll be spending most of your time.
1. `index.tsx`: **the app entrypoint** responsible for loading the appropriate view based on the current route.
2. `app.js`: **the default route** that contains the whole note taking experience (notes list, navigation, editor etc.)
3. `views/`: Contains **all the views** including views for login, settings, notes, notebooks & topics.
4. `components/`: All the **reusuable UI components** are here (e.g. button, editor, etc.)
5. `stores/`: Contains the glue code & **logic for all the UI interaction**. For example, when you pin a note the `src/stores/note-store.js` is responsible for everything including refreshing the list to reflect the changes.
6. `navigation/`: All the **routing & navigation** logic lives here. The app uses 2 kinds of routers:
1. `routes.js`: This contains all the main routes like `/notes`, `/notebooks` with information on what to render when user goes to a particular route.
2. `hash-routes.js`: The hash routes are used for temporary navigation like opening dialogs, opening a note. These look like `#/notes/6307bbd65d5d5d5cb86f6f74/edit`.
7. `interfaces/`: This is where the **platform specific storage & encryption logic** lives. These interface implementations are used by the `@notesnook/core` to provide capabilities such as persistence & encryption.
8. `hooks/`: Contains all the **general purpose React hooks**
9. `utils/`: These are **general-purpose utilities** for performing various tasks such as downloading files, storing configuration etc.
1. `src/`: 99% of the source code lives here & this is also where you'll spend most of your time.
1. `index.tsx`: **the app entry point** responsible for loading the appropriate view based on the current route.
2. `app.js`: **the default route** that contains the whole note-taking experience (notes list, navigation, editor, etc.)
3. `views/`: Contains **all the views**, including views for login, settings, notes, notebooks & topics.
4. `components/`: All the **reusable UI components** are here (e.g., button, editor, etc.)
5. `stores/`: Contains the glue code & **logic for all the UI interactions**. For example, when you pin a note, the `src/stores/note-store.js` is responsible for everything, including refreshing the list to reflect the changes.
6. `navigation/`: All the **routing & navigation** logic lives here. The app uses two kinds of routers:
1. `routes.js`: This contains all the main routes like `/notes`, `/notebooks` with information on what to render when the user goes to a particular route.
2. `hash-routes.js`: The hash routes are used for temporary navigation, like opening dialogs or opening a note. These look like `#/notes/6307bbd65d5d5d5cb86f6f74/edit`.
7. `interfaces/`: This is where the **platform-specific storage & encryption logic** lives. These interface implementations are used by the `@notesnook/core` to provide capabilities such as persistence & encryption.
8. `hooks/`: Contains all the **general-purpose React hooks**
9. `utils/`: These are **general-purpose utilities** for performing various tasks such as downloading files, storing configuration, etc.
10. `common/`: This directory contains **the shared logic between the whole app**. For example, this is where the database is instantiated for use throughout the app.
11. `commands/`: These are **commands used by the desktop app** for things like checking for updates, storing backups etc.
2. `desktop/`: The Electron layer for **the desktop app lives here**. (This should be moved outside into it's own project).
11. `commands/`: These are **commands the desktop app uses** for things like checking for updates, storing backups etc.
2. `desktop/`: The Electron layer for **the desktop app lives here**. (This should be moved outside into its own project).
### Running the tests

View File

@@ -33,6 +33,22 @@
h6 {
color: #212121;
}
p {
margin-bottom: 0px;
}
p[data-spacing="double"] {
margin-top: 1em;
}
p[data-spacing="single"] {
margin-top: 0px;
}
p[data-spacing="single"]:empty {
margin-top: 1em;
}
pre.codeblock {
overflow-x: auto;
@@ -147,9 +163,9 @@
font-family: PT Mono, Consolas, Monaco, "Andale Mono", "Ubuntu Mono",
monospace;
text-align: left;
white-space: pre;
white-space: pre-wrap;
word-spacing: normal;
word-break: normal;
word-break: break-word;
word-wrap: normal;
line-height: 1.5;

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.4.5",
"version": "2.4.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.4.5",
"version": "2.4.7",
"dependencies": {
"diary": "^0.3.1",
"electron-updater": "^5.3.0",

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.4.5",
"version": "2.4.7",
"appAppleId": "1544027013",
"private": true,
"main": "./build/electron.js",

View File

@@ -22,12 +22,14 @@ import { AssetManager } from "./asset-manager";
import { EVENTS } from "./events";
import bringToFront from "./ipc/actions/bringToFront";
import { sendMessageToRenderer } from "./ipc/utils";
import { isFlatpak } from "./utils";
let tray: Tray | undefined = undefined;
export function setupTray() {
if (tray) tray.destroy();
const trayIconSize = process.platform === "win32" || process.platform === "darwin" ? 16 : 32;
const trayIconSize =
process.platform === "win32" || process.platform === "darwin" ? 16 : 32;
tray = new Tray(
AssetManager.icon("tray-icon", {
@@ -39,14 +41,18 @@ export function setupTray() {
{
label: "Show app",
type: "normal",
icon: AssetManager.icon("tray-icon", { size: trayIconSize }),
icon: isFlatpak()
? undefined
: AssetManager.icon("tray-icon", { size: trayIconSize }),
click: bringToFront
},
{ type: "separator" },
{
label: "New note",
type: "normal",
icon: AssetManager.icon("note-add", { size: trayIconSize }),
icon: isFlatpak()
? undefined
: AssetManager.icon("note-add", { size: trayIconSize }),
click: () => {
bringToFront();
sendMessageToRenderer(EVENTS.createItem, { itemType: "note" });
@@ -55,7 +61,9 @@ export function setupTray() {
{
label: "New notebook",
type: "normal",
icon: AssetManager.icon("notebook-add", { size: trayIconSize }),
icon: isFlatpak()
? undefined
: AssetManager.icon("notebook-add", { size: trayIconSize }),
click: () => {
bringToFront();
sendMessageToRenderer(EVENTS.createItem, { itemType: "notebook" });
@@ -64,7 +72,9 @@ export function setupTray() {
{ type: "separator" },
{
label: "Quit",
icon: AssetManager.icon("quit", { size: trayIconSize }),
icon: isFlatpak()
? undefined
: AssetManager.icon("quit", { size: trayIconSize }),
type: "normal",
click: () => {
app.exit(0);

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { app } from "electron";
import { join } from "path";
import { statSync } from "fs";
import { statSync, existsSync } from "fs";
const APP_ICON_PATH = join(
__dirname,
@@ -52,4 +52,8 @@ function getPath(filePath) {
}
}
export { getPath, isDevelopment, APP_ICON_PATH };
function isFlatpak() {
return existsSync("/.flatpak-info");
}
export { getPath, isDevelopment, isFlatpak, APP_ICON_PATH };

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "2.4.4",
"version": "2.4.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "2.4.4",
"version": "2.4.7",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -93,8 +93,6 @@ export default function AppEffects({ setShow }) {
initEditorStore();
(async function () {
await resetReminders();
await updateLastSynced();
if (await initUser()) {
showUpgradeReminderDialogs();

View File

@@ -94,7 +94,9 @@ export function showAddNotebookDialog() {
isOpen={true}
onDone={async (nb: Record<string, unknown>) => {
// add the notebook to db
await db.notebooks?.add({ ...nb });
const notebook = await db.notebooks?.add({ ...nb });
if (!notebook) return perform(false);
notebookStore.refresh();
showToast("success", "Notebook added successfully!");
@@ -209,9 +211,8 @@ export function showError(title: string, message: string) {
export function showMultiDeleteConfirmation(length: number) {
return confirm({
title: `Delete ${length} items?`,
message: `These items will be **kept in your Trash for ${
db.settings?.getTrashCleanupInterval() || 7
} days** after which they will be permanently deleted.`,
message: `These items will be **kept in your Trash for ${db.settings?.getTrashCleanupInterval() || 7
} days** after which they will be permanently deleted.`,
positiveButtonText: "Yes",
negativeButtonText: "No"
});
@@ -471,9 +472,9 @@ export function showCreateTopicDialog() {
}}
onAction={async (topic: Record<string, unknown>) => {
if (!topic) return;
const notebookId = notebookStore.get().selectedNotebookId;
await db.notebooks?.notebook(notebookId).topics.add(topic);
notebookStore.setSelectedNotebook(notebookId);
const notebook = notebookStore.get().selectedNotebook;
await db.notebooks?.notebook(notebook.id).topics.add(topic);
notebookStore.setSelectedNotebook(notebook.id);
showToast("success", "Topic created!");
perform(true);
}}

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