Merge branch 'mobile-editor-ux-improvement' into beta

This commit is contained in:
Ammar Ahmed
2025-07-26 10:53:01 +05:00
7 changed files with 140 additions and 22 deletions

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ import {
toBlobURL,
usePermissionHandler
} from "@notesnook/editor";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import FingerprintIcon from "mdi-react/FingerprintIcon";
import {
@@ -47,7 +48,6 @@ import StatusBar from "./statusbar";
import Tags from "./tags";
import TiptapEditorWrapper from "./tiptap";
import Title from "./title";
import { strings } from "@notesnook/intl";
globalThis.toBlobURL = toBlobURL as typeof globalThis.toBlobURL;
@@ -380,11 +380,15 @@ const Tiptap = ({
const editor = editors[tab.id];
const firstChild = editor?.state.doc.firstChild;
const isParagraph = firstChild?.type.name === "paragraph";
const isFirstChildEmpty =
!firstChild?.textContent || firstChild?.textContent?.length === 0;
if (isParagraph && isFirstChildEmpty) {
const firstChildNodeType = editor?.state.doc.firstChild?.type.name;
const isSimpleNode =
firstChildNodeType !== "image" &&
firstChildNodeType !== "embed" &&
firstChildNodeType !== "attachment" &&
firstChildNodeType !== "mathBlock" &&
firstChildNodeType !== "horizontalRule" &&
firstChildNodeType !== "table";
if (isSimpleNode) {
editor?.commands.focus("end");
return;
}
@@ -405,11 +409,15 @@ const Tiptap = ({
const editor = editors[tab.id];
const docSize = editor?.state.doc.content.size;
if (!docSize) return;
const lastChild = editor?.state.doc.lastChild;
const isParagraph = lastChild?.type.name === "paragraph";
const isLastChildEmpty =
!lastChild?.textContent || lastChild?.textContent?.length === 0;
if (isParagraph && isLastChildEmpty) {
const lastChildNodeType = editor?.state.doc.lastChild?.type.name;
const isSimpleNode =
lastChildNodeType !== "image" &&
lastChildNodeType !== "embed" &&
lastChildNodeType !== "attachment" &&
lastChildNodeType !== "mathBlock" &&
lastChildNodeType !== "horizontalRule" &&
lastChildNodeType !== "table";
if (isSimpleNode) {
editor?.commands.focus("end");
return;
}
@@ -544,10 +552,12 @@ const Tiptap = ({
<div
onScroll={controller.scroll}
ref={containerRef}
id="editor-container-scroller"
style={{
overflowY: controller.loading ? "hidden" : "scroll",
height: "100%",
display: "block",
display: "flex",
flexDirection: "column",
position: "relative"
}}
>
@@ -865,9 +875,10 @@ const Tiptap = ({
}
}}
style={{
flexGrow: 1,
width: "100%",
minHeight: 300
display: "flex",
flex: 1,
minHeight: 100
}}
/>
</div>

View File

@@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ControlledMenu, MenuItem as MenuItemInner } from "@szhsin/react-menu";
import ArrowBackIcon from "mdi-react/ArrowBackIcon";
import ArrowTopIcon from "mdi-react/ArrowTopIcon";
import ArrowDownIcon from "mdi-react/ArrowDownIcon";
import ArrowForwardIcon from "mdi-react/ArrowForwardIcon";
import ArrowULeftTopIcon from "mdi-react/ArrowULeftTopIcon";
import ArrowURightTopIcon from "mdi-react/ArrowURightTopIcon";
@@ -428,6 +430,30 @@ function Header({
tab.session?.noteId
);
break;
case "scroll-top":
{
const element = document.getElementById(
"editor-container-scroller"
);
element?.scrollTo({
top: 0,
left: 0,
behavior: "smooth"
});
}
break;
case "scroll-bottom":
{
const element = document.getElementById(
"editor-container-scroller"
);
element?.scrollTo({
top: element?.scrollHeight,
left: 0,
behavior: "smooth"
});
}
break;
case "properties":
post(
EditorEvents.properties,
@@ -590,6 +616,47 @@ function Header({
{strings.toc()}
</span>
</MenuItem>
<MenuItem
value="scroll-top"
style={{
display: "flex",
gap: 10,
alignItems: "center"
}}
>
<ArrowTopIcon
size={22 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
style={{
color: "var(--nn_primary_paragraph)"
}}
>
{strings.scrollToTop()}
</span>
</MenuItem>
<MenuItem
value="scroll-bottom"
style={{
display: "flex",
gap: 10,
alignItems: "center"
}}
>
<ArrowDownIcon
size={22 * settings.fontScale}
color="var(--nn_primary_icon)"
/>
<span
style={{
color: "var(--nn_primary_paragraph)"
}}
>
{strings.scrollToBottom()}
</span>
</MenuItem>
<MenuItem
value="properties"
style={{

View File

@@ -5309,6 +5309,14 @@ msgstr "Scan the QR code with your authenticator app"
msgid "School work"
msgstr "School work"
#: src/strings.ts:2491
msgid "Scroll to bottom"
msgstr "Scroll to bottom"
#: src/strings.ts:2490
msgid "Scroll to top"
msgstr "Scroll to top"
#: src/strings.ts:1509
#: src/strings.ts:1527
msgid "Search"

View File

@@ -5283,6 +5283,14 @@ msgstr ""
msgid "School work"
msgstr ""
#: src/strings.ts:2491
msgid "Scroll to bottom"
msgstr ""
#: src/strings.ts:2490
msgid "Scroll to top"
msgstr ""
#: src/strings.ts:1509
#: src/strings.ts:1527
msgid "Search"

View File

@@ -2489,5 +2489,7 @@ Use this if changes from other devices are not appearing on this device. This wi
words: () => t`Words`,
characters: () => t`Characters`,
paragraphs: () => t`Paragraphs`,
noNotebooksSelectedToMove: () => t`No notebooks selected to move`
noNotebooksSelectedToMove: () => t`No notebooks selected to move`,
scrollToTop: () => t`Scroll to top`,
scrollToBottom: () => t`Scroll to bottom`
};