mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 11:08:47 +02:00
Compare commits
45 Commits
mobile-fix
...
intl-fix-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94f2a216ad | ||
|
|
78e3ee5fd2 | ||
|
|
1ddcf44eb0 | ||
|
|
5c5fb2ba2c | ||
|
|
aa4995d871 | ||
|
|
f818c41a02 | ||
|
|
3f9d954e2f | ||
|
|
f1fcd227e7 | ||
|
|
44d910121c | ||
|
|
1258a95aad | ||
|
|
1379193065 | ||
|
|
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 | ||
|
|
5a63a45d00 | ||
|
|
8fce6b5eb6 | ||
|
|
4e44661e1b | ||
|
|
81fd1c097e | ||
|
|
aaf82f359b | ||
|
|
0ad63da4a2 | ||
|
|
2885b9ac3e | ||
|
|
a1d8b34fb4 | ||
|
|
7f3c74b55b |
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",
|
||||
|
||||
@@ -33,9 +33,6 @@ export function hideAuth() {
|
||||
eSendEvent(eCloseLoginDialog);
|
||||
if (initialAuthMode.current === AuthMode.welcomeSignup) {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
setTimeout(() => {
|
||||
Navigation.resetRootState();
|
||||
}, 300);
|
||||
} else {
|
||||
Navigation.goBack();
|
||||
}
|
||||
|
||||
@@ -89,9 +89,6 @@ const Auth = ({ navigation, route }) => {
|
||||
onPress={() => {
|
||||
if (initialAuthMode.current === 2) {
|
||||
Navigation.replace("FluidPanelsView");
|
||||
setTimeout(() => {
|
||||
Navigation.resetRootState();
|
||||
}, 300);
|
||||
} 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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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%",
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
325
apps/mobile/package-lock.json
generated
325
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
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/",
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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%"
|
||||
}}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 />;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.characters(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,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -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
|
||||
|
||||
@@ -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}}"
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user