mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 03:29:21 +02:00
Compare commits
73 Commits
mobile-fix
...
3.3.0-andr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ae93dec4c | ||
|
|
93ecc9b1ac | ||
|
|
b49be89b06 | ||
|
|
23e0748137 | ||
|
|
2f6d222cca | ||
|
|
ba1cfe05c4 | ||
|
|
11702c9209 | ||
|
|
239e301042 | ||
|
|
c097c4d457 | ||
|
|
28f1f276ce | ||
|
|
9470d2b61f | ||
|
|
95795a20c2 | ||
|
|
5e05d96487 | ||
|
|
18c9912068 | ||
|
|
cd85753252 | ||
|
|
bdb135036c | ||
|
|
5d70f58a0d | ||
|
|
b7d2d66e82 | ||
|
|
6b028ef850 | ||
|
|
8f85795e53 | ||
|
|
a0b7dff5fd | ||
|
|
352018c042 | ||
|
|
99e642780e | ||
|
|
fd48684396 | ||
|
|
9b25ab8708 | ||
|
|
c725ba8530 | ||
|
|
98287fbfc4 | ||
|
|
9f24bd2250 | ||
|
|
94f2a216ad | ||
|
|
78e3ee5fd2 | ||
|
|
1ddcf44eb0 | ||
|
|
5c5fb2ba2c | ||
|
|
aa4995d871 | ||
|
|
f818c41a02 | ||
|
|
3f9d954e2f | ||
|
|
f1fcd227e7 | ||
|
|
44d910121c | ||
|
|
1258a95aad | ||
|
|
1379193065 | ||
|
|
02960fb5a9 | ||
|
|
a7fa42e944 | ||
|
|
02ab8e3938 | ||
|
|
5c120676e1 | ||
|
|
3821dc29ef | ||
|
|
3c51f73427 | ||
|
|
8db373ee34 | ||
|
|
adcb73a2fc | ||
|
|
4c97b3481d | ||
|
|
4785544e18 | ||
|
|
7812c15b48 | ||
|
|
b841c6e0eb | ||
|
|
55b6be0f15 | ||
|
|
dfe6a73ac1 | ||
|
|
afcb8835d2 | ||
|
|
9dc9f186e1 | ||
|
|
875125cfe4 | ||
|
|
32d9cdd265 | ||
|
|
09deae9dbf | ||
|
|
337582ac70 | ||
|
|
820c78ce81 | ||
|
|
565e692af8 | ||
|
|
94e4721663 | ||
|
|
8157c0812a | ||
|
|
bc36837cd0 | ||
|
|
33540b000c | ||
|
|
8fce6b5eb6 | ||
|
|
aa7f14ae79 | ||
|
|
0ad63da4a2 | ||
|
|
2885b9ac3e | ||
|
|
a15ab99056 | ||
|
|
222cdbfa63 | ||
|
|
6cca06fcbc | ||
|
|
7f3c74b55b |
4
.github/workflows/desktop.tests.yml
vendored
4
.github/workflows/desktop.tests.yml
vendored
@@ -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"
|
||||
|
||||
|
||||
4
.github/workflows/monograph.publish.yml
vendored
4
.github/workflows/monograph.publish.yml
vendored
@@ -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
|
||||
|
||||
|
||||
4
apps/desktop/package-lock.json
generated
4
apps/desktop/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -35,7 +35,7 @@ export function hideAuth() {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
setTimeout(() => {
|
||||
Navigation.resetRootState();
|
||||
}, 300);
|
||||
}, 1000);
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ const Auth = ({ navigation, route }) => {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
setTimeout(() => {
|
||||
Navigation.resetRootState();
|
||||
}, 300);
|
||||
}, 1000);
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -95,8 +95,6 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
[context]
|
||||
);
|
||||
|
||||
console.log(data?.keyboardHandlerDisabled);
|
||||
|
||||
return !visible || !data ? null : (
|
||||
<SheetWrapper
|
||||
fwdRef={actionSheetRef}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -312,7 +312,6 @@ export const UserSheet = () => {
|
||||
icon: "logout",
|
||||
title: strings.logout(),
|
||||
onPress: async () => {
|
||||
console.log("logout");
|
||||
ref.current?.hide();
|
||||
await sleep(300);
|
||||
logoutUser();
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -64,8 +64,6 @@ const SheetWrapper = ({
|
||||
const isGestureNavigationEnabled =
|
||||
NotesnookModule.isGestureNavigationEnabled();
|
||||
|
||||
console.log(isGestureNavigationEnabled);
|
||||
|
||||
const style = React.useMemo(() => {
|
||||
return {
|
||||
width: largeTablet || smallTablet ? width : "100%",
|
||||
|
||||
@@ -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)} />
|
||||
),
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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={() => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -875,7 +875,6 @@ export const useEditor = (
|
||||
}
|
||||
|
||||
lastContentChangeTime.current[note.id] = note.dateEdited;
|
||||
console.log(tab.session?.selection);
|
||||
await postMessage(
|
||||
NativeEvents.updatehtml,
|
||||
{
|
||||
|
||||
@@ -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={() => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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!
|
||||
@@ -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)";
|
||||
|
||||
@@ -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
|
||||
|
||||
325
apps/mobile/package-lock.json
generated
325
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
39
apps/mobile/patches/@react-navigation+core+6.4.8.patch
Normal file
39
apps/mobile/patches/@react-navigation+core+6.4.8.patch
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'; ` +
|
||||
|
||||
4
apps/monograph/package-lock.json
generated
4
apps/monograph/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/monograph",
|
||||
"version": "1.2.4",
|
||||
"version": "1.2.5",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
|
||||
@@ -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");
|
||||
|
||||
4
apps/web/package-lock.json
generated
4
apps/web/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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/",
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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>>(
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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%"
|
||||
}}
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" }}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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.
|
||||

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

|
||||
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
|
||||
3
fastlane/metadata/android/en-US/changelogs/15339.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/15339.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
5
fastlane/metadata/android/en-US/changelogs/15344.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/15344.txt
Normal 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!
|
||||
@@ -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]
|
||||
);
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -22,5 +22,5 @@ import { TemplateData } from "./index.js";
|
||||
export function buildText(data: TemplateData) {
|
||||
return `${data.title}
|
||||
|
||||
${data.content}`;
|
||||
${data.content}`;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }] : [];
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 }] : [];
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ export function SearchReplaceFloatingMenu(props: FloatingMenuProps) {
|
||||
<ResponsivePresenter
|
||||
mobile="sheet"
|
||||
desktop="popup"
|
||||
scope="dialog"
|
||||
isOpen={isSearching}
|
||||
onClose={() => editor.commands.endSearch()}
|
||||
position={{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 };
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user