Compare commits

..

73 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
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
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
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
Abdullah Atta
7f3c74b55b editor: fix theme related issues 2025-07-23 08:32:35 +05:00
87 changed files with 925 additions and 676 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

@@ -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

@@ -35,7 +35,7 @@ export function hideAuth() {
Navigation.replace("FluidPanelsView");
setTimeout(() => {
Navigation.resetRootState();
}, 300);
}, 1000);
} else {
Navigation.goBack();
}

View File

@@ -91,7 +91,7 @@ const Auth = ({ navigation, route }) => {
Navigation.replace("FluidPanelsView");
setTimeout(() => {
Navigation.resetRootState();
}, 300);
}, 1000);
} else {
Navigation.goBack();
}

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

@@ -41,7 +41,6 @@ import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import { Properties } from "../../properties";
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]);
@@ -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,6 +66,9 @@ export const SideMenuNotebooks = () => {
const updateNotebooks = React.useCallback(() => {
if (lastQuery.current) {
// useSideMenuNotebookTreeStore.setState({
// isSearching: true
// });
db.lookup
.notebooks(lastQuery.current)
.sorted(db.settings.getGroupOptions("notebooks"))
@@ -71,6 +76,9 @@ export const SideMenuNotebooks = () => {
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

@@ -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

@@ -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,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,13 +166,25 @@ function closeDrawer() {
fluidTabsRef.current?.closeDrawer();
}
function resetRootState() {
const state = rootNavigatorRef.getState();
const focusedRouteKey = state.routes[state.index].key;
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) =>
(route.name !== "Auth" && route.name !== "Welcome") ||
route.key === focusedRoute.key
);
const newIndex = routes.findIndex((route) => route.key === focusedRouteKey);
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,

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 3066
versionCode 3068
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -1,4 +1,5 @@
- Add scroll to top/bottom in editor
- 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

@@ -1089,7 +1089,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2145;
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.10;
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 = 2145;
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.10;
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 = 2145;
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.10;
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 = 2145;
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.10;
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 = 2145;
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.10;
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 = 2145;
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.10;
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.2.10",
"version": "3.2.12",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -56,4 +56,4 @@
"react": "18.2.0",
"react-native": "0.77.2"
}
}
}

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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -558,12 +558,29 @@ const Tiptap = ({
height: "100%",
display: "flex",
flexDirection: "column",
position: "relative"
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}
@@ -574,11 +591,6 @@ const Tiptap = ({
timeFormat={settings.timeFormat}
loading={controller.loading}
/>
<StatusBar
container={containerRef}
loading={controller.loading}
/>
</>
)}
@@ -918,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

@@ -58,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,
@@ -120,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 : (
@@ -132,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,
@@ -185,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",
@@ -218,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={
@@ -266,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={
@@ -296,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,
@@ -311,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",
@@ -348,9 +304,9 @@ function Header({
{openedTabsCount}
</p>
</div>
</Button>
</IconButton>
<Button
<IconButton
fwdRef={btnRef}
onPress={() => {
if (tab.session?.locked) {
@@ -364,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"
@@ -470,7 +409,7 @@ function Header({
<div
style={{
display: "flex",
gap: 10,
gap: 8,
alignItems: "center",
flexDirection: "row",
justifyContent: "center",
@@ -478,7 +417,7 @@ function Header({
paddingTop: 5
}}
>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.goBack,
@@ -488,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={
@@ -507,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,
@@ -523,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={
@@ -542,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
@@ -584,7 +484,7 @@ function Header({
}}
>
<PlusIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -605,7 +505,7 @@ function Header({
}}
>
<TableOfContentsIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -625,7 +525,7 @@ function Header({
}}
>
<ArrowTopIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -646,7 +546,7 @@ function Header({
}}
>
<ArrowDownIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -666,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

@@ -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 }] : [];
}
})
];
}
});

View File

@@ -38,6 +38,7 @@ export function SearchReplaceFloatingMenu(props: FloatingMenuProps) {
<ResponsivePresenter
mobile="sheet"
desktop="popup"
scope="dialog"
isOpen={isSearching}
onClose={() => editor.commands.endSearch()}
position={{

View File

@@ -125,7 +125,7 @@ export function TablePopup(props: TablePopupProps) {
bg: "background-selected",
borderColor: "transparent"
}
: { bg: "transparent", borderColor: "border" })
: { bg: "background-secondary", borderColor: "border" })
}}
onTouchStart={() => {
setCellLocation(getCellLocation(index, tableSize));

View File

@@ -32,6 +32,7 @@ import { Link as LinkNode } from "../../extensions/link/index.js";
import { getMarkAttributes } from "@tiptap/core";
import { useHoverPopupContext } from "../floating-menus/hover-popup/context.js";
import { strings } from "@notesnook/intl";
import { find } from "linkifyjs";
export function LinkSettings(props: ToolProps) {
const { editor } = props;
@@ -69,7 +70,11 @@ export function AddLink(props: ToolProps) {
editor.state.selection.from,
editor.state.selection.to
);
return { title: selectedText, href: "" };
const href =
find(selectedText).find(
(item) => item.isLink && item.href === selectedText
)?.href ?? "";
return { title: selectedText, href };
}}
/>
);

View File

@@ -42,7 +42,7 @@
margin-block: 0px;
cursor: pointer;
position: relative;
background-color: var(--border);
background-color: var(--separator);
width: 100%;
height: 5px;
margin-top: 4px;

View File

@@ -491,6 +491,10 @@ msgstr "{count, plural, one {Unpublish item} other {Unpublish # items}}"
msgid "{count, plural, one {Unpublish note} other {Unpublish # notes}}"
msgstr "{count, plural, one {Unpublish note} other {Unpublish # notes}}"
#: src/strings.ts:2492
msgid "{count} characters"
msgstr "{count} characters"
#: src/strings.ts:1563
msgid "{days, plural, one {1 day} other {# days}}"
msgstr "{days, plural, one {1 day} other {# days}}"
@@ -7115,6 +7119,10 @@ msgstr "Your data recovery key will be used to decrypt your data"
msgid "Your email has been confirmed."
msgstr "Your email has been confirmed."
#: src/strings.ts:2493
msgid "Your email has been confirmed. You can now securely sync your encrypted notes across all devices."
msgstr "Your email has been confirmed. You can now securely sync your encrypted notes across all devices."
#: src/strings.ts:765
msgid "Your email is not confirmed. Please confirm your email address to change account password."
msgstr "Your email is not confirmed. Please confirm your email address to change account password."

View File

@@ -491,6 +491,10 @@ msgstr ""
msgid "{count, plural, one {Unpublish note} other {Unpublish # notes}}"
msgstr ""
#: src/strings.ts:2492
msgid "{count} characters"
msgstr ""
#: src/strings.ts:1563
msgid "{days, plural, one {1 day} other {# days}}"
msgstr ""
@@ -7061,6 +7065,10 @@ msgstr ""
msgid "Your email has been confirmed."
msgstr ""
#: src/strings.ts:2493
msgid "Your email has been confirmed. You can now securely sync your encrypted notes across all devices."
msgstr ""
#: src/strings.ts:765
msgid "Your email is not confirmed. Please confirm your email address to change account password."
msgstr ""

View File

@@ -2488,5 +2488,8 @@ Use this if changes from other devices are not appearing on this device. This wi
t`Select a notebook to move this notebook into, or unselect to move it to the root level.`,
noNotebooksSelectedToMove: () => t`No notebooks selected to move`,
scrollToTop: () => t`Scroll to top`,
scrollToBottom: () => t`Scroll to bottom`
scrollToBottom: () => t`Scroll to bottom`,
emailConfirmedDesc: () =>
t`Your email has been confirmed. You can now securely sync your encrypted notes across all devices.`,
charactersCount: (count: number) => t`${count} characters`
};