Compare commits

...

112 Commits

Author SHA1 Message Date
Abdullah Atta
9ae93dec4c Merge pull request #8432 from 01zulfi/docs/standard-notes-import
docs: add standard notes import instructions
2025-10-02 10:35:02 +05:00
Abdullah Atta
93ecc9b1ac Merge pull request #8587 from 01zulfi/web/fix-status-item
web: fix update & vault status items vertical alignment
2025-10-02 10:33:01 +05:00
Abdullah Atta
b49be89b06 Merge pull request #8570 from 01zulfi/editor/support-math-paste
editor: support pasting math
2025-10-02 09:30:51 +05:00
Abdullah Atta
23e0748137 Merge pull request #8581 from streetwriters/fix/error-not-shown-gift-card
Fix error not shown on redeem code failure
2025-10-02 09:30:14 +05:00
Abdullah Atta
2f6d222cca Merge pull request #8603 from 01zulfi/core/fix-leading-spaces-export
core: fix leading spaces in txt export && remove unneeded space at start of txt
2025-10-02 09:29:23 +05:00
Abdullah Atta
ba1cfe05c4 Merge pull request #8621 from 01zulfi/core/fix-tag-title-sorting
core: fix tag sort by title
2025-10-02 09:29:07 +05:00
Abdullah Atta
11702c9209 Merge pull request #8615 from streetwriters/fix/web-app-not-working-on-mobile
Fix web app not working on mobile devices
2025-10-02 08:38:05 +05:00
01zulfi
239e301042 core: fix tag sort by title
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-29 11:23:03 +05:00
Abdullah Atta
c097c4d457 web: fix web app not working on mobile devices
i thought service workers would work on Android but unfortunately, they are
super flaky causing lots of issues. I might revisit it later.
2025-09-26 11:03:41 +05:00
Ammar Ahmed
28f1f276ce Merge pull request #8611 from streetwriters/fix-server-url-error
mobile: allow underscores in url validation
2025-09-25 12:24:18 +05:00
Ammar Ahmed
9470d2b61f mobile: allow underscores in url validation 2025-09-25 12:19:16 +05:00
01zulfi
95795a20c2 core: fix leading spaces in txt export && remove unneeded space at start of txt
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-24 09:35:26 +05:00
Ammar Ahmed
5e05d96487 Merge pull request #8588 from streetwriters/mobile-release-3.2.12
Mobile release 3.2.12
2025-09-19 10:41:45 +05:00
Ammar Ahmed
18c9912068 mobile: update changelog 2025-09-19 09:55:36 +05:00
01zulfi
cd85753252 web: fix update & vault status items vertical alignment
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-18 14:19:56 +05:00
Ammar Ahmed
bdb135036c mobile: release v3.2.12 2025-09-18 12:43:30 +05:00
Ammar Ahmed
5d70f58a0d mobile: fix editor styles 2025-09-18 12:42:40 +05:00
Ammar Ahmed
b7d2d66e82 mobile: fix add tag button not show when no tags 2025-09-18 12:42:28 +05:00
Ammar Ahmed
6b028ef850 mobile: fix localization 2025-09-18 12:42:18 +05:00
Ammar Ahmed
8f85795e53 mobile: fix notebook tree in notesnook sheet 2025-09-18 12:42:07 +05:00
Ammar Ahmed
a0b7dff5fd Merge pull request #8585 from streetwriters/mobile-release-3.2.11
Mobile release 3.2.11
2025-09-18 12:39:21 +05:00
Ammar Ahmed
352018c042 Merge branch 'master' into mobile-release-3.2.11
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2025-09-18 12:39:10 +05:00
Abdullah Atta
99e642780e web: fix error not shown on redeem code failure 2025-09-17 10:59:54 +05:00
Abdullah Atta
fd48684396 Merge pull request #8568 from 01zulfi/web/attachment-navigate
web: fix settings not opening again after navigation from attachment manager
2025-09-16 13:03:25 +05:00
01zulfi
9b25ab8708 web: fix settings not opening again after navigation from attachment manager
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-16 12:37:36 +05:00
Abdullah Atta
c725ba8530 Merge pull request #8574 from streetwriters/ci/fix-desktop-tests-not-running
Fix desktop tests not triggering on PR/commit
2025-09-16 11:15:41 +05:00
Abdullah Atta
98287fbfc4 ci: fix desktop tests not triggering on PR/commit 2025-09-16 11:07:52 +05:00
Ammar Ahmed
9f24bd2250 Merge pull request #8573 from streetwriters/intl-fix-strings
intl: fix strings.ts
2025-09-16 10:12:13 +05:00
Ammar Ahmed
94f2a216ad intl: fix strings.ts 2025-09-16 10:11:43 +05:00
Ammar Ahmed
78e3ee5fd2 Merge pull request #8572 from streetwriters/fix-ipad-split-view
Fix keyboard in iPad split view
2025-09-16 10:10:05 +05:00
Ammar Ahmed
1ddcf44eb0 Merge pull request #8551 from streetwriters/mobile-editor-ui-update
mobile: editor ui overhaul
2025-09-16 10:08:28 +05:00
Ammar Ahmed
5c5fb2ba2c Merge branch 'master' into mobile-editor-ui-update
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2025-09-16 10:08:10 +05:00
Ammar Ahmed
aa4995d871 Merge pull request #8525 from streetwriters/mobile-reminder-fixes
Fix reminders on mobile
2025-09-16 10:06:54 +05:00
Ammar Ahmed
f818c41a02 Merge pull request #8524 from streetwriters/mobile-fix-add-notebook-btn
Fix add notebook button in Notebooks sheet
2025-09-16 10:06:30 +05:00
Ammar Ahmed
3f9d954e2f Merge pull request #8508 from streetwriters/mobile-fix-notebook-tree
Fix Notebook Tree when Searching for Notebooks
2025-09-16 10:06:05 +05:00
Ammar Ahmed
f1fcd227e7 Merge pull request #8535 from streetwriters/mobile-fix-reset-navigation
mobile: fix resetting navigation
2025-09-16 10:04:54 +05:00
Ammar Ahmed
44d910121c Merge pull request #8507 from streetwriters/mobile-fix-string-email
Fix email confirmed description
2025-09-16 10:03:40 +05:00
Abdullah Atta
1258a95aad Merge pull request #8562 from streetwriters/fix/refresh-note-on-attachment-delete
Refresh note on attachment permanent delete
2025-09-16 09:43:15 +05:00
Ammar Ahmed
1379193065 mobile: fix keyboard in ipad split view 2025-09-16 09:11:34 +05:00
01zulfi
02960fb5a9 editor: support pasting math
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-15 15:41:16 +05:00
Abdullah Atta
a7fa42e944 web: refresh note on attachment permanent delete 2025-09-13 13:07:02 +05:00
Ammar Ahmed
02ab8e3938 mobile: editor ui improvements 2025-09-12 10:57:18 +05:00
Abdullah Atta
5c120676e1 Merge pull request #8550 from streetwriters/release/web/v3.2.4
Release v3.2.4
2025-09-12 08:47:48 +05:00
Abdullah Atta
3821dc29ef web: fix some editor tests on macos 2025-09-12 08:28:39 +05:00
Abdullah Atta
3c51f73427 web: fix cannot destructure property checkout of He as it is undefined 2025-09-12 08:28:39 +05:00
Abdullah Atta
8db373ee34 web: bump version to 3.2.4 2025-09-12 08:28:39 +05:00
Abdullah Atta
adcb73a2fc Merge pull request #8388 from streetwriters/fix/theme-issues-2
Fix theme related issues
2025-09-12 08:27:44 +05:00
Ammar Ahmed
4c97b3481d mobile: editor ui overhaul 2025-09-11 15:49:00 +05:00
Abdullah Atta
4785544e18 Merge pull request #8549 from 01zulfi/web/fix-toc-crash
web: fix crash when shift+click on toc item
2025-09-11 13:48:33 +05:00
01zulfi
7812c15b48 web: fix crash when shift+click on toc item
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-11 09:39:00 +05:00
Abdullah Atta
b841c6e0eb Merge pull request #8395 from 01zulfi/editor/image-resizing-dimensions
editor: show image dimensions during drag resizing
2025-09-09 14:51:44 +05:00
Abdullah Atta
55b6be0f15 Merge pull request #8542 from 01zulfi/editor/improve-insert-link-dialog
editor: if selected text is link, autofill url field in insert link popup
2025-09-09 13:07:18 +05:00
Abdullah Atta
dfe6a73ac1 Merge pull request #8546 from streetwriters/release/monograph/1.2.5
Release monographs v1.2.5
2025-09-09 12:53:20 +05:00
Abdullah Atta
afcb8835d2 ci: do not build for armv7 & armv8 platforms 2025-09-09 12:49:33 +05:00
Abdullah Atta
9dc9f186e1 monograph: use oven/bun:1.2.21-slim 2025-09-09 12:45:15 +05:00
Ammar Ahmed
875125cfe4 mobile: clear logs 2025-09-09 12:34:06 +05:00
Abdullah Atta
32d9cdd265 monograph: bump version to 1.2.5 2025-09-09 12:00:28 +05:00
Abdullah Atta
09deae9dbf Merge pull request #8545 from streetwriters/fix-monograph-report-not-working
Add report endpoint to connect-src content policy
2025-09-09 11:59:02 +05:00
Abdullah Atta
337582ac70 monograph: add report endpoint to connect-src content policy 2025-09-09 11:58:29 +05:00
01zulfi
820c78ce81 editor: if selected text is link, autofill url field in insert link popup
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-08 11:28:16 +05:00
Ammar Ahmed
565e692af8 mobile: fix crash during navigation 2025-09-05 12:40:20 +05:00
Ammar Ahmed
94e4721663 mobile: fix resetting navigation 2025-09-05 12:11:39 +05:00
Ammar Ahmed
8157c0812a mobile: reminder fixes
1. Allow editing reminder without changing date
2. Fix colors of buttons
3. Allow picking date and time in date picker dialog
4. Fix auto focus of title and description inputs
2025-09-05 11:30:18 +05:00
Ammar Ahmed
bc36837cd0 mobile: add missing buttons in reminder sheet 2025-09-04 14:26:47 +05:00
Ammar Ahmed
33540b000c mobile: show reminder description in properties 2025-09-04 14:26:33 +05:00
Ammar Ahmed
5a63a45d00 mobile: fix add notebook button 2025-09-04 11:43:36 +05:00
01zulfi
8fce6b5eb6 editor: show image dimensions during drag resizing
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-02 10:18:36 +05:00
Abdullah Atta
4e44661e1b Merge pull request #8487 from 01zulfi/web/reminder-search-delete
web: fix deleting a reminder not removing from search list
2025-08-29 11:43:15 +05:00
Abdullah Atta
81fd1c097e Merge pull request #8509 from 01zulfi/editor/handle-empty-link-hrefs
editor: don't show open & copy tools for empty links
2025-08-29 11:39:39 +05:00
01zulfi
aaf82f359b editor: don't show open & copy tools for empty links
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-08-28 11:45:12 +05:00
Ammar Ahmed
aa7f14ae79 mobile: fix email confirmed description 2025-08-28 10:12:32 +05:00
Ammar Ahmed
0ad63da4a2 mobile: fix tree items in search 2025-08-25 14:51:56 +05:00
Ammar Ahmed
2885b9ac3e mobile: fix notebook tree when searching notebooks 2025-08-25 13:08:29 +05:00
Ammar Ahmed
a15ab99056 mobile: release v3.2.11 2025-08-20 10:32:54 +05:00
Ammar Ahmed
222cdbfa63 mobile: fix navigation 2025-08-20 10:01:19 +05:00
01zulfi
a1d8b34fb4 web: fix deleting a reminder not removing from search list
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-08-20 09:02:44 +05:00
Ammar Ahmed
874352e753 Merge pull request #8400 from streetwriters/mobile-fix-open-note-notification
Fix opening pinned note from notification on android
2025-08-20 08:49:55 +05:00
Ammar Ahmed
49d1ab4be5 Merge pull request #8401 from streetwriters/mobile-fix-notebook-tag-sorting-filter
Fix filtered tags and notebooks not sorted correctly
2025-08-20 08:49:21 +05:00
Ammar Ahmed
65a80dbd46 Merge pull request #8402 from streetwriters/mobile-fix-back-navigation
Fix back navigation on first app launch by resetting routes
2025-08-20 08:48:40 +05:00
Ammar Ahmed
e55456f492 Merge pull request #8467 from streetwriters/fix-cache-clear-error
mobile: fix error when clearing cache on android
2025-08-20 08:48:01 +05:00
Ammar Ahmed
3f9fce0d34 Merge pull request #8482 from streetwriters/fix-open-share-exported-notes-ios
mobile: fix sharing and opening exported notes on iOS
2025-08-20 08:47:21 +05:00
Ammar Ahmed
2ae094cf1f mobile: fix sharing and opening exported notes 2025-08-19 12:51:16 +05:00
Abdullah Atta
bc224f1674 Merge pull request #8466 from 01zulfi/web/list-pane-max-size
web: increase list pane max size to 1000 for large screens
2025-08-19 09:52:28 +05:00
01zulfi
8bb1582cca web: increase list pane max size to 1000
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-08-18 15:48:59 +05:00
Ammar Ahmed
d02125a93c mobile: fix error when clearing cache on android 2025-08-18 13:15:45 +05:00
Abdullah Atta
d91ce4e936 Merge pull request #8389 from 01zulfi/common/update-keybindings
common: update keybindings for editor search, link, internal link, and command palette
2025-08-13 13:30:39 +05:00
Abdullah Atta
deb902549b Merge pull request #8392 from 01zulfi/web/fix-open-settings-shortcut
web: fix open settings shortcut interfering with editor shortcut
2025-08-13 13:29:34 +05:00
Abdullah Atta
e41ed07805 Merge pull request #8385 from Scott-will/master
Handle pasting urls in html
2025-08-13 13:27:14 +05:00
Scott-will
f6e8dc0a88 editor: fix pasting html links with titles that look like links
Signed-off-by: Scott Williams scottwill1999@gmail.com

editor: update tests for html handling

Signed-off-by: Scott Williams scottwill1999@gmail.com

editor: add logic for markdown links

Signed-off-by: Scott Williams scottwill1999@gmail.com

editor: remove plugin in favour of markPasteRule

Signed-off-by: Scott Williams scottwill1999@gmail.com
2025-08-13 13:26:01 +05:00
Abdullah Atta
044dd5ce59 Merge pull request #8391 from 01zulfi/docs/update-desktop-instructions
docs: add missing instruction in desktop app dev setup
2025-08-13 12:21:12 +05:00
01zulfi
6cca06fcbc docs: add standard notes import instructions
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-08-07 12:09:23 +05:00
Ammar Ahmed
b24830666c Merge pull request #8409 from streetwriters/fix-fdroid-build
mobile: fix fdroid build
2025-07-31 09:10:13 +05:00
Ammar Ahmed
470802c3e2 mobile: fix fdroid build 2025-07-31 09:09:38 +05:00
Ammar Ahmed
7e0e1eac93 Merge pull request #8406 from streetwriters/mobile-fix-fdroid-build
mobile: compile lazysodium from source
2025-07-29 15:05:39 +05:00
Ammar Ahmed
58cbf653b2 mobile: compile lazysodium from source 2025-07-29 15:04:49 +05:00
Ammar Ahmed
4b35f2ecd2 mobile: fix back navigation on first app launch 2025-07-28 14:20:11 +05:00
Ammar Ahmed
48d9a863ec mobile: fix filtered tags and notebooks not sorted correctly 2025-07-28 13:42:46 +05:00
Ammar Ahmed
1d616cf68f mobile: fix opening note from notification on android 2025-07-28 11:56:54 +05:00
Ammar Ahmed
cdf6a4bb5b Merge pull request #8398 from streetwriters/mobile-release-3.2.10-fix
mobile: release v3.2.10
2025-07-26 12:48:28 +05:00
Ammar Ahmed
1b3735e2b2 mobile: release v3.2.10 2025-07-26 12:19:13 +05:00
Ammar Ahmed
850fc6b066 Merge pull request #8397 from streetwriters/mobile-release-3.2.9
mobile: release v3.2.9
2025-07-26 11:58:20 +05:00
Ammar Ahmed
8a62070c8e Merge pull request #8394 from streetwriters/mobile-editor-ux-improvement
Mobile editor ux improvement
2025-07-26 10:52:01 +05:00
Ammar Ahmed
8aa31b8380 mobile: fix exiting table not working 2025-07-25 14:24:14 +05:00
Ammar Ahmed
f445a963f9 mobile: improve ux when clicking empty area in editor 2025-07-25 12:44:55 +05:00
Ammar Ahmed
d6e3f27612 mobile: add scroll to top and bottom in editor 2025-07-25 12:44:34 +05:00
Ammar Ahmed
4688d74edb mobile: improvements to editor ux
1. Tapping on empty in editor opens the keyboard
2. Swiping to open the editor when editor is empty opens the keyboard automatically
2025-07-25 11:57:44 +05:00
Ammar Ahmed
1985007039 Merge pull request #8393 from streetwriters/mobile-fix-edit-notebook
mobile: fix edit notebook button causes app freeze on iOS
2025-07-25 11:01:03 +05:00
01zulfi
fb1eb234b6 web: fix open settings shortcut interfering with editor shortcut
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-07-25 08:25:01 +05:00
01zulfi
7e70cd96b9 docs: add missing instruction in desktop app dev setup
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-07-24 15:56:17 +05:00
01zulfi
f9a89783ed common: update keybindings for editor search, link, internal link, and command palette
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-07-23 09:45:12 +05:00
Abdullah Atta
7f3c74b55b editor: fix theme related issues 2025-07-23 08:32:35 +05:00
Ammar Ahmed
765f3a476d mobile: release v3.2.9 2025-07-16 11:26:19 +05:00
107 changed files with 1012 additions and 589 deletions

View File

@@ -6,14 +6,14 @@ on:
branches:
- "master"
paths:
- "app/desktop/**"
- "apps/desktop/**"
# re-run workflow if workflow file changes
- ".github/workflows/desktop.tests.yml"
pull_request:
branches:
- "master"
paths:
- "app/desktop/**"
- "apps/desktop/**"
# re-run workflow if workflow file changes
- ".github/workflows/desktop.tests.yml"

View File

@@ -45,7 +45,7 @@ jobs:
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
platforms: linux/amd64,linux/arm64
- name: Log in to Docker Hub
uses: docker/login-action@v3
@@ -77,7 +77,7 @@ jobs:
context: apps/monograph
file: apps/monograph/Dockerfile
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
platforms: linux/amd64,linux/arm64
tags: streetwriters/monograph:${{ steps.package_metadata.outputs.app_version }},streetwriters/monograph:latest
cache-from: streetwriters/monograph:latest

View File

@@ -24,7 +24,7 @@ Requirements:
Before you can do anything, you'll need to [install Node.js](https://nodejs.org/en/download/) v16 or later on your system.
Once you have completed the setup, the first step is to `clone` the monorepo:
1. `clone` the monorepo:
```bash
git clone https://github.com/streetwriters/notesnook.git
@@ -33,19 +33,28 @@ git clone https://github.com/streetwriters/notesnook.git
cd notesnook
```
Once you are inside the `./notesnook` directory, run the preparation step:
2. Install dependencies:
```bash
# this might take a while to complete
npm install
```
Now you can finally start the desktop app for development:
3. Run the webapp for desktop environment:
```bash
cd apps/web
npm run start:desktop
```
4. In a separate terminal session, run the desktop app from the root of the project:
```bash
npm run start:desktop
```
### Release mode
To run the app in release mode:
```bash

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "3.2.3",
"version": "3.2.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "3.2.3",
"version": "3.2.4",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.2.3",
"version": "3.2.4",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/cjs/index.js",

View File

@@ -137,6 +137,7 @@ export async function deleteFile(
export async function clearFileStorage() {
try {
await createCacheDir();
const files = await RNFetchBlob.fs.ls(cacheDir);
const oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
@@ -221,6 +222,7 @@ export async function deleteCacheFileByName(name: string) {
}
export async function deleteDCacheFiles() {
await createCacheDir();
const files = await RNFetchBlob.fs.ls(cacheDir);
for (const file of files) {
if (file.includes("_dcache") || file.startsWith("NN_")) {

View File

@@ -33,6 +33,9 @@ export function hideAuth() {
eSendEvent(eCloseLoginDialog);
if (initialAuthMode.current === AuthMode.welcomeSignup) {
Navigation.replace("FluidPanelsView");
setTimeout(() => {
Navigation.resetRootState();
}, 1000);
} else {
Navigation.goBack();
}

View File

@@ -89,6 +89,9 @@ const Auth = ({ navigation, route }) => {
onPress={() => {
if (initialAuthMode.current === 2) {
Navigation.replace("FluidPanelsView");
setTimeout(() => {
Navigation.resetRootState();
}, 1000);
} else {
Navigation.goBack();
}

View File

@@ -30,6 +30,7 @@ import { BackHandler, Platform, ViewProps } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import Animated, {
runOnJS,
SharedValue,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
@@ -67,6 +68,7 @@ export interface TabsRef {
setScrollEnabled: () => true;
isDrawerOpen: () => boolean;
node: RefObject<Animated.View>;
tabChangedFromSwipeAction: SharedValue<boolean>;
}
export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
@@ -116,6 +118,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
const isLoaded = useRef(false);
const prevWidths = useRef(widths);
const isIPhone = Platform.OS === "ios";
const tabChangedFromSwipeAction = useSharedValue(false);
useEffect(() => {
if (deviceMode === "tablet" || fullscreen) {
@@ -191,6 +194,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
: withTiming(editorPosition);
currentTab.value = 2;
}
tabChangedFromSwipeAction.value = false;
},
goToIndex: (index: number, animated = true) => {
if (deviceMode === "tablet") {
@@ -210,6 +214,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
: editorPosition;
currentTab.value = 2;
}
tabChangedFromSwipeAction.value = false;
},
unlock: () => {
forcedLock.value = false;
@@ -252,7 +257,8 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
},
page: () => (currentTab.value === 1 ? "home" : "editor"),
setScrollEnabled: () => true,
node: node
node: node,
tabChangedFromSwipeAction: tabChangedFromSwipeAction
}),
[
currentTab,
@@ -262,7 +268,8 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
homePosition,
editorPosition,
forcedLock,
isDrawerOpen
isDrawerOpen,
tabChangedFromSwipeAction
]
);
@@ -362,18 +369,20 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
isDrawerOpen.value = true;
currentTab.value = 1;
runOnJS(onDrawerStateChange)(true);
tabChangedFromSwipeAction.value = true;
return;
} else if (!isSwipeLeft && finalValue > 100) {
translateX.value = withSpring(homePosition, animationConfig);
isDrawerOpen.value = false;
currentTab.value = 1;
runOnJS(onDrawerStateChange)(false);
tabChangedFromSwipeAction.value = true;
return;
} else if (!isSwipeLeft && finalValue < 100) {
translateX.value = withSpring(0, animationConfig);
isDrawerOpen.value = true;
currentTab.value = 1;
tabChangedFromSwipeAction.value = true;
return;
}
}
@@ -384,6 +393,8 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
translateX.value = withSpring(editorPosition, animationConfig);
currentTab.value = 2;
isDrawerOpen.value = false;
tabChangedFromSwipeAction.value = true;
return;
}
}
@@ -398,6 +409,8 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
translateX.value = withSpring(editorPosition, animationConfig);
currentTab.value = 2;
}
tabChangedFromSwipeAction.value = true;
return;
}

View File

@@ -153,7 +153,6 @@ export const SearchResult = (props: SearchResultProps) => {
for (let i = 0; i <= index; i++) {
activeIndex += props.item.content[i].length;
}
console.log(activeIndex);
openNote(activeIndex);
}}
>

View File

@@ -21,13 +21,14 @@ import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import { eSendEvent, presentSheet } from "../../services/event-manager";
import { ColorValues } from "../../utils/colors";
import { eOnLoadNote } from "../../utils/events";
import { fluidTabsRef } from "../../utils/global-refs";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import SheetProvider from "../sheet-provider";
import { IconButton } from "../ui/icon-button";
import { Pressable } from "../ui/pressable";
@@ -38,8 +39,6 @@ import { DateMeta } from "./date-meta";
import { Items } from "./items";
import Notebooks from "./notebooks";
import { TagStrip, Tags } from "./tags";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { DefaultAppStyles } from "../../utils/styles";
const Line = ({ top = 6, bottom = 6 }) => {
const { colors } = useThemeColors();
@@ -151,7 +150,8 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
) : null}
</View>
{item.type === "notebook" && item.description ? (
{(item.type === "notebook" || item.type === "reminder") &&
item.description ? (
<Paragraph>{item.description}</Paragraph>
) : null}

View File

@@ -65,7 +65,9 @@ const COLUMN_BAR_ITEMS: ActionId[] = [
"edit-notebook",
"move-notes",
"move-notebook",
"edit-reminder",
"pin",
"disable-reminder",
"default-notebook",
"default-tag",
"default-homepage",

View File

@@ -29,6 +29,10 @@ import { ColorTags } from "./color-tags";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../utils/styles";
import ManageTags from "../../screens/manage-tags";
import {
paddingTop,
paddingVertical
} from "deprecated-react-native-prop-types/DeprecatedLayoutPropTypes";
export const Tags = ({ item, close }) => {
const { colors } = useThemeColors();
@@ -86,7 +90,6 @@ export const TagStrip = ({ item, close }) => {
flexDirection: "row",
flexWrap: "wrap",
alignItems: "center",
marginTop: DefaultAppStyles.GAP_VERTICAL,
gap: 5
}}
>
@@ -107,16 +110,13 @@ const TagItem = ({ tag, close }) => {
const style = {
paddingHorizontal: 0,
borderRadius: 100,
marginTop: 0,
backgroundColor: "transparent"
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
};
return (
<Button
onPress={onPress}
title={"#" + tag.title}
type="plain"
height={20}
fontSize={AppFontSize.xs}
style={style}
textStyle={{

View File

@@ -95,8 +95,6 @@ const SheetProvider = ({ context = "global" }) => {
[context]
);
console.log(data?.keyboardHandlerDisabled);
return !visible || !data ? null : (
<SheetWrapper
fwdRef={actionSheetRef}

View File

@@ -310,6 +310,7 @@ const ExportNotesSheet = ({
ToastManager.error(e as Error);
});
} else {
await sleep(500);
FileViewer.open(result?.filePath, {
showOpenWithDialog: true,
showAppsSuggestions: true
@@ -338,6 +339,7 @@ const ExportNotesSheet = ({
.getState()
.setAppDidEnterBackgroundForAction(true);
if (Platform.OS === "ios") {
await sleep(500);
Share.open({
url: result?.fileDir + result.fileName
}).catch(() => {

View File

@@ -38,10 +38,9 @@ import { eCloseSheet, eOnNotebookUpdated } from "../../../utils/events";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import { Properties } from "../../properties";
import SheetProvider from "../../sheet-provider";
import { NotebookItem } from "../../side-menu/notebook-item";
import { useSideMenuNotebookTreeStore } from "../../side-menu/stores";
import { IconButton } from "../../ui/icon-button";
import Paragraph from "../../ui/typography/paragraph";
import { AddNotebookSheet } from "../add-notebook";
@@ -60,13 +59,8 @@ export const Notebooks = (props: {
close?: (ctx?: string) => void;
}) => {
const tree = useNotebookTreeStore((state) => state.tree);
const [isLoading, setIsLoading] = useState(true);
const { colors } = useThemeColors();
const [notebooks, setNotebooks] = useState<Notebook[]>();
const [filteredNotebooks, setFilteredNotebooks] =
React.useState<VirtualizedGrouping<Notebook>>();
const searchTimer = React.useRef<NodeJS.Timeout>();
const lastQuery = React.useRef<string>();
const loadRootNotebooks = React.useCallback(async () => {
const notebooks = await db.relations
.from(
@@ -95,7 +89,6 @@ export const Notebooks = (props: {
useEffect(() => {
(async () => {
loadRootNotebooks();
setIsLoading(false);
})();
}, [loadRootNotebooks]);
@@ -151,7 +144,7 @@ export const Notebooks = (props: {
height: 400
}}
>
<SheetProvider context="local" />
<Dialog context="local" />
<View
style={{
@@ -175,7 +168,7 @@ export const Notebooks = (props: {
}}
name="plus"
onPress={() => {
AddNotebookSheet.present(props.rootNotebook, undefined, "local");
AddNotebookSheet.present(undefined, props.rootNotebook, "local");
}}
/>
</View>
@@ -244,20 +237,18 @@ const NotebookItemWrapper = React.memo(
const onItemUpdate = React.useCallback(async () => {
const notebook = await db.notebooks.notebook(item.notebook.id);
if (notebook) {
useSideMenuNotebookTreeStore
.getState()
.updateItem(item.notebook.id, notebook);
useNotebookTreeStore.getState().updateItem(item.notebook.id, notebook);
if (expanded) {
useSideMenuNotebookTreeStore
useNotebookTreeStore
.getState()
.setTree(
await useSideMenuNotebookTreeStore
await useNotebookTreeStore
.getState()
.fetchAndAdd(item.notebook.id, item.depth + 1)
);
}
} else {
useSideMenuNotebookTreeStore.getState().removeItem(item.notebook.id);
useNotebookTreeStore.getState().removeItem(item.notebook.id);
}
}, [expanded, item.depth, item.notebook.id]);
@@ -274,17 +265,15 @@ const NotebookItemWrapper = React.memo(
onToggleExpanded={async () => {
useNotebookExpandedStore.getState().setExpanded(item.notebook.id);
if (!expanded) {
useSideMenuNotebookTreeStore
useNotebookTreeStore
.getState()
.setTree(
await useSideMenuNotebookTreeStore
await useNotebookTreeStore
.getState()
.fetchAndAdd(item.notebook.id, item.depth + 1)
);
} else {
useSideMenuNotebookTreeStore
.getState()
.removeChildren(item.notebook.id);
useNotebookTreeStore.getState().removeChildren(item.notebook.id);
}
}}
selected={selected}

View File

@@ -312,7 +312,6 @@ export const UserSheet = () => {
icon: "logout",
title: strings.logout(),
onPress: async () => {
console.log("logout");
ref.current?.hide();
await sleep(300);
logoutUser();

View File

@@ -49,7 +49,8 @@ export const NotebookItem = ({
onPress,
onLongPress,
onAddNotebook,
canDisableSelectionMode
canDisableSelectionMode,
disableExpand
}: {
index: number;
item: TreeItem;
@@ -64,6 +65,7 @@ export const NotebookItem = ({
onLongPress?: () => void;
onAddNotebook?: () => void;
canDisableSelectionMode?: boolean;
disableExpand?: boolean;
}) => {
const notebook = item.notebook;
const isFocused = focused;
@@ -166,7 +168,7 @@ export const NotebookItem = ({
}
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
onPress={() => {
if (item.hasChildren) {
if (item.hasChildren && !disableExpand) {
onToggleExpanded?.();
} else {
onPress?.();
@@ -182,7 +184,7 @@ export const NotebookItem = ({
borderRadius: defaultBorderRadius
}}
name={
!item.hasChildren
!item.hasChildren || disableExpand
? "book-outline"
: expanded
? "chevron-down"

View File

@@ -20,8 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Notebook } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import { FlashList } from "@shopify/flash-list";
import React, { useEffect, useState } from "react";
import { FlatList, TextInput, View } from "react-native";
import { TextInput, View } from "react-native";
import { db } from "../../common/database";
import NotebookScreen from "../../screens/notebook";
import Navigation from "../../services/navigation";
@@ -42,6 +43,7 @@ import {
useSideMenuNotebookSelectionStore.setState({
multiSelect: true
});
export const SideMenuNotebooks = () => {
const tree = useSideMenuNotebookTreeStore((state) => state.tree);
const [notebooks, loading] = useNotebooks();
@@ -64,13 +66,19 @@ export const SideMenuNotebooks = () => {
const updateNotebooks = React.useCallback(() => {
if (lastQuery.current) {
// useSideMenuNotebookTreeStore.setState({
// isSearching: true
// });
db.lookup
.notebooks(lastQuery.current)
.sorted()
.sorted(db.settings.getGroupOptions("notebooks"))
.then((filtered) => {
setFilteredNotebooks(filtered);
});
} else {
// useSideMenuNotebookTreeStore.setState({
// isSearching: false
// });
setFilteredNotebooks(notebooks);
}
}, [notebooks]);
@@ -137,13 +145,13 @@ export const SideMenuNotebooks = () => {
/>
) : (
<>
<FlatList
<FlashList
data={tree}
bounces={false}
bouncesZoom={false}
overScrollMode="never"
keyExtractor={(item) => item.notebook.id}
windowSize={3}
// keyExtractor={(item,) => item.notebook.id}
estimatedItemSize={30}
ListHeaderComponent={
<View
style={{
@@ -196,7 +204,9 @@ const NotebookItemWrapper = React.memo(
const expanded = useSideMenuNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
);
const disableExpand = useSideMenuNotebookTreeStore(
(state) => state.isSearching
);
const selectionEnabled = useSideMenuNotebookSelectionStore(
(state) => state.enabled
);
@@ -256,6 +266,7 @@ const NotebookItemWrapper = React.memo(
.removeChildren(item.notebook.id);
}
}}
disableExpand={disableExpand}
selected={selected}
selectionEnabled={selectionEnabled}
selectionStore={useSideMenuNotebookSelectionStore}

View File

@@ -220,7 +220,7 @@ export const SideMenuTags = () => {
if (lastQuery.current) {
db.lookup
.tags(lastQuery.current.trim())
.sorted()
.sorted(db.settings.getGroupOptions("tags"))
.then(async (filtered) => {
setFilteredTags(filtered);
});

View File

@@ -154,7 +154,7 @@ const Input = ({
isError = customValidator && value === customValidator();
break;
case "url":
isError = isURL(value);
isError = isURL(value, { allow_underscores: true });
break;
case "phonenumber": {
const result = phone(value, {

View File

@@ -54,6 +54,7 @@ type ButtonTypes =
| "accent"
| "shade"
| "secondary"
| "selectedAccent"
| "secondaryAccented"
| "inverted"
| "white"
@@ -126,6 +127,18 @@ const buttonTypes = (
isDark
)
},
selectedAccent: {
primary: colors.selected.accent,
text: colors.selected.accentForeground,
selected: colors.selected.accent,
borderWidth: 0.8,
borderColor: getColorLinearShade(colors.selected.accent, 0.05, isDark),
borderSelectedColor: getColorLinearShade(
colors.selected.accent,
0.05,
isDark
)
},
secondaryAccented: {
primary: colors.secondary.background,
text: colors.primary.accent,

View File

@@ -64,8 +64,6 @@ const SheetWrapper = ({
const isGestureNavigationEnabled =
NotesnookModule.isGestureNavigationEnabled();
console.log(isGestureNavigationEnabled);
const style = React.useMemo(() => {
return {
width: largeTablet || smallTablet ? width : "100%",

View File

@@ -336,7 +336,7 @@ const emailconfirmed: { id: string; steps: TStep[] } = {
steps: [
{
title: strings.emailConfirmed(),
text: strings.emailNotConfirmedDesc(),
text: strings.emailConfirmedDesc(),
walkthroughItem: (colors) => (
<SvgView src={WELCOME_SVG(colors.primary.paragraph)} />
),

View File

@@ -19,9 +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: "Improved Search",
body: "Improved search results with better relevance and highlighting."
}
];
export const features: FeatureType[] = [];

View File

@@ -26,12 +26,14 @@ import { useCallback } from "react";
* @returns Is keyboard floating or not
*/
const useIsFloatingKeyboard = () => {
const { width } = useWindowDimensions();
const { width, height } = useWindowDimensions();
const [floating, setFloating] = useState<boolean>(false);
const onKeyboardWillChangeFrame = useCallback(
(event: KeyboardEvent) => {
setFloating(event.endCoordinates.width < width);
setFloating(
event.endCoordinates.width === 0 || event.endCoordinates.width < width
);
},
[width]
);
@@ -41,8 +43,13 @@ const useIsFloatingKeyboard = () => {
"keyboardWillChangeFrame",
onKeyboardWillChangeFrame
);
const sub2 = Keyboard.addListener(
"keyboardWillShow",
onKeyboardWillChangeFrame
);
return () => {
sub1?.remove();
sub2?.remove();
};
}, [onKeyboardWillChangeFrame, width]);

View File

@@ -30,6 +30,10 @@ import React, {
useState
} from "react";
import { Dimensions, LayoutChangeEvent, Platform, View } from "react-native";
import Orientation, {
OrientationType,
useDeviceOrientationChange
} from "react-native-orientation-locker";
import Animated, {
useAnimatedStyle,
useSharedValue,
@@ -67,10 +71,6 @@ import {
} from "../utils/events";
import { editorRef, fluidTabsRef } from "../utils/global-refs";
import { AppNavigationStack } from "./navigation-stack";
import Orientation, {
OrientationType,
useDeviceOrientationChange
} from "react-native-orientation-locker";
const MOBILE_SIDEBAR_SIZE = 0.85;
@@ -535,6 +535,15 @@ const onChangeTab = async (event: { i: number; from: number }) => {
) {
eSendEvent(eUnlockNote);
}
if (
fluidTabsRef.current?.tabChangedFromSwipeAction.value &&
!useTabStore.getState().getNoteIdForTab(useTabStore.getState().currentTab)
) {
editorController?.current?.commands?.focus(
useTabStore.getState().currentTab
);
}
} else {
if (event.from === 2) {
deactivateKeepAwake();

View File

@@ -30,6 +30,7 @@ import useNavigationStore, {
import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { rootNavigatorRef } from "../utils/global-refs";
import Navigation from "../services/navigation";
const RootStack = createNativeStackNavigator();
const AppStack = createNativeStackNavigator();
@@ -255,12 +256,18 @@ export const RootNavigation = () => {
(state) => state.settings.introCompleted
);
const clearSelection = useSelectionStore((state) => state.clearSelection);
const onStateChange = React.useCallback(() => {
if (useSelectionStore.getState().selectionMode) {
clearSelection();
}
hideAllTooltips();
}, [clearSelection]);
const onStateChange = React.useCallback(
(state: any) => {
if (useSelectionStore.getState().selectionMode) {
clearSelection();
}
setTimeout(() => {
Navigation.resetRootState(state);
}, 1000);
hideAllTooltips();
},
[clearSelection]
);
return (
<NavigationContainer onStateChange={onStateChange} ref={rootNavigatorRef}>

View File

@@ -47,6 +47,7 @@ import SettingsService from "../../services/settings";
import { useRelationStore } from "../../stores/use-relation-store";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { getFormattedDate } from "@notesnook/common";
const ReminderModes =
Platform.OS === "ios"
@@ -114,6 +115,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
!reminder ? referencedItem?.headline : reminder?.description
);
const titleRef = useRef<TextInput>(null);
const descriptionRef = useRef<TextInput>(null);
const timer = useRef<NodeJS.Timeout>();
const showDatePicker = () => {
@@ -128,7 +130,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
timer.current = setTimeout(() => {
hideDatePicker();
setDate(date);
}, 50);
}, 10);
};
function nth(n: number) {
return (
@@ -166,8 +168,11 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
throw new Error(strings.selectDayError());
if (!title.current) throw new Error(strings.setTitleError());
if (date.getTime() < Date.now() && reminderMode === "once") {
titleRef?.current?.focus();
if (
date.getTime() < Date.now() &&
reminderMode === "once" &&
!props.route.params.reminder
) {
throw new Error(strings.dateError());
}
@@ -242,15 +247,20 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
defaultValue={reminder?.title || referencedItem?.title}
placeholder={strings.remindeMeOf()}
onChangeText={(text) => (title.current = text)}
autoFocus
wrapperStyle={{
marginTop: DefaultAppStyles.GAP_VERTICAL
}}
onSubmit={() => {
descriptionRef.current?.focus();
}}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
fwdRef={descriptionRef}
placeholder={strings.addShortNote()}
onChangeText={(text) => (details.current = text)}
containerStyle={{
@@ -290,7 +300,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
type={
reminderMode ===
ReminderModes[mode as keyof typeof ReminderModes]
? "selected"
? "selectedAccent"
: "plain"
}
onPress={() => {
@@ -443,9 +453,10 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
mode="datetime"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
isDarkModeEnabled={isDark}
is24Hour={db.settings.getTimeFormat() === "24-hour"}
date={date || new Date(Date.now())}
/>
@@ -476,7 +487,9 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
width: "100%"
}}
title={
date ? date.toLocaleDateString() : strings.selectDate()
date
? getFormattedDate(date, "date-time")
: strings.selectDate()
}
type={date ? "secondaryAccented" : "secondary"}
icon="calendar"
@@ -578,7 +591,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "selected"
? "selectedAccent"
: "plain"
}
onPress={() => {

View File

@@ -131,7 +131,6 @@ const showActionsheet = async () => {
.getState()
.getNoteIdForTab(useTabStore.getState().currentTab!);
if (noteId) {
console.log("OPEN NOTE");
const note = await db.notes?.note(noteId);
Properties.present(note, false);
} else {

View File

@@ -875,7 +875,6 @@ export const useEditor = (
}
lastContentChangeTime.current[note.id] = note.dateEdited;
console.log(tab.session?.selection);
await postMessage(
NativeEvents.updatehtml,
{

View File

@@ -300,6 +300,8 @@ const NotebookItemWrapper = React.memo(
const expanded = useNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
);
const disableExpand = useNotebookTreeStore((state) => state.isSearching);
const selectionEnabled = useNotebookSelectionStore(
(state) => state.enabled
);
@@ -371,6 +373,7 @@ const NotebookItemWrapper = React.memo(
selectionEnabled={selectionEnabled}
selectionStore={useNotebookSelectionStore}
onItemUpdate={onItemUpdate}
disableExpand={disableExpand}
focused={false}
onPress={onPress}
onAddNotebook={() => {

View File

@@ -112,7 +112,7 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
if (query && query.trim() !== "") {
db.lookup
.tags(query)
.sorted()
.sorted(db.settings.getGroupOptions("tags"))
.then((items) => {
setTags(items);
});
@@ -249,6 +249,7 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
}}
>
<Header title={strings.manageTags()} canGoBack />
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP

View File

@@ -328,6 +328,8 @@ const NotebookItemWrapper = React.memo(
const expanded = useNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
);
const disableExpand = useNotebookTreeStore((state) => state.isSearching);
const selectionEnabled = useNotebookSelectionStore(
(state) => state.enabled
);
@@ -378,6 +380,7 @@ const NotebookItemWrapper = React.memo(
useNotebookTreeStore.getState().removeChildren(item.notebook.id);
}
}}
disableExpand={disableExpand}
selected={selected}
selectionEnabled={selectionEnabled}
selectionStore={useNotebookSelectionStore}

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 { StackActions } from "@react-navigation/native";
import { NavigationHelpers, StackActions } from "@react-navigation/native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { useFavoriteStore } from "../stores/use-favorite-store";
import useNavigationStore, {
@@ -166,6 +166,32 @@ function closeDrawer() {
fluidTabsRef.current?.closeDrawer();
}
function resetRootState(
_state?: ReturnType<NavigationHelpers<any, any>["getState"]>
) {
const state = _state || rootNavigatorRef.getState();
const focusedRoute = state.routes[state.index];
if (state.routes.length < 2) return;
const routes = state.routes.filter(
(route) =>
(route.name !== "Auth" && route.name !== "Welcome") ||
route.key === focusedRoute.key
);
if (routes.length === state.routes.length) return;
if (routes.length === 0) {
routes.push(focusedRoute);
}
const newIndex = routes.findIndex((route) => route.key === focusedRoute.key);
rootNavigatorRef.reset({
...state,
routes: routes,
index: newIndex
});
}
const Navigation = {
navigate,
goBack,
@@ -177,7 +203,8 @@ const Navigation = {
queueRoutesForUpdate,
routeNeedsUpdate,
routeNames,
routeUpdateFunctions
routeUpdateFunctions,
resetRootState
};
export default Navigation;

View File

@@ -17,6 +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 { getFormattedReminderTime } from "@notesnook/common";
import { isReminderActive, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import notifee, {
@@ -36,6 +37,7 @@ import dayjs, { Dayjs } from "dayjs";
import { encodeNonAsciiHTML } from "entities";
import { Platform } from "react-native";
import { db, setupDatabase } from "../common/database";
import { MMKV } from "../common/database/mmkv";
import { presentDialog } from "../components/dialog/functions";
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
import { editorState } from "../screens/editor/tiptap/utils";
@@ -52,8 +54,6 @@ import { DDS } from "./device-detection";
import { eSendEvent } from "./event-manager";
import Navigation from "./navigation";
import SettingsService from "./settings";
import { getFormattedReminderTime } from "@notesnook/common";
import { MMKV } from "../common/database/mmkv";
let pinned: DisplayedNotification[] = [];
@@ -454,7 +454,7 @@ async function loadNote(id: string, jump: boolean) {
const tab = useTabStore.getState().getTabForNote(id);
if (useTabStore.getState().currentTab !== tab) {
eSendEvent(eOnLoadNote, {
note: note
item: note
});
}
}
@@ -919,12 +919,14 @@ async function pinQuickNote(launch: boolean) {
async function setupReminders(checkNeedsScheduling = false) {
const reminders = ((await db.reminders?.all.items()) as Reminder[]) || [];
let notificationsCancelled = false;
if (Platform.OS === "android") {
// If the API level has changed, cancel all notifications.
// This is to ensure that the app does not crash on Android 14+.
const API_LEVEL = MMKV.getInt("android_apiLevel");
if (API_LEVEL !== (Platform.Version as number)) {
await notifee.cancelAllNotifications();
notificationsCancelled = true;
MMKV.setInt("android_apiLevel", Platform.Version as number);
}
}
@@ -938,20 +940,22 @@ async function setupReminders(checkNeedsScheduling = false) {
// Skip reminders that are not repeating and their trigger date is in past.
if (reminder.mode === "once" && dayjs().isAfter(reminder.date)) continue;
const pending = triggers.filter((t) =>
t.notification.id?.startsWith(reminder.id)
);
if (!notificationsCancelled) {
const pending = triggers.filter((t) =>
t.notification.id?.startsWith(reminder.id)
);
let needsReschedule = pending.length === 0 ? true : false;
if (!needsReschedule) {
needsReschedule = pending[0].notification.data?.dateModified
? parseInt(pending[0].notification.data?.dateModified as string) <
reminder.dateModified
: true;
let needsReschedule = pending.length === 0 ? true : false;
if (!needsReschedule) {
needsReschedule = pending[0].notification.data?.dateModified
? parseInt(pending[0].notification.data?.dateModified as string) <
reminder.dateModified
: true;
}
if (!needsReschedule && checkNeedsScheduling) continue;
}
if (!needsReschedule && checkNeedsScheduling) continue;
await scheduleNotification(reminder);
}
// Check for any triggers whose notifications

View File

@@ -49,6 +49,7 @@ export function createNotebookTreeStores(
) {
const useNotebookTreeStore = create<{
tree: TreeItem[];
isSearching?: boolean;
setTree: (tree: TreeItem[]) => void;
removeItem: (id: string) => void;
addNotebooks: (
@@ -113,16 +114,32 @@ export function createNotebookTreeStores(
}
}
const newTreeItems = notebooks.map((notebook) => {
return {
parentId,
notebook,
depth: depth,
hasChildren: items.some((item) => {
return item.fromId === notebook.id;
})
};
});
const rootTreeItems = newTree.filter((item) => item.parentId === "root");
const newTreeItems = notebooks.reduce((acc, notebook) => {
if (!rootTreeItems.find((item) => item.notebook.id === notebook.id)) {
acc.push({
parentId,
notebook,
depth: depth,
hasChildren: false
});
}
return acc;
}, [] as TreeItem[]);
if (parentId === "root") {
rootTreeItems.splice(0, 0, ...newTreeItems);
}
for (const treeItem of newTreeItems) {
treeItem.hasChildren = items.some((item) => {
return (
rootTreeItems.findIndex(
(treeItem) => treeItem.notebook.id === item.toId
) === -1 && item.fromId === treeItem.notebook.id
);
});
}
newTree.splice(parentIndex + 1, 0, ...newTreeItems);
@@ -130,7 +147,7 @@ export function createNotebookTreeStores(
const expanded =
useNotebookExpandedStore.getState().expanded[item.notebook.id] &&
item.hasChildren;
if (expanded) {
if (expanded && !get().isSearching) {
newTree = await get().fetchAndAdd(
item.notebook.id,
depth + 1,

View File

@@ -89,10 +89,10 @@ function getSize() {
xs: normalize(13.5) * scale.fontScale,
sm: normalize(14.5) * scale.fontScale,
md: normalize(16.5) * scale.fontScale,
lg: normalize(22) * scale.fontScale,
xl: normalize(24) * scale.fontScale,
xxl: normalize(28) * scale.fontScale,
xxxl: normalize(32) * scale.fontScale
lg: normalize(20) * scale.fontScale,
xl: normalize(22) * scale.fontScale,
xxl: normalize(25) * scale.fontScale,
xxxl: normalize(30) * scale.fontScale
};
}

View File

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

View File

@@ -1,3 +1,5 @@
- Improved editor UI for better usability
- Improved UX for creating and editing reminders
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -5,5 +5,5 @@ rootProject.name = 'Notesnook'
include ':app'
includeBuild('../../node_modules/@react-native/gradle-plugin')
include ":lazysodium"
project(':lazysodium').projectDir = new File(rootProject.projectDir, '../../node_modules/@ammarahmed/react-native-sodium/android/lazysodium')
include ":lazysodium-android"
project(':lazysodium-android').projectDir = new File(rootProject.projectDir, '../../node_modules/@ammarahmed/react-native-sodium/android/lazysodium-android/app')

View File

@@ -1089,7 +1089,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2143;
CURRENT_PROJECT_VERSION = 2147;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1163,7 +1163,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.8;
MARKETING_VERSION = 3.2.12;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1194,7 +1194,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2143;
CURRENT_PROJECT_VERSION = 2147;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1268,7 +1268,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.8;
MARKETING_VERSION = 3.2.12;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1427,7 +1427,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2143;
CURRENT_PROJECT_VERSION = 2147;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1439,7 +1439,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.8;
MARKETING_VERSION = 3.2.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1470,7 +1470,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2143;
CURRENT_PROJECT_VERSION = 2147;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1483,7 +1483,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.8;
MARKETING_VERSION = 3.2.12;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1513,7 +1513,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2143;
CURRENT_PROJECT_VERSION = 2147;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1594,7 +1594,7 @@
"@executable_path/../../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.8;
MARKETING_VERSION = 3.2.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1625,7 +1625,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2143;
CURRENT_PROJECT_VERSION = 2147;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1707,7 +1707,7 @@
"@executable_path/../../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.8;
MARKETING_VERSION = 3.2.12;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1374,7 +1374,7 @@ PODS:
- SDWebImage (~> 5.11.1)
- react-native-share-extension (2.9.0):
- React
- react-native-sodium (1.6.4):
- react-native-sodium (1.6.5):
- React
- react-native-theme-switch-animation (0.6.0):
- DoubleConversion
@@ -2435,7 +2435,7 @@ SPEC CHECKSUMS:
react-native-safe-area-context: 9d72abf6d8473da73033b597090a80b709c0b2f1
react-native-screenguard: 82437eeb0086a90b5e5d7e54130bb04fb406373e
react-native-share-extension: bcb7e466390a9e50c742f4b1019d6f181aedd7ad
react-native-sodium: 5b55f74705005612e12838ffd583927229a65e6a
react-native-sodium: 285eec063e4232cb67347ef6a434b85e588d38cb
react-native-theme-switch-animation: d90fe2de0d9e87a63cd6235d98cba6e7054e9a10
react-native-webview: 079eca50edf657503318b66687dadfb903731aa8
React-nativeconfig: ecf4dc92c40b97e2b3f0c619938f78bfd6507b08

View File

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

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.2.2",
"version": "3.2.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.2.2",
"version": "3.2.10",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -1098,7 +1098,7 @@
"refractor": "^4.8.1",
"sqlite-better-trigram": "^0.0.3",
"sqlite-regex": "^0.2.4-alpha.1",
"sqlite3-fts5-html": "^0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"vitest": "2.1.8",
"vitest-fetch-mock": "^0.2.2",
"ws": "^8.13.0"
@@ -28324,7 +28324,7 @@
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
"@ammarahmed/react-native-share-extension": "^2.9.0",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@ammarahmed/react-native-sodium": "^1.6.5",
"@bam.tech/react-native-image-resizer": "3.0.11",
"@callstack/repack": "~5.1.2",
"@formatjs/intl-locale": "4.0.0",
@@ -28478,9 +28478,9 @@
}
},
"node_modules/@ammarahmed/react-native-sodium": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.4.tgz",
"integrity": "sha512-+OIBABV/8IPXVQPRxiub0WW86X/bs14GHnQ1VqtxWGv7phwfyOVzi9c1/LDeRqjCDKBAYHOv2a5PLHZb2RzeGw==",
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.5.tgz",
"integrity": "sha512-x8YkHWHaHiB4QomjTAXyw/aLjrsFMWumJxSRrEeUOVeArVszbrwZ88B+2zAq+sPZ+HSMi/YrnrvgzbwvOCLnNA==",
"license": "ISC"
},
"node_modules/@ampproject/remapping": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.2.8",
"version": "3.2.12",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -0,0 +1,39 @@
diff --git a/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js b/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js
index c7f1df6..0141f28 100644
--- a/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js
+++ b/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js
@@ -109,7 +109,7 @@ function useNavigationCache(_ref) {
})),
isFocused: () => {
const state = getState();
- if (state.routes[state.index].key !== route.key) {
+ if (state.routes[state.index]?.key !== route.key) {
return false;
}
diff --git a/node_modules/@react-navigation/core/lib/module/useNavigationCache.js b/node_modules/@react-navigation/core/lib/module/useNavigationCache.js
index c4d65c6..89abe63 100644
--- a/node_modules/@react-navigation/core/lib/module/useNavigationCache.js
+++ b/node_modules/@react-navigation/core/lib/module/useNavigationCache.js
@@ -100,7 +100,7 @@ export default function useNavigationCache(_ref) {
})),
isFocused: () => {
const state = getState();
- if (state.routes[state.index].key !== route.key) {
+ if (state.routes[state.index]?.key !== route.key) {
return false;
}
diff --git a/node_modules/@react-navigation/core/src/useNavigationCache.tsx b/node_modules/@react-navigation/core/src/useNavigationCache.tsx
index 390120a..1b0f2d1 100644
--- a/node_modules/@react-navigation/core/src/useNavigationCache.tsx
+++ b/node_modules/@react-navigation/core/src/useNavigationCache.tsx
@@ -157,7 +157,7 @@ export default function useNavigationCache<
isFocused: () => {
const state = getState();
- if (state.routes[state.index].key !== route.key) {
+ if (state.routes[state.index]?.key !== route.key) {
return false;
}

View File

@@ -1,3 +1,24 @@
diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 77c81f0..be34558 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -156,7 +156,8 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
if (
Platform.OS === 'ios' &&
- this._windowWidth !== this._keyboardEvent.endCoordinates.width
+ this._windowWidth !== this._keyboardEvent.endCoordinates.width &&
+ this._windowWidth > this._keyboardEvent.endCoordinates.width
) {
// The keyboard is not the standard bottom-of-the-screen keyboard. For example, floating keyboard on iPadOS.
this._setBottom(0);
diff --git a/node_modules/react-native/scripts/.packager.env b/node_modules/react-native/scripts/.packager.env
new file mode 100644
index 0000000..361f5fb
--- /dev/null
+++ b/node_modules/react-native/scripts/.packager.env
@@ -0,0 +1 @@
+export RCT_METRO_PORT=8081
diff --git a/node_modules/react-native/scripts/packager.sh b/node_modules/react-native/scripts/packager.sh
index 00d6ebc..6846832 100755
--- a/node_modules/react-native/scripts/packager.sh

View File

@@ -1,5 +1,5 @@
FROM oven/bun:1.2.2-alpine
FROM oven/bun:1.2.21-slim
RUN mkdir -p /home/bun/app && chown -R bun:bun /home/bun/app

View File

@@ -90,7 +90,7 @@ function getContentSecurityPolicy(nonce?: string) {
const connect_src =
process.env.NODE_ENV === "development"
? "'self' ws://localhost:*"
: "'self'";
: "'self' https://notesnook.com/api/v1/reports/submit";
return (
`script-src ${script_src} 'strict-dynamic'; ` +

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/monograph",
"version": "1.2.4",
"version": "1.2.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/monograph",
"version": "1.2.4",
"version": "1.2.5",
"dependencies": {
"@emotion/cache": "11.11.0",
"@emotion/react": "11.11.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/monograph",
"version": "1.2.4",
"version": "1.2.5",
"private": true,
"sideEffects": false,
"type": "module",

View File

@@ -430,12 +430,12 @@ test("control + alt + right arrow should go to next note", async ({ page }) => {
await note1?.openNote();
await note2?.openNote(true);
await page.keyboard.press("Control+Alt+ArrowRight");
await page.keyboard.press("ControlOrMeta+Alt+ArrowRight");
expect(await notes.editor.getTitle()).toBe("Note 1");
expect(await notes.editor.getContent("text")).toBe("Note 1 content");
await page.keyboard.press("Control+Alt+ArrowRight");
await page.keyboard.press("ControlOrMeta+Alt+ArrowRight");
expect(await notes.editor.getTitle()).toBe("Note 2");
expect(await notes.editor.getContent("text")).toBe("Note 2 content");
@@ -458,12 +458,12 @@ test("control + alt + left arrow should go to previous note", async ({
await note1?.openNote();
await note2?.openNote(true);
await page.keyboard.press("Control+Alt+ArrowLeft");
await page.keyboard.press("ControlOrMeta+Alt+ArrowLeft");
expect(await notes.editor.getTitle()).toBe("Note 1");
expect(await notes.editor.getContent("text")).toBe("Note 1 content");
await page.keyboard.press("Control+Alt+ArrowLeft");
await page.keyboard.press("ControlOrMeta+Alt+ArrowLeft");
expect(await notes.editor.getTitle()).toBe("Note 2");
expect(await notes.editor.getContent("text")).toBe("Note 2 content");

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "3.2.3",
"version": "3.2.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "3.2.3",
"version": "3.2.4",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

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

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 { useState, Suspense, useEffect, useRef } from "react";
import { Suspense, useEffect, useRef } from "react";
import { Box, Flex } from "@theme-ui/components";
import { ScopedThemeProvider } from "./components/theme-provider";
import useMobile from "./hooks/use-mobile";
@@ -224,7 +224,7 @@ function DesktopAppContents() {
initialSize={380}
style={{ flex: 1, display: "flex" }}
snapSize={120}
maxSize={500}
maxSize={1000}
className="list-pane"
>
<ScopedThemeProvider

View File

@@ -56,7 +56,7 @@ async function initializeDatabase(persistence: DatabasePersistence) {
);
await storage.migrate();
const multiTab = isFeatureSupported("opfs");
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
database.setup({
sqliteOptions: {
dialect: (name, init) =>

View File

@@ -45,7 +45,7 @@ class _DialogManager {
onClose: (result: any) => void;
}
>;
return new Promise<Result>((resolve, reject) => {
return new Promise<Result | false>((resolve, reject) => {
const close = () => {
this.openedDialogs.delete(component);
root.unmount();
@@ -53,7 +53,7 @@ class _DialogManager {
};
this.openedDialogs.set(component, () => {
close();
reject(new Error("Dialog force closed."));
resolve(false);
});
root.render(
<Dialog
@@ -72,11 +72,14 @@ class _DialogManager {
}
closeAll() {
this.openedDialogs.clear();
const dialogs = document.querySelectorAll(
".ReactModalPortal,[data-react-modal-body-trap]"
);
dialogs.forEach((elem) => elem.remove());
for (const close of this.openedDialogs.values()) {
close();
}
}
register<Props extends BaseDialogProps<any>>(

View File

@@ -75,7 +75,10 @@ const actions: Partial<
isCommandMode: false
}).catch(() => {});
},
openSettings: () => hashNavigate("/settings", { replace: true }),
openSettings: (e) => {
if (isInEditor(e)) return;
hashNavigate("/settings", { replace: true });
},
openKeyboardShortcuts: () => KeyboardShortcutsDialog.show({})
};

View File

@@ -243,7 +243,8 @@ const MemoizedEditorView = React.memo(EditorView, (prev, next) => {
prev.session.needsHydration === next.session.needsHydration &&
prev.session.activeBlockId === next.session.activeBlockId &&
prev.session.activeSearchResultIndex ===
next.session.activeSearchResultIndex
next.session.activeSearchResultIndex &&
prev.session.nonce === next.session.nonce
);
});
function EditorView({

View File

@@ -247,7 +247,7 @@ function errorToString(error: unknown) {
}
async function resetDatabase() {
const multiTab = isFeatureSupported("opfs");
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
await useKeyStore.getState().clear();
const dialect = createDialect({
name: "notesnook",

View File

@@ -42,6 +42,7 @@ import {
} from "../../stores/editor-store";
import { db } from "../../common/db";
import { useStore as useAppStore } from "../../stores/app-store";
import { useStore as useAttachmentStore } from "../../stores/attachment-store";
import { store as noteStore } from "../../stores/note-store";
import Toggle from "./toggle";
import ScrollContainer from "../scroll-container";
@@ -727,26 +728,27 @@ function Reminders({ noteId }: { noteId: string }) {
);
}
function Attachments({ noteId }: { noteId: string }) {
const nonce = useAttachmentStore((store) => store.nonce);
const result = usePromise(
() =>
db.attachments
.ofNote(noteId, "all")
.sorted({ sortBy: "dateCreated", sortDirection: "desc" }),
[noteId]
[noteId, nonce]
);
if (result.status !== "fulfilled" || result.value.length <= 0) return null;
return (
<Section
title={strings.dataTypesPluralCamelCase.attachment()}
sx={{ borderTop: "1px solid var(--border)" }}
sx={{ borderTop: "1px solid var(--border)", mt: 1 }}
>
<VirtualizedTable
estimatedSize={25}
getItemKey={(index) => result.value.key(index)}
items={result.value.placeholders}
style={{
marginTop: 5,
tableLayout: "fixed",
width: "100%"
}}

View File

@@ -156,7 +156,8 @@ function StatusBar() {
ml: 1,
alignItems: "center",
justifyContent: "center",
display: "flex"
display: "flex",
height: "100%"
}}
>
<Update
@@ -182,7 +183,8 @@ function StatusBar() {
sx={{
alignItems: "center",
justifyContent: "center",
display: "flex"
display: "flex",
height: "100%"
}}
data-test-id="vault-unlocked"
>

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Virtualizer } from "@tanstack/react-virtual";
import { Flex } from "@theme-ui/components";
import React, { useRef } from "react";
import React, { useLayoutEffect, useRef, useState } from "react";
import { TableVirtuoso } from "react-virtuoso";
export type VirtualizedTableRowProps<T, C> = {
@@ -57,6 +57,14 @@ export function VirtualizedTable<T, C>(props: VirtualizedTableProps<T, C>) {
style
} = props;
const containerRef = useRef<HTMLDivElement>(null);
const [scrollParent, setScrollParent] = useState(scrollElement);
useLayoutEffect(() => {
setScrollParent(
scrollElement ||
containerRef.current?.closest<HTMLDivElement>(".ms-container")
);
}, [scrollElement]);
return (
<Flex
@@ -67,11 +75,7 @@ export function VirtualizedTable<T, C>(props: VirtualizedTableProps<T, C>) {
<TableVirtuoso
data={items}
context={context}
customScrollParent={
scrollElement ||
containerRef.current?.closest(".ms-container") ||
undefined
}
customScrollParent={scrollParent || undefined}
increaseViewportBy={300}
computeItemKey={(index) => getItemKey(index)}
defaultItemHeight={estimatedSize}

View File

@@ -177,7 +177,7 @@ export function VirtualizedTree<T>(props: TreeViewProps<T>) {
const ids =
indices.length === nodes.length
? nodes.map((c) => c.id)
: (indices.map((i) => nodes[i].id).filter(Boolean) as string[]);
: (indices.map((i) => nodes[i]?.id).filter(Boolean) as string[]);
bulkSelect?.(ids);
},
focusItemAt: (index) => {

View File

@@ -140,7 +140,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
if (ev.origin !== PADDLE_ORIGIN) return;
logger.debug("Paddle event received", { data: ev.data });
const { event, event_name, callback_data } = ev.data;
const { checkout } = callback_data;
const { checkout } = callback_data || {};
if (event === PaddleEvents["Checkout.RemoveSpinner"]) setIsLoading(false);
@@ -165,7 +165,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
const pricingInfo = getPricingInfo(plan, {
public_checkout_id: checkout.id,
ip_geo_country_code: callback_data.user?.country || "US",
ip_geo_country_code: callback_data?.user?.country || "US",
items: [
{
prices: checkout.prices.customer.items,

View File

@@ -219,8 +219,11 @@ export function SubscriptionStatus() {
type: "modal",
title: strings.redeemingGiftCode(),
subtitle: strings.pleaseWait() + "...",
action: () => db.subscriptions.redeemCode(giftCode)
}).catch((e) => showToast("error", e.message));
action: () =>
db.subscriptions
.redeemCode(giftCode)
.catch((e) => showToast("error", e.message))
});
}
}}
sx={{ bg: "background" }}

View File

@@ -135,13 +135,19 @@ class AttachmentStore extends BaseStore<AttachmentStore> {
if (await db.attachments.remove(attachment.hash, false)) {
await this.get().refresh();
await useNoteStore.getState().refresh();
useEditorStore.getState().closeNotes(...linkedNotes);
for (const noteId of linkedNotes) {
await useEditorStore.getState().openSession(noteId, {
force: true,
silent: useEditorStore.getState().getActiveNote()?.id !== noteId
});
}
}
} catch (e) {
console.error(e);
this._changeWorkingStatus(attachment.hash);
if (e instanceof Error)
showToast("error", `${strings.failedToDelete()}: ${e.message}`);
} finally {
this._changeWorkingStatus(attachment.hash);
}
};

View File

@@ -88,6 +88,10 @@ export type BaseEditorSession = {
* The index of search result to scroll to after opening the session successfully.
*/
activeSearchResultIndex?: number;
/**
* Used for force refreshing a session.
*/
nonce?: number;
};
export type LockedEditorSession = BaseEditorSession & {
@@ -759,7 +763,7 @@ class EditorStore extends BaseStore<EditorStore> {
activeBlockId: options.activeBlockId,
tabId
},
options.silent
options
);
} else if (isLocked && note.type !== "trash") {
this.addSession(
@@ -770,7 +774,7 @@ class EditorStore extends BaseStore<EditorStore> {
activeBlockId: options.activeBlockId,
tabId
},
options.silent
options
);
} else {
const content = note.contentId
@@ -793,7 +797,7 @@ class EditorStore extends BaseStore<EditorStore> {
activeSearchResultIndex: options.activeSearchResultIndex,
tabId
},
options.silent
options
);
} else {
const attachmentsLength = await db.attachments
@@ -817,7 +821,7 @@ class EditorStore extends BaseStore<EditorStore> {
activeSearchResultIndex: options.activeSearchResultIndex,
tabId
},
options.silent
options
);
} else {
this.addSession(
@@ -838,7 +842,7 @@ class EditorStore extends BaseStore<EditorStore> {
activeSearchResultIndex: options.activeSearchResultIndex,
tabId
},
options.silent
options
);
}
}
@@ -934,15 +938,20 @@ class EditorStore extends BaseStore<EditorStore> {
}
};
addSession = (session: EditorSession, silent = false) => {
addSession = (
session: EditorSession,
options?: { force?: boolean; silent?: boolean }
) => {
this.set((state) => {
const index = state.sessions.findIndex((s) => s.id === session.id);
if (index > -1) {
if (options?.force)
session.nonce = (state.sessions[index]?.nonce || 0) + 1;
state.sessions[index] = session;
} else state.sessions.push(session);
});
this.activateSession(session.id, undefined, silent);
this.activateSession(session.id, undefined, options?.silent);
};
saveSession = async (

View File

@@ -27,7 +27,7 @@ import { isFeatureSupported } from "./feature-check";
let logger: typeof _logger = new NoopLogger();
async function initializeLogger() {
const multiTab = isFeatureSupported("opfs");
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
await initialize(
{
dialect: (name, init) =>

View File

@@ -30,8 +30,10 @@ function Reminders() {
useNavigate("reminders", () => store.refresh());
const reminders = useStore((state) => state.reminders);
const refresh = useStore((state) => state.refresh);
const filteredItems = useSearch("reminders", (query, sortOptions) =>
db.lookup.reminders(query).sorted(sortOptions)
const filteredItems = useSearch(
"reminders",
(query, sortOptions) => db.lookup.reminders(query).sorted(sortOptions),
[reminders]
);
if (!reminders) return <ListLoader />;

View File

@@ -0,0 +1,23 @@
---
title: Standard Notes
---
# How do I import notes from Standard Notes?
The following steps will help you import your notes from Standard notes easily.
1. Open Standard Notes app on Desktop or visit [https://app.standardnotes.org](https://app.standardnotes.org) and login to your account.
2. Select all your notes, right click, and select `Export`.
3. Open the Notesnook (app or desktop).
4. Go to `Settings > Notesnook Importer` and select `Standard Notes` from list of apps.
![](/static/standard-notes-importer/3.png)
5. Drop the .zip file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
![](/static/standard-notes-importer/4.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
## Supported formats
- [x] Text files
- [x] Authentication notes
- [x] Spreadsheets
- [ ] Tags

View File

@@ -22,7 +22,7 @@ The following keyboard shortcuts will help you navigate Notesnook faster.
| --- | --- | --- | --- |
| Next tab | Ctrl Alt → / Ctrl Alt ⇧ → | Ctrl tab | ⌘ tab |
| Previous tab | Ctrl Alt ← / Ctrl Alt ⇧ ← | Ctrl ⇧ tab | ⌘ ⇧ tab |
| Command palette | Ctrl K | Ctrl K | ⌘ K |
| Command palette | Ctrl ⇧ P | Ctrl ⇧ P | ⌘ ⇧ P |
| Quick open | Ctrl P | Ctrl P | ⌘ P |
| New tab | - | Ctrl T | ⌘ T |
| Close active tab | - | Ctrl W | ⌘ W |
@@ -60,8 +60,8 @@ The following keyboard shortcuts will help you navigate Notesnook faster.
| Add image | Ctrl ⇧ I | Ctrl ⇧ I | ⌘ ⇧ I |
| Toggle italic | Ctrl I | Ctrl I | ⌘ I |
| Remove formatting in selection | Ctrl \ | Ctrl \ | ⌘ \ |
| Insert internal link | Ctrl ⇧ L | Ctrl ⇧ L | ⌘ ⇧ L |
| Insert link | Ctrl K | Ctrl K | ⌘ K |
| Insert internal link | Ctrl ⇧ K | Ctrl ⇧ K | ⌘ ⇧ K |
| Insert link | Ctrl K | Ctrl K | ⌘ K |
| Insert math block | Ctrl ⇧ M | Ctrl ⇧ M | ⌘ ⇧ M |
| Toggle ordered list | Ctrl ⇧ 7 | Ctrl ⇧ 7 | ⌘ ⇧ 7 |
| Toggle outline list | Ctrl ⇧ O | Ctrl ⇧ O | ⌘ ⇧ O |

View File

@@ -0,0 +1,4 @@
- Fix crash on android devices upgraded to android 16
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,4 @@
- Add scroll to top/bottom in editor
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,3 @@
- Bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,5 @@
- Improved editor UI for better usability
- Improved UX for creating and editing reminders
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -96,7 +96,7 @@ export const hotkeys = {
type: "hotkeys"
},
openCommandPalette: {
keys: normalizeKeys(["ctrl+k"]),
keys: normalizeKeys(["ctrl+shift+p"]),
description: "Command palette",
category: "Navigation",
type: "hotkeys"
@@ -294,13 +294,13 @@ export const tiptapKeys = {
type: "tiptap"
},
insertInternalLink: {
keys: "Mod-Shift-L",
keys: "Mod-Shift-K",
description: "Insert internal link",
category: "Editor",
type: "tiptap"
},
insertLink: {
keys: "Mod-Shift-K",
keys: "Mod-k",
description: "Insert link",
category: "Editor",
type: "tiptap"
@@ -330,7 +330,7 @@ export const tiptapKeys = {
type: "tiptap"
},
openSearch: {
keys: "Mod-F",
keys: "Mod-f",
description: "Open search",
category: "Editor",
type: "tiptap"

View File

@@ -18,10 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { databaseTest, noteTest, TEST_NOTE } from "./utils/index.js";
import { test, expect } from "vitest";
import { test, expect, describe } from "vitest";
function tag(title: string) {
return { title };
function tag(title: string, dateCreated?: number) {
return { title, dateCreated };
}
function color(title: string) {
@@ -98,3 +98,50 @@ for (const type of ["tag", "color"] as const) {
);
}));
}
describe("sort tags by", () => {
const tags = ["apple", "mango", "melon", "orange", "zucchini"];
const sortTestCases = [
{
sortBy: "title",
sortDirection: "asc",
expectedOrder: ["apple", "mango", "melon", "orange", "zucchini"]
},
{
sortBy: "title",
sortDirection: "desc",
expectedOrder: ["zucchini", "orange", "melon", "mango", "apple"]
},
{
sortBy: "dateCreated",
sortDirection: "asc",
expectedOrder: ["apple", "mango", "melon", "orange", "zucchini"]
},
{
sortBy: "dateCreated",
sortDirection: "desc",
expectedOrder: ["zucchini", "orange", "melon", "mango", "apple"]
}
] as const;
for (const testCase of sortTestCases) {
test(`${testCase.sortBy} ${testCase.sortDirection}`, () =>
databaseTest().then(async (db) => {
let previousCreatedDate = Date.now();
for (const title of tags) {
await db.tags.add(tag(title, previousCreatedDate++));
}
const items = await db.tags.all.grouped({
sortBy: testCase.sortBy,
sortDirection: testCase.sortDirection,
groupBy: "default"
});
for (let i = 0; i < testCase.expectedOrder.length; i++) {
expect((await items.item(i)).item?.title).toEqual(
testCase.expectedOrder[i]
);
}
}));
}
});

View File

@@ -126,6 +126,16 @@ But this post was supposed to be about the "Secrets" of Typescript Generics, rig
"
`;
exports[`convert HTML to markdown with leadingWhitespaces > html-to-md-leadingWhitespaces.md 1`] = `
"two spaces
four spaces
two spaces
four spaces
"
`;
exports[`convert HTML to markdown with outlinelists > html-to-md-outlinelists.md 1`] = `
"Testing outline list:
@@ -389,6 +399,14 @@ right? Well, let's get into that.
4. TYPE INFERENCE USING INTERFACE PROPERTIES"
`;
exports[`convert HTML to text with leadingWhitespaces > html-to-txt-leadingWhitespaces.txt 1`] = `
"  two spaces
    four spaces
  two spaces
    four spaces"
`;
exports[`convert HTML to text with outlinelists > html-to-txt-outlinelists.txt 1`] = `
"Testing outline list:
* My outline list

View File

@@ -65,7 +65,8 @@ const HTMLS = {
tasklists: `<p>Hello</p><ul data-collapsed="false" class="checklist"><li class="checked checklist--item"><p>Task item 1</p></li><li class="checked checklist--item"><p>Task item 2</p></li><li class="checked checklist--item"><p>Task item 3</p></li><li class="checklist--item"><p>Task item 4</p><ul data-collapsed="false" class="checklist"><li class="checklist--item"><p>Sub task item 1</p></li><li class="checklist--item"><p>Sub task item 2</p></li></ul></li><li class="checklist--item"><p>Task Item 5</p></li></ul><p>Nene</p><ul><li><p>dasvsadv</p></li><li><p>adsva\`sd</p></li><li><p>vasd</p></li><li><p>vsadvdsa</p></li></ul>`,
outlinelists: `<p >Testing outline list:</p><ul data-collapsed="false" data-type="outlineList"><li data-type="outlineListItem"><p >My outline list</p></li><li data-type="outlineListItem"><p >works</p></li><li data-type="outlineListItem"><p >but sometimes</p><ul data-collapsed="false" data-type="outlineList"><li data-type="outlineListItem"><p >It doesn't</p></li><li data-type="outlineListItem"><p >what do I do?</p></li><li data-type="outlineListItem"><p >I need to do something!</p></li></ul></li><li data-type="outlineListItem"><p >Makes no sense!</p></li><li data-type="outlineListItem"><p >Yes it doesn't!</p></li></ul>`,
codeblock2: `<pre>hello<br></pre>`,
singleSpacedParagraphs: `<p data-spacing="single">hello world</p><p data-spacing="single">hello world 2</p>`
singleSpacedParagraphs: `<p data-spacing="single">hello world</p><p data-spacing="single">hello world 2</p>`,
leadingWhitespaces: `<p data-spacing="double"> two spaces</p><p data-spacing="double"> four spaces</p><p data-spacing="single"> two spaces</p><p data-spacing="single"> four spaces</p>`
};
for (const html in HTMLS) {

View File

@@ -413,6 +413,22 @@ function convertHtmlToTxt(html: string, wrap = true) {
builder.openBlock({
leadingLineBreaks: dataSpacing == "single" ? 1 : 2
});
// convert leading whitespace to NO-BREAK SPACE
if (elem.children && elem.children.length > 0) {
const firstChild = elem.children[0];
if (firstChild.type === "text" && firstChild.data) {
firstChild.data = firstChild.data.replace(
/^([ \t]+)/,
(match) => {
return match
.replace(/ /g, "\u00A0")
.replace(/\t/g, "\u00A0\u00A0\u00A0\u00A0");
}
);
}
}
walk(elem.children, builder);
builder.closeBlock({
trailingLineBreaks: 1

View File

@@ -609,10 +609,10 @@ export class FilteredSelector<T extends Item> {
sanitizeSortOptions(this.type, options);
const sortBy: Set<SortOptions["sortBy"]> = new Set();
sortBy.add(options.sortBy);
if (options.groupBy === "abc") sortBy.add("title");
else if (options.sortBy === "title" && options.groupBy !== "none")
sortBy.add("dateCreated");
sortBy.add(options.sortBy);
return <T>(
qb: SelectQueryBuilder<DatabaseSchema, keyof DatabaseSchema, T>

View File

@@ -22,5 +22,5 @@ import { TemplateData } from "./index.js";
export function buildText(data: TemplateData) {
return `${data.title}
${data.content}`;
${data.content}`;
}

View File

@@ -16,8 +16,8 @@
}
/* p { */
/* font-family: "Inter"; */
/* color: var(--nn_primary_paragraph) ## TODO: use fixed color */
/* font-family: "Inter"; */
/* color: var(--nn_primary_paragraph) ## TODO: use fixed color */
/* } */
::selection {
@@ -33,6 +33,10 @@
color: var(--nn_primary_placeholder) !important;
}
.ProseMirror > :first-child {
margin-top: 0.4em !important;
}
#root {
height: 100%;
display: flex;

View File

@@ -25,6 +25,7 @@ import {
toBlobURL,
usePermissionHandler
} from "@notesnook/editor";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import FingerprintIcon from "mdi-react/FingerprintIcon";
import {
@@ -47,7 +48,6 @@ import StatusBar from "./statusbar";
import Tags from "./tags";
import TiptapEditorWrapper from "./tiptap";
import Title from "./title";
import { strings } from "@notesnook/intl";
globalThis.toBlobURL = toBlobURL as typeof globalThis.toBlobURL;
@@ -380,11 +380,15 @@ const Tiptap = ({
const editor = editors[tab.id];
const firstChild = editor?.state.doc.firstChild;
const isParagraph = firstChild?.type.name === "paragraph";
const isFirstChildEmpty =
!firstChild?.textContent || firstChild?.textContent?.length === 0;
if (isParagraph && isFirstChildEmpty) {
const firstChildNodeType = editor?.state.doc.firstChild?.type.name;
const isSimpleNode =
firstChildNodeType !== "image" &&
firstChildNodeType !== "embed" &&
firstChildNodeType !== "attachment" &&
firstChildNodeType !== "mathBlock" &&
firstChildNodeType !== "horizontalRule" &&
firstChildNodeType !== "table";
if (isSimpleNode) {
editor?.commands.focus("end");
return;
}
@@ -405,11 +409,15 @@ const Tiptap = ({
const editor = editors[tab.id];
const docSize = editor?.state.doc.content.size;
if (!docSize) return;
const lastChild = editor?.state.doc.lastChild;
const isParagraph = lastChild?.type.name === "paragraph";
const isLastChildEmpty =
!lastChild?.textContent || lastChild?.textContent?.length === 0;
if (isParagraph && isLastChildEmpty) {
const lastChildNodeType = editor?.state.doc.lastChild?.type.name;
const isSimpleNode =
lastChildNodeType !== "image" &&
lastChildNodeType !== "embed" &&
lastChildNodeType !== "attachment" &&
lastChildNodeType !== "mathBlock" &&
lastChildNodeType !== "horizontalRule" &&
lastChildNodeType !== "table";
if (isSimpleNode) {
editor?.commands.focus("end");
return;
}
@@ -544,16 +552,35 @@ const Tiptap = ({
<div
onScroll={controller.scroll}
ref={containerRef}
id="editor-container-scroller"
style={{
overflowY: controller.loading ? "hidden" : "scroll",
height: "100%",
display: "block",
position: "relative"
display: "flex",
flexDirection: "column",
position: "relative",
paddingTop: "12px"
}}
>
{settings.noHeader || tab.session?.locked ? null : (
<>
<Tags settings={settings} loading={controller.loading} />
<div
style={{
flexDirection: "row",
justifyContent: "space-between",
display: "flex",
alignItems: "center",
padding: "0px 16px",
paddingBottom: "6px"
}}
>
<StatusBar
container={containerRef}
loading={controller.loading}
/>
<Tags settings={settings} loading={controller.loading} />
</div>
<Title
titlePlaceholder={controller.titlePlaceholder}
readonly={settings.readonly}
@@ -564,11 +591,6 @@ const Tiptap = ({
timeFormat={settings.timeFormat}
loading={controller.loading}
/>
<StatusBar
container={containerRef}
loading={controller.loading}
/>
</>
)}
@@ -865,9 +887,10 @@ const Tiptap = ({
}
}}
style={{
flexGrow: 1,
width: "100%",
minHeight: 300
display: "flex",
flex: 1,
minHeight: 100
}}
/>
</div>
@@ -907,7 +930,7 @@ const TiptapProvider = (): JSX.Element => {
editorContainer.classList.add("selectable", "main-editor", "searching");
editorContainer.style.flex = "1";
editorContainer.style.cursor = "text";
editorContainer.style.padding = "0px 12px";
editorContainer.style.padding = "0px 16px";
editorContainer.style.color = colors.primary.paragraph;
editorContainer.style.fontSize = `${settings.fontSize}px`;
editorContainer.style.fontFamily =

View File

@@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ControlledMenu, MenuItem as MenuItemInner } from "@szhsin/react-menu";
import ArrowBackIcon from "mdi-react/ArrowBackIcon";
import ArrowTopIcon from "mdi-react/ArrowTopIcon";
import ArrowDownIcon from "mdi-react/ArrowDownIcon";
import ArrowForwardIcon from "mdi-react/ArrowForwardIcon";
import ArrowULeftTopIcon from "mdi-react/ArrowULeftTopIcon";
import ArrowURightTopIcon from "mdi-react/ArrowURightTopIcon";
@@ -56,6 +58,43 @@ const MenuItem = (props: any) => (
<MenuItemInner {...props} className={menuItemClassName} />
);
const iconButtonStyle: React.CSSProperties = {
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
};
function IconButton({
onPress,
children,
fwdRef,
style = {},
preventDefault = false
}: {
onPress?: () => void;
children: React.ReactNode;
fwdRef?: any;
style?: React.CSSProperties;
preventDefault?: boolean;
}) {
return (
<Button
onPress={onPress}
fwdRef={fwdRef}
preventDefault={preventDefault}
style={{ ...iconButtonStyle, ...style }}
>
{children}
</Button>
);
}
const Button = ({
onPress,
children,
@@ -118,7 +157,8 @@ function Header({
backgroundColor: "var(--nn_primary_background)",
position: "sticky",
width: "100vw",
zIndex: 999
zIndex: 999,
borderBottom: "0.5px solid var(--nn_primary_border)"
}}
>
{noHeader ? null : (
@@ -130,51 +170,39 @@ function Header({
flexDirection: "row",
paddingTop: insets.top,
height: 50,
alignItems: "center"
alignItems: "center",
paddingLeft: 12,
paddingRight: 16
}}
id="header"
>
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
<div />
) : (
<Button
<IconButton
onPress={() => {
post(EditorEvents.back, undefined, tab.id, tab.session?.noteId);
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginLeft: 6,
width: 40,
height: 40,
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
position: "relative"
}}
style={{ width: 40, height: 40 }}
>
<ArrowBackIcon
size={28 * settings.fontScale}
style={{
position: "absolute"
}}
size={25 * settings.fontScale}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
</Button>
</IconButton>
)}
<div
style={{
display: "flex",
alignItems: "center",
flexDirection: "row"
flexDirection: "row",
gap: 8
}}
>
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
<Button
<IconButton
onPress={() => {
post(
EditorEvents.fullscreen,
@@ -183,32 +211,17 @@ function Header({
tab.session?.noteId
);
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 10,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<FullscreenIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
</Button>
</IconButton>
) : null}
{tab.session?.readonly ? (
<Button
<IconButton
onPress={() => {
post(
"editor-events:disable-readonly-mode",
@@ -216,46 +229,20 @@ function Header({
);
}}
fwdRef={btnRef}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_accent)",
marginRight: 12,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
style={{ color: "var(--nn_primary_accent)" }}
>
<PencilLockIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_accent)"
/>
</Button>
</IconButton>
) : null}
<Button
<IconButton
onPress={() => {
editor?.commands.undo();
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 10,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowULeftTopIcon
color={
@@ -264,28 +251,14 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
editor?.commands.redo();
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 10,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowURightTopIcon
color={
@@ -294,13 +267,11 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.showTabs,
@@ -309,26 +280,13 @@ function Header({
tab.session?.noteId
);
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 12,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<div
style={{
border: "2px solid var(--nn_primary_icon)",
width: 19 * settings.fontScale,
height: 19 * settings.fontScale,
minWidth: 19 * settings.fontScale,
width: 17 * settings.fontScale,
height: 17 * settings.fontScale,
minWidth: 17 * settings.fontScale,
borderRadius: 5,
display: "flex",
justifyContent: "center",
@@ -346,9 +304,9 @@ function Header({
{openedTabsCount}
</p>
</div>
</Button>
</IconButton>
<Button
<IconButton
fwdRef={btnRef}
onPress={() => {
if (tab.session?.locked) {
@@ -362,38 +320,21 @@ function Header({
setOpen(!isOpen);
}
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 12,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
{tab.session?.locked ? (
<DotsHorizontalIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
) : (
<DotsVerticalIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
)}
</Button>
</IconButton>
<ControlledMenu
align="end"
@@ -428,6 +369,30 @@ function Header({
tab.session?.noteId
);
break;
case "scroll-top":
{
const element = document.getElementById(
"editor-container-scroller"
);
element?.scrollTo({
top: 0,
left: 0,
behavior: "smooth"
});
}
break;
case "scroll-bottom":
{
const element = document.getElementById(
"editor-container-scroller"
);
element?.scrollTo({
top: element?.scrollHeight,
left: 0,
behavior: "smooth"
});
}
break;
case "properties":
post(
EditorEvents.properties,
@@ -444,7 +409,7 @@ function Header({
<div
style={{
display: "flex",
gap: 10,
gap: 8,
alignItems: "center",
flexDirection: "row",
justifyContent: "center",
@@ -452,7 +417,7 @@ function Header({
paddingTop: 5
}}
>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.goBack,
@@ -462,17 +427,6 @@ function Header({
);
setOpen(false);
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowBackIcon
color={
@@ -481,13 +435,11 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.goForward,
@@ -497,17 +449,6 @@ function Header({
);
setOpen(false);
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowForwardIcon
color={
@@ -516,37 +457,22 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
editor?.commands.startSearch();
setOpen(false);
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<MagnifyIcon
size={28 * settings.fontScale}
style={{
position: "absolute"
}}
size={25 * settings.fontScale}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
</Button>
</IconButton>
</div>
<MenuItem
@@ -558,7 +484,7 @@ function Header({
}}
>
<PlusIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -579,7 +505,7 @@ function Header({
}}
>
<TableOfContentsIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -590,6 +516,47 @@ function Header({
{strings.toc()}
</span>
</MenuItem>
<MenuItem
value="scroll-top"
style={{
display: "flex",
gap: 10,
alignItems: "center"
}}
>
<ArrowTopIcon
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
style={{
color: "var(--nn_primary_paragraph)"
}}
>
{strings.scrollToTop()}
</span>
</MenuItem>
<MenuItem
value="scroll-bottom"
style={{
display: "flex",
gap: 10,
alignItems: "center"
}}
>
<ArrowDownIcon
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
style={{
color: "var(--nn_primary_paragraph)"
}}
>
{strings.scrollToBottom()}
</span>
</MenuItem>
<MenuItem
value="properties"
style={{
@@ -599,7 +566,7 @@ function Header({
}}
>
<DotsHorizontalIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span

View File

@@ -29,19 +29,13 @@ function StatusBar({
container: RefObject<HTMLDivElement>;
loading?: boolean;
}) {
const [status, setStatus] = useState({
date: "",
saved: ""
});
const tab = useTabContext();
const [sticky, setSticky] = useState(false);
const stickyRef = useRef(false);
const prevScroll = useRef(0);
const lastStickyChangeTime = useRef(0);
const [showChars, setShowChars] = useState(false);
const [words, setWords] = useState(strings.totalWords(0));
const currentWords = useRef(words);
const [chars, setChars] = useState(0);
const statusBar = useRef({
set: setStatus,
set: () => {},
updateWords: () => {
const editor = editors[tab.id];
if (!editor) return;
@@ -57,109 +51,51 @@ function StatusBar({
useEffect(() => {
globalThis.statusBars[tab.id] = statusBar;
if (showChars) {
editors[tab.id]?.on("selectionUpdate", (event) => {
setChars(event.editor.extensionStorage.characterCount.characters());
});
}
if (editors[tab.id]) {
setChars(
editors[tab.id]?.extensionStorage.characterCount.characters() || 0
);
}
return () => {
globalThis.statusBars[tab.id] = undefined;
};
}, [tab.id, statusBar]);
const scrollState = useRef({
isMovingUp: false,
startingOffset: 0
});
const onScroll = React.useCallback((event: Event) => {
const currentOffset = (event.target as HTMLElement)?.scrollTop;
if (currentOffset < 200) {
if (stickyRef.current) {
stickyRef.current = false;
setSticky(false);
lastStickyChangeTime.current = Date.now();
prevScroll.current = currentOffset;
}
return;
}
if (Date.now() - lastStickyChangeTime.current < 300) return;
if (currentOffset > prevScroll.current) {
if (
!scrollState.current.startingOffset ||
scrollState.current.isMovingUp
) {
scrollState.current.startingOffset = currentOffset;
}
scrollState.current.isMovingUp = false;
} else {
if (
!scrollState.current.startingOffset ||
!scrollState.current.isMovingUp
) {
scrollState.current.startingOffset = currentOffset;
}
scrollState.current.isMovingUp = true;
}
if (scrollState.current.isMovingUp) {
if (currentOffset < scrollState.current.startingOffset - 50) {
if (!stickyRef.current) {
stickyRef.current = true;
setSticky(true);
}
scrollState.current.startingOffset = 0;
}
} else {
if (currentOffset > scrollState.current.startingOffset + 50) {
if (stickyRef.current) {
stickyRef.current = false;
setSticky(false);
}
scrollState.current.startingOffset = 0;
}
}
lastStickyChangeTime.current = Date.now();
prevScroll.current = currentOffset;
}, []);
}, [tab.id, statusBar, showChars]);
useEffect(() => {
currentWords.current = words;
}, [words]);
useEffect(() => {
const node = container.current;
node?.addEventListener("scroll", onScroll);
return () => {
node?.removeEventListener("scroll", onScroll);
};
}, [onScroll, container]);
const paragraphStyle: React.CSSProperties = {
marginTop: 0,
marginBottom: 0,
fontSize: "12px",
fontSize: 12,
color: "var(--nn_secondary_paragraph)",
marginRight: 8,
paddingBottom: 0,
userSelect: "none",
pointerEvents: "none"
userSelect: "none"
};
return (
<div
style={{
flexDirection: "row",
display: loading ? "none" : "flex",
paddingRight: 12,
paddingLeft: 12,
position: sticky ? "sticky" : "relative",
top: -3,
backgroundColor: "var(--nn_primary_background)",
zIndex: 10,
justifyContent: sticky ? "center" : "flex-start",
paddingTop: 4,
paddingBottom: 2
display: "flex",
height: "25px",
alignItems: "center"
}}
id="statusbar"
>
<p style={paragraphStyle}>{words}</p>
<p style={paragraphStyle}>{status.date}</p>
<p style={paragraphStyle}>{status.saved}</p>
<p
onMouseDown={(e) => {
setShowChars(!showChars);
}}
style={paragraphStyle}
>
{showChars ? strings.charactersCount(chars) : words}
</p>
</div>
);
}

View File

@@ -50,14 +50,15 @@
z-index: 999;
list-style: none;
user-select: none;
padding: 0px;
font-family: sans-serif;
font-size: 0.95em;
border: 1px solid var(--nn_primary_border);
box-shadow: 1px 1px 20px 1px rgba(0, 0, 0, 0.1);
border-radius: 6px;
border-radius: 8px;
background-color: var(--nn_secondary_background);
min-width: 12rem;
min-width: 13rem;
padding: 0 !important;
}
.menuOpening {
@@ -73,10 +74,11 @@
.menuItem {
cursor: pointer;
border-radius: 0;
padding: 1rem 0.6rem !important;
padding: 0.5rem 0.6rem !important;
color: var(--nn_primary_paragraph);
font-family: "Inter";
padding: 12px 6px;
font-size: 0.9em;
flex-wrap: nowrap;
}
.menuItemHover {

View File

@@ -17,14 +17,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { strings } from "@notesnook/intl";
import React, { useEffect, useRef, useState } from "react";
import { useTabContext } from "../hooks/useTabStore";
import { Settings } from "../utils";
import { EditorEvents } from "../utils/editor-events";
import styles from "./styles.module.css";
import { useTabContext } from "../hooks/useTabStore";
import { strings } from "@notesnook/intl";
function Tags(props: { settings: Settings; loading?: boolean }): JSX.Element {
function Tags(props: { settings: Settings; loading?: boolean }) {
const [tags, setTags] = useState<
{ title: string; alias: string; id: string; type: "tag" }[]
>([]);
@@ -50,87 +49,85 @@ function Tags(props: { settings: Settings; loading?: boolean }): JSX.Element {
};
const fontScale = props.settings?.fontScale || 1;
return (
return !tab.session?.noteId ? null : (
<div
className={styles.container}
style={{
padding: "0px 12px",
display: "flex",
alignItems: "center",
overflowX: "scroll",
minHeight: "40px",
opacity: props.loading ? 0 : 1
minHeight: "25px",
opacity: props.loading ? 0 : 1,
gap: 6
}}
>
<button
className={styles.btn}
onClick={(e) => {
e.preventDefault();
openManageTagsSheet();
}}
style={{
border: `1px solid var(--nn_primary_border)`,
backgroundColor: "var(--nn_secondary_background)",
marginRight: 5,
borderRadius: 100,
padding: "0px 10px",
fontFamily: "Inter",
display: "flex",
alignItems: "center",
height: "30px",
userSelect: "none",
WebkitUserSelect: "none"
}}
>
{tags.length === 0 ? (
{tags.length === 0 ? (
<button
className={styles.btn}
onClick={(e) => {
e.preventDefault();
openManageTagsSheet();
}}
style={{
border: `none`,
backgroundColor: "transparent",
borderRadius: 8,
padding: "0px 6px",
fontFamily: "Inter",
display: "flex",
alignItems: "center",
height: "24px",
userSelect: "none",
WebkitUserSelect: "none"
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width={18 * fontScale}
height={18 * fontScale}
viewBox={`0 0 24 24`}
>
<path
fill="var(--nn_primary_accent)"
d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"
/>
</svg>
<p
style={{
marginRight: 4,
fontSize: 13,
fontSize: 12,
marginLeft: 4,
color: "var(--nn_primary_icon)",
userSelect: "none"
}}
>
{strings.addATag()}
{strings.addTag()}
</p>
) : null}
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width={20 * fontScale}
height={20 * fontScale}
viewBox={`0 0 24 24`}
>
<path
fill="var(--nn_primary_accent)"
d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"
/>
</svg>
</button>
</button>
) : null}
{tags.map((tag) => (
{tags.slice(0, 2).map((tag, index) => (
<button
key={tag.title}
className={styles.btn}
style={{
border: "1px solid var(--nn_primary_border)",
backgroundColor: "var(--nn_secondary_background)",
marginRight: 5,
borderRadius: 100,
padding: "0px 10px",
height: "30px",
border: index !== 0 ? "none" : "1px solid var(--nn_primary_border)",
backgroundColor:
index !== 0 ? "transparent" : "var(--nn_secondary_background)",
borderRadius: 6,
padding: "0px 4px",
height: "24px",
fontFamily: "Inter",
fontSize: 13,
fontSize: 12,
color: "var(--nn_primary_icon)",
userSelect: "none",
WebkitUserSelect: "none"
}}
onClick={(e) => {
e.preventDefault();
post(EditorEvents.tag, tag, tab.id, tab.session?.noteId);
openManageTagsSheet();
}}
>
#{tag.alias}
{index > 0 ? `+${tags.length - 1}` : `#${tag.title}`}
</button>
))}
</div>

View File

@@ -43,7 +43,7 @@ export default function TiptapEditorWrapper(props: {
sx={{
display: props.settings.noToolbar ? "none" : "flex",
overflowY: "hidden",
minHeight: "50px",
minHeight: "40px",
...(globalThis.__PLATFORM__ === "ios" && {
position: "absolute",
bottom: 0,

View File

@@ -49,9 +49,9 @@ function Title({
if (!titleSizeDiv.current || !titleRef.current) return;
titleSizeDiv.current.innerText = titleRef.current.value;
titleRef.current.style.height = `${titleSizeDiv.current.clientHeight}px`;
titleRef.current.style.minHeight = `${titleSizeDiv.current.clientHeight}px`;
titleSizeDiv.current.style.width = `${titleRef.current.clientWidth}px`;
}, []);
useEffect(() => {
if (titleRef.current) {
titleRef.current.value = title;
@@ -88,8 +88,8 @@ function Title({
maxWidth: "100%",
minHeight: 40,
opacity: 0,
paddingRight: 10,
paddingLeft: 10,
paddingRight: 16,
paddingLeft: 16,
fontWeight: 600,
fontFamily: getFontById(fontFamily)?.font || "Inter",
boxSizing: "border-box",
@@ -119,8 +119,8 @@ function Title({
boxSizing: "border-box",
border: 0,
opacity: 1,
paddingRight: 10,
paddingLeft: 10,
paddingRight: 16,
paddingLeft: 16,
fontWeight: 600,
fontFamily: getFontById(fontFamily)?.font || "Inter",
backgroundColor: "transparent",

View File

@@ -32,6 +32,7 @@ type ResizerProps = {
height?: number;
handleColor?: SchemeColors;
onResize: (width: number, height: number) => void;
onResizeStop: (width: number, height: number) => void;
style?: React.CSSProperties;
};
export function Resizer(props: PropsWithChildren<ResizerProps>) {
@@ -39,6 +40,7 @@ export function Resizer(props: PropsWithChildren<ResizerProps>) {
enabled,
selected,
onResize,
onResizeStop,
width,
height,
children,
@@ -92,13 +94,20 @@ export function Resizer(props: PropsWithChildren<ResizerProps>) {
/>
)
}}
onResizeStop={(_e, _direction, ref) => {
onResize={(_e, _direction, ref) => {
try {
onResize(ref.clientWidth, ref.clientHeight);
} catch {
// ignore
}
}}
onResizeStop={(_e, _direction, ref) => {
try {
onResizeStop(ref.clientWidth, ref.clientHeight);
} catch {
// ignore
}
}}
onResizeStart={(e) => e.preventDefault()}
lockAspectRatio={true}
>

View File

@@ -26,7 +26,8 @@ import {
import {
MenuPresenter,
MenuPresenterProps,
PopupPresenter
PopupPresenter,
PopupPresenterProps
} from "@notesnook/ui";
import { getPopupContainer } from "../../toolbar/utils/dom.js";
@@ -51,7 +52,8 @@ export function MobileOnly(props: PropsWithChildren<unknown>) {
export type PopupType = "sheet" | "menu" | "none" | "popup";
export type ResponsivePresenterProps = MenuPresenterProps &
ActionSheetPresenterProps & {
ActionSheetPresenterProps &
PopupPresenterProps & {
mobile?: PopupType;
desktop?: PopupType;
};

View File

@@ -51,6 +51,10 @@ export function ImageComponent(
const [bloburl, setBloburl] = useState<string | undefined>(
toBlobURL("", "image", mime, hash)
);
const [resizing, setResizing] = useState<{
width: number;
height: number;
} | null>(null);
const controllerRef = useRef(new AbortController());
const isMobile = useIsMobile();
@@ -123,6 +127,10 @@ export function ImageComponent(
width={size.width}
height={size.height}
onResize={(width, height) => {
setResizing({ width, height });
}}
onResizeStop={(width, height) => {
setResizing(null);
editor.commands.setImageSize({ width, height });
}}
>
@@ -165,6 +173,26 @@ export function ImageComponent(
/>
</Flex>
)}
{Boolean(resizing) && (
<Box
sx={{
position: "absolute",
top: -30,
left: 0,
zIndex: 9999,
background: "var(--background-secondary)",
px: 2,
py: 1,
borderRadius: "default"
}}
>
<Text variant="subBody" sx={{ fontWeight: "bold" }}>
{resizing?.width}
{" × "}
{resizing?.height}
</Text>
</Box>
)}
</DesktopOnly>
{progress ? (
<Flex

View File

@@ -232,9 +232,10 @@ export const Link = Mark.create<LinkOptions>({
}
}),
markPasteRule({
find: (text) => {
find: (text, ev) => {
const foundLinks: PasteRuleMatch[] = [];
const html = ev?.clipboardData?.getData("text/html");
if (html && html.includes("<a")) return [];
if (text) {
const links = find(text).filter((item) => item.isLink);

View File

@@ -1,5 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`paste text > with html link 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" spellcheck="false" href="nn://note/68798972093c8c6ea22efca2">example.py</a></p></div></div>"`;
exports[`paste text > with markdown link 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" spellcheck="false" href="example.com">test</a></p></div></div>"`;
exports[`paste text > with multiple markdown links 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" spellcheck="false" href="example.com">test</a> some text <a target="_blank" rel="noopener noreferrer nofollow" spellcheck="false" href="example2.com">test2</a></p></div></div>"`;

View File

@@ -114,4 +114,33 @@ describe("paste text", () => {
expect(editorElement.outerHTML).toMatchSnapshot();
});
test("with html link", async () => {
const editorElement = h("div");
const { editor } = createEditor({
element: editorElement,
extensions: {
link: Link
}
});
const clipboardEvent = new Event("paste", {
bubbles: true,
cancelable: true,
composed: true
});
(clipboardEvent as unknown as any)["clipboardData"] = {
getData: (type: string) =>
type === "text/html"
? `<meta charset='utf-8'><html><head></head><body><a href="nn://note/68798972093c8c6ea22efca2">example.py</a></body></html>`
: ""
};
editor.view.dom.dispatchEvent(clipboardEvent);
await new Promise((resolve) => setTimeout(resolve, 100));
expect(editorElement.outerHTML).toMatchSnapshot();
});
});

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 { Node, mergeAttributes } from "@tiptap/core";
import { Node, mergeAttributes, nodePasteRule } from "@tiptap/core";
import { insertMathNode } from "./plugin/index.js";
import { NodeSelection } from "prosemirror-state";
import { tiptapKeys } from "@notesnook/common";
@@ -32,6 +32,8 @@ declare module "@tiptap/core" {
// simple inputrule for block math
const REGEX_BLOCK_MATH_DOLLARS = /\$\$\$\s+$/; //new RegExp("\$\$\s+$", "i");
const REGEX_PASTE_BLOCK_MATH_DOLLARS = /\$\$\$([\s\S]*?)\$\$\$/g;
export const MathBlock = Node.create({
name: "mathBlock",
group: "block math",
@@ -99,5 +101,20 @@ export const MathBlock = Node.create({
}
}
];
},
addPasteRules() {
return [
nodePasteRule({
find: REGEX_PASTE_BLOCK_MATH_DOLLARS,
type: this.type,
getAttributes: (match) => {
return { content: match[1] };
},
getContent: (attrs) => {
return attrs.content ? [{ type: "text", text: attrs.content }] : [];
}
})
];
}
});

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 { Node, mergeAttributes } from "@tiptap/core";
import { Node, mergeAttributes, nodePasteRule } from "@tiptap/core";
import { mathPlugin } from "./plugin/index.js";
declare module "@tiptap/core" {
@@ -40,6 +40,7 @@ const REGEX_INLINE_MATH_DOLLARS = /\$\$(.+)\$\$/; //new RegExp("\$(.+)\$", "i");
// return REGEX_INLINE_MATH_DOLLARS;
// }
// })();
const REGEX_PASTE_INLINE_MATH_DOLLARS = /\$\$([\s\S]*?)\$\$/g;
export const MathInline = Node.create({
name: "mathInline",
@@ -107,5 +108,20 @@ export const MathInline = Node.create({
}
}
];
},
addPasteRules() {
return [
nodePasteRule({
find: REGEX_PASTE_INLINE_MATH_DOLLARS,
type: this.type,
getAttributes: (match) => {
return { content: match[1] };
},
getContent: (attrs) => {
return attrs.content ? [{ type: "text", text: attrs.content }] : [];
}
})
];
}
});

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