Compare commits

...

45 Commits

Author SHA1 Message Date
Ammar Ahmed
94f2a216ad intl: fix strings.ts 2025-09-16 10:11:43 +05:00
Ammar Ahmed
78e3ee5fd2 Merge pull request #8572 from streetwriters/fix-ipad-split-view
Fix keyboard in iPad split view
2025-09-16 10:10:05 +05:00
Ammar Ahmed
1ddcf44eb0 Merge pull request #8551 from streetwriters/mobile-editor-ui-update
mobile: editor ui overhaul
2025-09-16 10:08:28 +05:00
Ammar Ahmed
5c5fb2ba2c Merge branch 'master' into mobile-editor-ui-update
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2025-09-16 10:08:10 +05:00
Ammar Ahmed
aa4995d871 Merge pull request #8525 from streetwriters/mobile-reminder-fixes
Fix reminders on mobile
2025-09-16 10:06:54 +05:00
Ammar Ahmed
f818c41a02 Merge pull request #8524 from streetwriters/mobile-fix-add-notebook-btn
Fix add notebook button in Notebooks sheet
2025-09-16 10:06:30 +05:00
Ammar Ahmed
3f9d954e2f Merge pull request #8508 from streetwriters/mobile-fix-notebook-tree
Fix Notebook Tree when Searching for Notebooks
2025-09-16 10:06:05 +05:00
Ammar Ahmed
f1fcd227e7 Merge pull request #8535 from streetwriters/mobile-fix-reset-navigation
mobile: fix resetting navigation
2025-09-16 10:04:54 +05:00
Ammar Ahmed
44d910121c Merge pull request #8507 from streetwriters/mobile-fix-string-email
Fix email confirmed description
2025-09-16 10:03:40 +05:00
Abdullah Atta
1258a95aad Merge pull request #8562 from streetwriters/fix/refresh-note-on-attachment-delete
Refresh note on attachment permanent delete
2025-09-16 09:43:15 +05:00
Ammar Ahmed
1379193065 mobile: fix keyboard in ipad split view 2025-09-16 09:11:34 +05:00
Abdullah Atta
a7fa42e944 web: refresh note on attachment permanent delete 2025-09-13 13:07:02 +05:00
Ammar Ahmed
02ab8e3938 mobile: editor ui improvements 2025-09-12 10:57:18 +05:00
Abdullah Atta
5c120676e1 Merge pull request #8550 from streetwriters/release/web/v3.2.4
Release v3.2.4
2025-09-12 08:47:48 +05:00
Abdullah Atta
3821dc29ef web: fix some editor tests on macos 2025-09-12 08:28:39 +05:00
Abdullah Atta
3c51f73427 web: fix cannot destructure property checkout of He as it is undefined 2025-09-12 08:28:39 +05:00
Abdullah Atta
8db373ee34 web: bump version to 3.2.4 2025-09-12 08:28:39 +05:00
Abdullah Atta
adcb73a2fc Merge pull request #8388 from streetwriters/fix/theme-issues-2
Fix theme related issues
2025-09-12 08:27:44 +05:00
Ammar Ahmed
4c97b3481d mobile: editor ui overhaul 2025-09-11 15:49:00 +05:00
Abdullah Atta
4785544e18 Merge pull request #8549 from 01zulfi/web/fix-toc-crash
web: fix crash when shift+click on toc item
2025-09-11 13:48:33 +05:00
01zulfi
7812c15b48 web: fix crash when shift+click on toc item
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-11 09:39:00 +05:00
Abdullah Atta
b841c6e0eb Merge pull request #8395 from 01zulfi/editor/image-resizing-dimensions
editor: show image dimensions during drag resizing
2025-09-09 14:51:44 +05:00
Abdullah Atta
55b6be0f15 Merge pull request #8542 from 01zulfi/editor/improve-insert-link-dialog
editor: if selected text is link, autofill url field in insert link popup
2025-09-09 13:07:18 +05:00
Abdullah Atta
dfe6a73ac1 Merge pull request #8546 from streetwriters/release/monograph/1.2.5
Release monographs v1.2.5
2025-09-09 12:53:20 +05:00
Abdullah Atta
afcb8835d2 ci: do not build for armv7 & armv8 platforms 2025-09-09 12:49:33 +05:00
Abdullah Atta
9dc9f186e1 monograph: use oven/bun:1.2.21-slim 2025-09-09 12:45:15 +05:00
Ammar Ahmed
875125cfe4 mobile: clear logs 2025-09-09 12:34:06 +05:00
Abdullah Atta
32d9cdd265 monograph: bump version to 1.2.5 2025-09-09 12:00:28 +05:00
Abdullah Atta
09deae9dbf Merge pull request #8545 from streetwriters/fix-monograph-report-not-working
Add report endpoint to connect-src content policy
2025-09-09 11:59:02 +05:00
Abdullah Atta
337582ac70 monograph: add report endpoint to connect-src content policy 2025-09-09 11:58:29 +05:00
01zulfi
820c78ce81 editor: if selected text is link, autofill url field in insert link popup
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-08 11:28:16 +05:00
Ammar Ahmed
565e692af8 mobile: fix crash during navigation 2025-09-05 12:40:20 +05:00
Ammar Ahmed
94e4721663 mobile: fix resetting navigation 2025-09-05 12:11:39 +05:00
Ammar Ahmed
8157c0812a mobile: reminder fixes
1. Allow editing reminder without changing date
2. Fix colors of buttons
3. Allow picking date and time in date picker dialog
4. Fix auto focus of title and description inputs
2025-09-05 11:30:18 +05:00
Ammar Ahmed
bc36837cd0 mobile: add missing buttons in reminder sheet 2025-09-04 14:26:47 +05:00
Ammar Ahmed
33540b000c mobile: show reminder description in properties 2025-09-04 14:26:33 +05:00
Ammar Ahmed
5a63a45d00 mobile: fix add notebook button 2025-09-04 11:43:36 +05:00
01zulfi
8fce6b5eb6 editor: show image dimensions during drag resizing
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-09-02 10:18:36 +05:00
Abdullah Atta
4e44661e1b Merge pull request #8487 from 01zulfi/web/reminder-search-delete
web: fix deleting a reminder not removing from search list
2025-08-29 11:43:15 +05:00
Abdullah Atta
81fd1c097e Merge pull request #8509 from 01zulfi/editor/handle-empty-link-hrefs
editor: don't show open & copy tools for empty links
2025-08-29 11:39:39 +05:00
01zulfi
aaf82f359b editor: don't show open & copy tools for empty links
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-08-28 11:45:12 +05:00
Ammar Ahmed
0ad63da4a2 mobile: fix tree items in search 2025-08-25 14:51:56 +05:00
Ammar Ahmed
2885b9ac3e mobile: fix notebook tree when searching notebooks 2025-08-25 13:08:29 +05:00
01zulfi
a1d8b34fb4 web: fix deleting a reminder not removing from search list
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2025-08-20 09:02:44 +05:00
Abdullah Atta
7f3c74b55b editor: fix theme related issues 2025-07-23 08:32:35 +05:00
63 changed files with 720 additions and 625 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,8 +38,8 @@ import { eCloseSheet, eOnNotebookUpdated } from "../../../utils/events";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import { Properties } from "../../properties";
import SheetProvider from "../../sheet-provider";
import { NotebookItem } from "../../side-menu/notebook-item";
import { useSideMenuNotebookTreeStore } from "../../side-menu/stores";
import { IconButton } from "../../ui/icon-button";
@@ -151,7 +151,7 @@ export const Notebooks = (props: {
height: 400
}}
>
<SheetProvider context="local" />
<Dialog context="local" />
<View
style={{
@@ -175,7 +175,7 @@ export const Notebooks = (props: {
}}
name="plus"
onPress={() => {
AddNotebookSheet.present(props.rootNotebook, undefined, "local");
AddNotebookSheet.present(undefined, props.rootNotebook, "local");
}}
/>
</View>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { StackActions } from "@react-navigation/native";
import { NavigationHelpers, StackActions } from "@react-navigation/native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { useFavoriteStore } from "../stores/use-favorite-store";
import useNavigationStore, {
@@ -166,13 +166,25 @@ function closeDrawer() {
fluidTabsRef.current?.closeDrawer();
}
function resetRootState() {
const state = rootNavigatorRef.getState();
const focusedRouteKey = state.routes[state.index].key;
function resetRootState(
_state?: ReturnType<NavigationHelpers<any, any>["getState"]>
) {
const state = _state || rootNavigatorRef.getState();
const focusedRoute = state.routes[state.index];
if (state.routes.length < 2) return;
const routes = state.routes.filter(
(route) => route.name !== "Auth" && route.name !== "Welcome"
(route) =>
(route.name !== "Auth" && route.name !== "Welcome") ||
route.key === focusedRoute.key
);
const newIndex = routes.findIndex((route) => route.key === focusedRouteKey);
if (routes.length === state.routes.length) return;
if (routes.length === 0) {
routes.push(focusedRoute);
}
const newIndex = routes.findIndex((route) => route.key === focusedRoute.key);
rootNavigatorRef.reset({
...state,
routes: routes,

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
}
.main-editor *:first-child {
margin-top: 0.5em !important;
}
#root {
height: 100%;
display: flex;

View File

@@ -558,12 +558,29 @@ const Tiptap = ({
height: "100%",
display: "flex",
flexDirection: "column",
position: "relative"
position: "relative",
paddingTop: "12px"
}}
>
{settings.noHeader || tab.session?.locked ? null : (
<>
<Tags settings={settings} loading={controller.loading} />
<div
style={{
flexDirection: "row",
justifyContent: "space-between",
display: "flex",
alignItems: "center",
padding: "0px 16px",
paddingBottom: "6px"
}}
>
<StatusBar
container={containerRef}
loading={controller.loading}
/>
<Tags settings={settings} loading={controller.loading} />
</div>
<Title
titlePlaceholder={controller.titlePlaceholder}
readonly={settings.readonly}
@@ -574,11 +591,6 @@ const Tiptap = ({
timeFormat={settings.timeFormat}
loading={controller.loading}
/>
<StatusBar
container={containerRef}
loading={controller.loading}
/>
</>
)}
@@ -918,7 +930,7 @@ const TiptapProvider = (): JSX.Element => {
editorContainer.classList.add("selectable", "main-editor", "searching");
editorContainer.style.flex = "1";
editorContainer.style.cursor = "text";
editorContainer.style.padding = "0px 12px";
editorContainer.style.padding = "0px 16px";
editorContainer.style.color = colors.primary.paragraph;
editorContainer.style.fontSize = `${settings.fontSize}px`;
editorContainer.style.fontFamily =

View File

@@ -58,6 +58,43 @@ const MenuItem = (props: any) => (
<MenuItemInner {...props} className={menuItemClassName} />
);
const iconButtonStyle: React.CSSProperties = {
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
};
function IconButton({
onPress,
children,
fwdRef,
style = {},
preventDefault = false
}: {
onPress?: () => void;
children: React.ReactNode;
fwdRef?: any;
style?: React.CSSProperties;
preventDefault?: boolean;
}) {
return (
<Button
onPress={onPress}
fwdRef={fwdRef}
preventDefault={preventDefault}
style={{ ...iconButtonStyle, ...style }}
>
{children}
</Button>
);
}
const Button = ({
onPress,
children,
@@ -120,7 +157,8 @@ function Header({
backgroundColor: "var(--nn_primary_background)",
position: "sticky",
width: "100vw",
zIndex: 999
zIndex: 999,
borderBottom: "0.5px solid var(--nn_primary_border)"
}}
>
{noHeader ? null : (
@@ -132,51 +170,39 @@ function Header({
flexDirection: "row",
paddingTop: insets.top,
height: 50,
alignItems: "center"
alignItems: "center",
paddingLeft: 12,
paddingRight: 16
}}
id="header"
>
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
<div />
) : (
<Button
<IconButton
onPress={() => {
post(EditorEvents.back, undefined, tab.id, tab.session?.noteId);
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginLeft: 6,
width: 40,
height: 40,
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
position: "relative"
}}
style={{ width: 40, height: 40 }}
>
<ArrowBackIcon
size={28 * settings.fontScale}
style={{
position: "absolute"
}}
size={25 * settings.fontScale}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
</Button>
</IconButton>
)}
<div
style={{
display: "flex",
alignItems: "center",
flexDirection: "row"
flexDirection: "row",
gap: 8
}}
>
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
<Button
<IconButton
onPress={() => {
post(
EditorEvents.fullscreen,
@@ -185,32 +211,17 @@ function Header({
tab.session?.noteId
);
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 10,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<FullscreenIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
</Button>
</IconButton>
) : null}
{tab.session?.readonly ? (
<Button
<IconButton
onPress={() => {
post(
"editor-events:disable-readonly-mode",
@@ -218,46 +229,20 @@ function Header({
);
}}
fwdRef={btnRef}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_accent)",
marginRight: 12,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
style={{ color: "var(--nn_primary_accent)" }}
>
<PencilLockIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_accent)"
/>
</Button>
</IconButton>
) : null}
<Button
<IconButton
onPress={() => {
editor?.commands.undo();
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 10,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowULeftTopIcon
color={
@@ -266,28 +251,14 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
editor?.commands.redo();
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 10,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowURightTopIcon
color={
@@ -296,13 +267,11 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.showTabs,
@@ -311,26 +280,13 @@ function Header({
tab.session?.noteId
);
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 12,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<div
style={{
border: "2px solid var(--nn_primary_icon)",
width: 19 * settings.fontScale,
height: 19 * settings.fontScale,
minWidth: 19 * settings.fontScale,
width: 17 * settings.fontScale,
height: 17 * settings.fontScale,
minWidth: 17 * settings.fontScale,
borderRadius: 5,
display: "flex",
justifyContent: "center",
@@ -348,9 +304,9 @@ function Header({
{openedTabsCount}
</p>
</div>
</Button>
</IconButton>
<Button
<IconButton
fwdRef={btnRef}
onPress={() => {
if (tab.session?.locked) {
@@ -364,38 +320,21 @@ function Header({
setOpen(!isOpen);
}
}}
preventDefault={false}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
marginRight: 12,
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
{tab.session?.locked ? (
<DotsHorizontalIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
) : (
<DotsVerticalIcon
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
)}
</Button>
</IconButton>
<ControlledMenu
align="end"
@@ -470,7 +409,7 @@ function Header({
<div
style={{
display: "flex",
gap: 10,
gap: 8,
alignItems: "center",
flexDirection: "row",
justifyContent: "center",
@@ -478,7 +417,7 @@ function Header({
paddingTop: 5
}}
>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.goBack,
@@ -488,17 +427,6 @@ function Header({
);
setOpen(false);
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowBackIcon
color={
@@ -507,13 +435,11 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
post(
EditorEvents.goForward,
@@ -523,17 +449,6 @@ function Header({
);
setOpen(false);
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<ArrowForwardIcon
color={
@@ -542,37 +457,22 @@ function Header({
: "var(--nn_primary_icon)"
}
size={25 * settings.fontScale}
style={{
position: "absolute"
}}
style={{ position: "absolute" }}
/>
</Button>
</IconButton>
<Button
<IconButton
onPress={() => {
editor?.commands.startSearch();
setOpen(false);
}}
style={{
borderWidth: 0,
borderRadius: 100,
color: "var(--nn_primary_icon)",
width: 39,
height: 39,
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
>
<MagnifyIcon
size={28 * settings.fontScale}
style={{
position: "absolute"
}}
size={25 * settings.fontScale}
style={{ position: "absolute" }}
color="var(--nn_primary_icon)"
/>
</Button>
</IconButton>
</div>
<MenuItem
@@ -584,7 +484,7 @@ function Header({
}}
>
<PlusIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -605,7 +505,7 @@ function Header({
}}
>
<TableOfContentsIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -625,7 +525,7 @@ function Header({
}}
>
<ArrowTopIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -646,7 +546,7 @@ function Header({
}}
>
<ArrowDownIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
@@ -666,7 +566,7 @@ function Header({
}}
>
<DotsHorizontalIcon
size={22 * settings.fontScale}
size={20 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span

View File

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

View File

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

View File

@@ -17,13 +17,12 @@ 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 {
const [tags, setTags] = useState<
{ title: string; alias: string; id: string; type: "tag" }[]
@@ -54,83 +53,83 @@ function Tags(props: { settings: Settings; loading?: boolean }): JSX.Element {
<div
className={styles.container}
style={{
padding: "0px 12px",
display: "flex",
display: tags.length === 0 ? "none" : "flex",
position: tags.length === 0 ? "absolute" : "relative",
alignItems: "center",
overflowX: "scroll",
minHeight: "40px",
opacity: props.loading ? 0 : 1
minHeight: "25px",
opacity: props.loading ? 0 : 1,
gap: 6
}}
>
<button
className={styles.btn}
onClick={(e) => {
e.preventDefault();
openManageTagsSheet();
}}
style={{
border: `1px solid var(--nn_primary_border)`,
backgroundColor: "var(--nn_secondary_background)",
marginRight: 5,
borderRadius: 100,
padding: "0px 10px",
fontFamily: "Inter",
display: "flex",
alignItems: "center",
height: "30px",
userSelect: "none",
WebkitUserSelect: "none"
}}
>
{tags.length === 0 ? (
{tags.length === 0 ? (
<button
className={styles.btn}
onClick={(e) => {
e.preventDefault();
openManageTagsSheet();
}}
style={{
border: `none`,
backgroundColor: "transparent",
borderRadius: 8,
padding: "0px 6px",
fontFamily: "Inter",
display: "flex",
alignItems: "center",
height: "24px",
userSelect: "none",
WebkitUserSelect: "none"
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width={18 * fontScale}
height={18 * fontScale}
viewBox={`0 0 24 24`}
>
<path
fill="var(--nn_primary_accent)"
d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"
/>
</svg>
<p
style={{
marginRight: 4,
fontSize: 13,
fontSize: 12,
marginLeft: 4,
color: "var(--nn_primary_icon)",
userSelect: "none"
}}
>
{strings.addATag()}
{strings.addTag()}
</p>
) : null}
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width={20 * fontScale}
height={20 * fontScale}
viewBox={`0 0 24 24`}
>
<path
fill="var(--nn_primary_accent)"
d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"
/>
</svg>
</button>
</button>
) : null}
{tags.map((tag) => (
{tags.slice(0, 2).map((tag, index) => (
<button
key={tag.title}
className={styles.btn}
style={{
border: "1px solid var(--nn_primary_border)",
backgroundColor: "var(--nn_secondary_background)",
marginRight: 5,
borderRadius: 100,
padding: "0px 10px",
height: "30px",
border: index !== 0 ? "none" : "1px solid var(--nn_primary_border)",
backgroundColor:
index !== 0 ? "transparent" : "var(--nn_secondary_background)",
borderRadius: 6,
padding: "0px 4px",
height: "24px",
fontFamily: "Inter",
fontSize: 13,
fontSize: 12,
color: "var(--nn_primary_icon)",
userSelect: "none",
WebkitUserSelect: "none"
}}
onClick={(e) => {
e.preventDefault();
post(EditorEvents.tag, tag, tab.id, tab.session?.noteId);
openManageTagsSheet();
}}
>
#{tag.alias}
{index > 0 ? `+${tags.length - 1}` : `#${tag.title}`}
</button>
))}
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,7 @@ import { Link as LinkNode } from "../../extensions/link/index.js";
import { getMarkAttributes } from "@tiptap/core";
import { useHoverPopupContext } from "../floating-menus/hover-popup/context.js";
import { strings } from "@notesnook/intl";
import { find } from "linkifyjs";
export function LinkSettings(props: ToolProps) {
const { editor } = props;
@@ -69,7 +70,11 @@ export function AddLink(props: ToolProps) {
editor.state.selection.from,
editor.state.selection.to
);
return { title: selectedText, href: "" };
const href =
find(selectedText).find(
(item) => item.isLink && item.href === selectedText
)?.href ?? "";
return { title: selectedText, href };
}}
/>
);
@@ -196,6 +201,7 @@ export function OpenLink(props: ToolProps) {
const link = node ? findMark(node, "link") : null;
if (!link) return null;
const href = link?.attrs.href;
if (!href) return null;
return (
<Flex sx={{ alignItems: "center" }}>
@@ -245,6 +251,7 @@ export function CopyLink(props: ToolProps) {
const link = node ? findMark(node, "link") : null;
if (!link) return null;
const href = link?.attrs.href;
if (!href) return null;
return (
<ToolButton

View File

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

View File

@@ -491,6 +491,10 @@ msgstr "{count, plural, one {Unpublish item} other {Unpublish # items}}"
msgid "{count, plural, one {Unpublish note} other {Unpublish # notes}}"
msgstr "{count, plural, one {Unpublish note} other {Unpublish # notes}}"
#: src/strings.ts:2492
msgid "{count} characters"
msgstr "{count} characters"
#: src/strings.ts:1563
msgid "{days, plural, one {1 day} other {# days}}"
msgstr "{days, plural, one {1 day} other {# days}}"

View File

@@ -491,6 +491,10 @@ msgstr ""
msgid "{count, plural, one {Unpublish note} other {Unpublish # notes}}"
msgstr ""
#: src/strings.ts:2492
msgid "{count} characters"
msgstr ""
#: src/strings.ts:1563
msgid "{days, plural, one {1 day} other {# days}}"
msgstr ""

View File

@@ -2490,5 +2490,6 @@ Use this if changes from other devices are not appearing on this device. This wi
scrollToTop: () => t`Scroll to top`,
scrollToBottom: () => t`Scroll to bottom`,
emailConfirmedDesc: () =>
t`Your email has been confirmed. You can now securely sync your encrypted notes across all devices.`
t`Your email has been confirmed. You can now securely sync your encrypted notes across all devices.`,
charactersCount: (count: number) => t`${count} characters`
};