From 64943647f81584e53b2e641688153eb5bcd98c5b Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Thu, 25 Jun 2026 22:10:11 +0500 Subject: [PATCH 01/11] mobile: add app shortcut for creating reminders Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/hooks/use-shortcut-manager.ts | 6 ++++++ apps/mobile/app/navigation/fluid-panels-view.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/apps/mobile/app/hooks/use-shortcut-manager.ts b/apps/mobile/app/hooks/use-shortcut-manager.ts index cb3ff2fee..011c463a8 100644 --- a/apps/mobile/app/hooks/use-shortcut-manager.ts +++ b/apps/mobile/app/hooks/use-shortcut-manager.ts @@ -36,6 +36,12 @@ const defaultShortcuts: ShortcutItem[] = [ title: strings.createNewNote(), shortTitle: strings.newNote(), iconName: Platform.OS === "android" ? "ic_newnote" : "plus" + }, + { + type: "notesnook.action.newreminder", + title: strings.setReminder(), + shortTitle: strings.newReminder(), + iconName: Platform.OS === "android" ? "ic_newnote" : "plus" } ]; export const useShortcutManager = ({ diff --git a/apps/mobile/app/navigation/fluid-panels-view.tsx b/apps/mobile/app/navigation/fluid-panels-view.tsx index 2ef17c040..9cb659e8a 100644 --- a/apps/mobile/app/navigation/fluid-panels-view.tsx +++ b/apps/mobile/app/navigation/fluid-panels-view.tsx @@ -67,6 +67,7 @@ import { valueLimiter } from "../utils/functions"; import { fluidTabsRef } from "../utils/global-refs"; import { AppNavigationStack } from "./navigation-stack"; import type { PaneWidths } from "../screens/editor/wrapper"; +import AddReminder from "../screens/add-reminder"; const MOBILE_SIDEBAR_SIZE = 0.85; @@ -131,6 +132,11 @@ export const FluidPanelsView = React.memo( 300 ); } + if (item?.type === "notesnook.action.newreminder") { + setTimeout(() => { + AddReminder.present(); + }, 1000); + } } }); From 180c202c4e613d24a71fdde2ffddb03d183d510d Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Tue, 7 Jul 2026 14:28:23 +0500 Subject: [PATCH 02/11] mobile: handle app shortcuts correctly during cold and warm app launches Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/app.tsx | 19 +++++++ apps/mobile/app/hooks/use-shortcut-manager.ts | 21 +++----- .../app/navigation/fluid-panels-view.tsx | 52 +++++++++++-------- .../app/navigation/navigation-stack.tsx | 17 +++++- .../mobile/app/screens/add-reminder/index.tsx | 2 +- 5 files changed, 71 insertions(+), 40 deletions(-) diff --git a/apps/mobile/app/app.tsx b/apps/mobile/app/app.tsx index 6cf5dd016..6224cf8e6 100644 --- a/apps/mobile/app/app.tsx +++ b/apps/mobile/app/app.tsx @@ -44,9 +44,15 @@ import { useUserStore } from "./stores/use-user-store"; import RNBootSplash from "react-native-bootsplash"; import AppLocked from "./components/app-lock"; import { useSettingStore } from "./stores/use-setting-store"; +import { registerAppShortcuts } from "./hooks/use-shortcut-manager"; +import Shortcuts, { ShortcutItem } from "react-native-actions-shortcuts"; I18nManager.allowRTL(false); I18nManager.forceRTL(false); I18nManager.swapLeftAndRightInRTL(false); +declare global { + var __pendingShortcut: ShortcutItem | null | undefined; +} + const { appLockEnabled, appLockMode } = SettingsService.get(); if (appLockEnabled || appLockMode !== "none") { useUserStore.getState().lockApp(true); @@ -59,10 +65,23 @@ Linking.getInitialURL().then((url) => { initialUrl: url }); }); +Shortcuts.getInitialShortcut().then((shortcut) => { + globalThis.__pendingShortcut = shortcut; +}); const App = (props: { configureMode: "note-preview" }) => { useAppEvents(); //@ts-ignore globalThis["IS_MAIN_APP_RUNNING"] = true; + const introCompleted = useSettingStore( + (state) => state.settings.introCompleted + ); + + useEffect(() => { + if (introCompleted) { + registerAppShortcuts(); + } + }, [introCompleted]); + useEffect(() => { SettingsService.onFirstLaunch(); changeSystemBarColors(); diff --git a/apps/mobile/app/hooks/use-shortcut-manager.ts b/apps/mobile/app/hooks/use-shortcut-manager.ts index 011c463a8..7f9fcb614 100644 --- a/apps/mobile/app/hooks/use-shortcut-manager.ts +++ b/apps/mobile/app/hooks/use-shortcut-manager.ts @@ -45,26 +45,12 @@ const defaultShortcuts: ShortcutItem[] = [ } ]; export const useShortcutManager = ({ - onShortcutPressed, - shortcuts = defaultShortcuts + onShortcutPressed }: { onShortcutPressed: (shortcut: ShortcutItem | null) => void; - shortcuts?: ShortcutItem[]; }) => { - const initialShortcutRecieved = useRef(false); - useEffect(() => { if (!isSupported()) return; - Shortcuts.setShortcuts(shortcuts); - }, [shortcuts]); - - useEffect(() => { - if (!isSupported()) return; - Shortcuts.getInitialShortcut().then((shortcut) => { - if (initialShortcutRecieved.current || !shortcut) return; - onShortcutPressed(shortcut); - initialShortcutRecieved.current = true; - }); const subscription = ShortcutsEmitter.addListener( "onShortcutItemPressed", onShortcutPressed @@ -74,3 +60,8 @@ export const useShortcutManager = ({ }; }, [onShortcutPressed]); }; + +export const registerAppShortcuts = () => { + if (!isSupported()) return; + Shortcuts.setShortcuts(defaultShortcuts); +}; diff --git a/apps/mobile/app/navigation/fluid-panels-view.tsx b/apps/mobile/app/navigation/fluid-panels-view.tsx index 9cb659e8a..7a9a601d5 100644 --- a/apps/mobile/app/navigation/fluid-panels-view.tsx +++ b/apps/mobile/app/navigation/fluid-panels-view.tsx @@ -68,6 +68,8 @@ import { fluidTabsRef } from "../utils/global-refs"; import { AppNavigationStack } from "./navigation-stack"; import type { PaneWidths } from "../screens/editor/wrapper"; import AddReminder from "../screens/add-reminder"; +import Navigation from "../services/navigation"; +import { ShortcutItem } from "react-native-actions-shortcuts"; const MOBILE_SIDEBAR_SIZE = 0.85; @@ -112,33 +114,37 @@ export const FluidPanelsView = React.memo( } }, [appLoading]); - useShortcutManager({ - onShortcutPressed: async (item) => { - if (!item) return; + useEffect(() => { + const pending = globalThis.__pendingShortcut; + if ( + pending?.type === "notesnook.action.newnote" && + fluidTabsRef.current && + !appLoading + ) { + eSendEvent(eOnLoadNote, { newNote: true }); + editorState().movedAway = false; + fluidTabsRef.current.goToPage("editor", false); + globalThis.__pendingShortcut = null; + } + }, [deviceMode, appLoading]); - if (item?.type === "notesnook.action.newnote") { - if (!fluidTabsRef.current) { - setTimeout(() => { - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current?.goToPage("editor", false); - }, 1000); - return; - } + const onShortcutPressed = useCallback(async (item: ShortcutItem | null) => { + if (!item) return; + + if (item?.type === "notesnook.action.newnote") { + Navigation.navigate("FluidPanelsView"); + requestAnimationFrame(() => { eSendEvent(eOnLoadNote, { newNote: true }); editorState().movedAway = false; - setTimeout( - () => fluidTabsRef.current?.goToPage("editor", false), - 300 - ); - } - if (item?.type === "notesnook.action.newreminder") { - setTimeout(() => { - AddReminder.present(); - }, 1000); - } + fluidTabsRef.current?.goToPage("editor", false); + }); } - }); + if (item?.type === "notesnook.action.newreminder") { + AddReminder.present(); + } + }, []); + + useShortcutManager({ onShortcutPressed }); const showFullScreenEditor = useCallback(() => { setFullscreen(true); diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index 570b7e1c8..6dd0e4121 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -316,8 +316,23 @@ export const RootNavigation = () => { [clearSelection] ); + const onNavigationReady = React.useCallback(() => { + const pending = globalThis.__pendingShortcut; + if (pending?.type === "notesnook.action.newreminder") { + rootNavigatorRef.current?.navigate("AddReminder", { + reminder: undefined, + reference: undefined + }); + globalThis.__pendingShortcut = null; + } + }, []); + return ( - + ) { - const { reminder, reference } = props.route.params; + const { reminder, reference } = props.route.params ?? {}; useNavigationFocus(props.navigation, { focusOnInit: true, onFocus: () => { From a40814a3ab575db4ab6934480d95cdf2d6c2b8c6 Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Sat, 11 Jul 2026 19:14:30 +0500 Subject: [PATCH 03/11] mobile: revise app shortcut handling implementation Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/app.tsx | 26 +++---- .../app/components/fluid-panels/index.tsx | 13 ++-- apps/mobile/app/hooks/use-shortcut-manager.ts | 48 +++++++------ .../app/navigation/fluid-panels-view.tsx | 50 ++++---------- .../app/navigation/navigation-stack.tsx | 69 +++++++++++++++---- .../mobile/app/screens/add-reminder/index.tsx | 21 +++++- apps/mobile/app/stores/use-setting-store.ts | 7 +- 7 files changed, 140 insertions(+), 94 deletions(-) diff --git a/apps/mobile/app/app.tsx b/apps/mobile/app/app.tsx index 6224cf8e6..b8fd22ba3 100644 --- a/apps/mobile/app/app.tsx +++ b/apps/mobile/app/app.tsx @@ -44,30 +44,32 @@ import { useUserStore } from "./stores/use-user-store"; import RNBootSplash from "react-native-bootsplash"; import AppLocked from "./components/app-lock"; import { useSettingStore } from "./stores/use-setting-store"; -import { registerAppShortcuts } from "./hooks/use-shortcut-manager"; -import Shortcuts, { ShortcutItem } from "react-native-actions-shortcuts"; +import { + initShortcutListener, + registerAppShortcuts +} from "./hooks/use-shortcut-manager"; +import Shortcuts from "react-native-actions-shortcuts"; I18nManager.allowRTL(false); I18nManager.forceRTL(false); I18nManager.swapLeftAndRightInRTL(false); -declare global { - var __pendingShortcut: ShortcutItem | null | undefined; -} const { appLockEnabled, appLockMode } = SettingsService.get(); if (appLockEnabled || appLockMode !== "none") { useUserStore.getState().lockApp(true); } -RNBootSplash.hide({ - fade: true -}); +initShortcutListener(); Linking.getInitialURL().then((url) => { - useSettingStore.setState({ - initialUrl: url - }); + useSettingStore.setState({ initialUrl: url }); }); + Shortcuts.getInitialShortcut().then((shortcut) => { - globalThis.__pendingShortcut = shortcut; + useSettingStore.setState({ + pendingShortcut: shortcut ?? null, + pendingShortcutLoaded: true + }); + RNBootSplash.hide({ fade: true }); }); + const App = (props: { configureMode: "note-preview" }) => { useAppEvents(); //@ts-ignore diff --git a/apps/mobile/app/components/fluid-panels/index.tsx b/apps/mobile/app/components/fluid-panels/index.tsx index c2c0d0372..072be6f7e 100644 --- a/apps/mobile/app/components/fluid-panels/index.tsx +++ b/apps/mobile/app/components/fluid-panels/index.tsx @@ -51,9 +51,10 @@ interface TabProps extends ViewProps { onScroll: (offset: number) => void; enabled: boolean; onDrawerStateChange: (state: boolean) => void; + initialPage?: FluidTabPage; } -type FluidTabPage = "home" | "editor"; +export type FluidTabPage = "home" | "editor"; export interface TabsRef { goToPage: (page: FluidTabPage, animated?: boolean) => void; @@ -77,15 +78,19 @@ export const FluidPanels = forwardRef(function FluidTabs( onChangeTab, onScroll, enabled, - onDrawerStateChange + onDrawerStateChange, + initialPage }: TabProps, ref ) { const deviceMode = useSettingStore((state) => state.deviceMode); const fullscreen = useSettingStore((state) => state.fullscreen); - const translateX = useSharedValue(widths ? widths.sidebar : 0); + const editorStartPosition = widths.sidebar + widths.list; + const translateX = useSharedValue( + initialPage === "editor" ? editorStartPosition : widths ? widths.sidebar : 0 + ); const startX = useSharedValue(0); - const currentTab = useSharedValue(1); + const currentTab = useSharedValue(initialPage === "editor" ? 2 : 1); const previousTab = useSharedValue(1); const isDrawerOpen = useSharedValue(false); const gestureStartValue = useSharedValue({ diff --git a/apps/mobile/app/hooks/use-shortcut-manager.ts b/apps/mobile/app/hooks/use-shortcut-manager.ts index 7f9fcb614..224ac9a09 100644 --- a/apps/mobile/app/hooks/use-shortcut-manager.ts +++ b/apps/mobile/app/hooks/use-shortcut-manager.ts @@ -17,19 +17,19 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ import Shortcuts, { ShortcutItem } from "react-native-actions-shortcuts"; -import { useEffect } from "react"; -import { NativeEventEmitter, NativeModule } from "react-native"; -import { useRef } from "react"; -import { Platform } from "react-native"; +import { NativeEventEmitter, NativeModule, Platform } from "react-native"; import deviceInfoModule from "react-native-device-info"; import { strings } from "@notesnook/intl"; +import { useSettingStore } from "../stores/use-setting-store"; + const ShortcutsEmitter = new NativeEventEmitter( Shortcuts as unknown as NativeModule ); -function isSupported() { +export function isShortcutsSupported() { return Platform.OS !== "android" || deviceInfoModule.getApiLevelSync() > 25; } + const defaultShortcuts: ShortcutItem[] = [ { type: "notesnook.action.newnote", @@ -44,24 +44,22 @@ const defaultShortcuts: ShortcutItem[] = [ iconName: Platform.OS === "android" ? "ic_newnote" : "plus" } ]; -export const useShortcutManager = ({ - onShortcutPressed -}: { - onShortcutPressed: (shortcut: ShortcutItem | null) => void; -}) => { - useEffect(() => { - if (!isSupported()) return; - const subscription = ShortcutsEmitter.addListener( - "onShortcutItemPressed", - onShortcutPressed - ); - return () => { - subscription?.remove(); - }; - }, [onShortcutPressed]); -}; -export const registerAppShortcuts = () => { - if (!isSupported()) return; - Shortcuts.setShortcuts(defaultShortcuts); -}; +export function registerAppShortcuts( + shortcuts: ShortcutItem[] = defaultShortcuts +) { + if (!isShortcutsSupported()) return; + Shortcuts.setShortcuts(shortcuts); +} + +let listenerInitialized = false; +export function initShortcutListener() { + if (!isShortcutsSupported() || listenerInitialized) return; + listenerInitialized = true; + ShortcutsEmitter.addListener( + "onShortcutItemPressed", + (shortcut: ShortcutItem) => { + useSettingStore.setState({ pendingShortcut: shortcut }); + } + ); +} diff --git a/apps/mobile/app/navigation/fluid-panels-view.tsx b/apps/mobile/app/navigation/fluid-panels-view.tsx index 7a9a601d5..677f6ac31 100644 --- a/apps/mobile/app/navigation/fluid-panels-view.tsx +++ b/apps/mobile/app/navigation/fluid-panels-view.tsx @@ -41,10 +41,9 @@ import Animated, { } from "react-native-reanimated"; import { notesnook } from "../../e2e/test.ids"; import { db } from "../common/database"; -import { FluidPanels } from "../components/fluid-panels"; +import { FluidPanels, FluidTabPage } from "../components/fluid-panels"; import { useSideBarDraggingStore } from "../components/side-menu/dragging-store"; import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets"; -import { useShortcutManager } from "../hooks/use-shortcut-manager"; import { hideAllTooltips } from "../hooks/use-tooltip"; import { useTabStore } from "../screens/editor/tiptap/use-tab-store"; import { editorController, editorState } from "../screens/editor/tiptap/utils"; @@ -67,9 +66,7 @@ import { valueLimiter } from "../utils/functions"; import { fluidTabsRef } from "../utils/global-refs"; import { AppNavigationStack } from "./navigation-stack"; import type { PaneWidths } from "../screens/editor/wrapper"; -import AddReminder from "../screens/add-reminder"; import Navigation from "../services/navigation"; -import { ShortcutItem } from "react-native-actions-shortcuts"; const MOBILE_SIDEBAR_SIZE = 0.85; @@ -94,6 +91,18 @@ export const FluidPanelsView = React.memo( ); const appLoading = useSettingStore((state) => state.isAppLoading); const [isLoading, setIsLoading] = useState(false); + const pendingShortcut = useSettingStore((state) => state.pendingShortcut); + const [initialPane] = useState(() => + pendingShortcut?.type === "notesnook.action.newnote" ? "editor" : "home" + ); + + useEffect(() => { + if (pendingShortcut?.type === "notesnook.action.newnote") { + eSendEvent(eOnLoadNote, { newNote: true }); + editorState().movedAway = false; + useSettingStore.setState({ pendingShortcut: null }); + } + }, []); useDeviceOrientationChange((o) => { if ( @@ -114,38 +123,6 @@ export const FluidPanelsView = React.memo( } }, [appLoading]); - useEffect(() => { - const pending = globalThis.__pendingShortcut; - if ( - pending?.type === "notesnook.action.newnote" && - fluidTabsRef.current && - !appLoading - ) { - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current.goToPage("editor", false); - globalThis.__pendingShortcut = null; - } - }, [deviceMode, appLoading]); - - const onShortcutPressed = useCallback(async (item: ShortcutItem | null) => { - if (!item) return; - - if (item?.type === "notesnook.action.newnote") { - Navigation.navigate("FluidPanelsView"); - requestAnimationFrame(() => { - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current?.goToPage("editor", false); - }); - } - if (item?.type === "notesnook.action.newreminder") { - AddReminder.present(); - } - }, []); - - useShortcutManager({ onShortcutPressed }); - const showFullScreenEditor = useCallback(() => { setFullscreen(true); if (deviceMode === "smallTablet") { @@ -368,6 +345,7 @@ export const FluidPanelsView = React.memo( dimensions={dimensions} widths={PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS]} enabled={deviceMode !== "tablet" && !fullscreen} + initialPage={initialPane} onScroll={onScroll} onChangeTab={onChangeTab} onDrawerStateChange={(state) => { diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index 6dd0e4121..7dc5cac78 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -27,10 +27,13 @@ import useNavigationStore, { } from "../stores/use-navigation-store"; import { useSelectionStore } from "../stores/use-selection-store"; import { useSettingStore } from "../stores/use-setting-store"; -import { rootNavigatorRef } from "../utils/global-refs"; +import { fluidTabsRef, rootNavigatorRef } from "../utils/global-refs"; import Navigation from "../services/navigation"; import { isFeatureAvailable } from "@notesnook/common"; import { isInternalLink, parseInternalLink } from "@notesnook/core"; +import { eSendEvent } from "../services/event-manager"; +import { editorState } from "../screens/editor/tiptap/utils"; +import { eOnLoadNote } from "../utils/events"; const RootStack = createNativeStackNavigator(); const AppStack = createNativeStackNavigator(); @@ -300,8 +303,13 @@ export const RootNavigation = () => { const introCompleted = useSettingStore( (state) => state.settings.introCompleted ); + const pendingShortcut = useSettingStore((state) => state.pendingShortcut); + const pendingShortcutLoaded = useSettingStore( + (state) => state.pendingShortcutLoaded + ); const clearSelection = useSelectionStore((state) => state.clearSelection); const resetTimer = React.useRef(undefined); + const isFirstRun = React.useRef(true); const onStateChange = React.useCallback( (state: any) => { if (useSelectionStore.getState().selectionMode) { @@ -316,28 +324,59 @@ export const RootNavigation = () => { [clearSelection] ); - const onNavigationReady = React.useCallback(() => { - const pending = globalThis.__pendingShortcut; - if (pending?.type === "notesnook.action.newreminder") { - rootNavigatorRef.current?.navigate("AddReminder", { - reminder: undefined, - reference: undefined - }); - globalThis.__pendingShortcut = null; + React.useEffect(() => { + if (pendingShortcut?.type === "notesnook.action.newreminder") { + useSettingStore.setState({ pendingShortcut: null }); } }, []); + React.useEffect(() => { + if (isFirstRun.current) { + isFirstRun.current = false; + return; + } + if (!pendingShortcut) return; + + const routes = rootNavigatorRef.current?.getState()?.routes; + const currentRoute = routes?.[routes.length - 1]?.name; + + if (pendingShortcut.type === "notesnook.action.newreminder") { + if (currentRoute !== "AddReminder") { + rootNavigatorRef.current?.navigate("AddReminder" as any); + } + useSettingStore.setState({ pendingShortcut: null }); + } else if (pendingShortcut.type === "notesnook.action.newnote") { + if (fluidTabsRef.current) { + if (currentRoute !== "FluidPanelsView") { + rootNavigatorRef.current?.navigate("FluidPanelsView" as any); + } + eSendEvent(eOnLoadNote, { newNote: true }); + editorState().movedAway = false; + fluidTabsRef.current.goToPage("editor", true); + useSettingStore.setState({ pendingShortcut: null }); + } else { + if (currentRoute !== "FluidPanelsView") { + rootNavigatorRef.current?.navigate("FluidPanelsView" as any); + } + } + } + }, [pendingShortcut]); + + if (!pendingShortcutLoaded) return null; + + const initialRouteName = !introCompleted + ? "Welcome" + : pendingShortcut?.type === "notesnook.action.newreminder" + ? "AddReminder" + : "FluidPanelsView"; + return ( - + ) { return false; } }); + const handleBackNavigation = useCallback(() => { + const routes = props.navigation.getState()?.routes; + if (routes && routes.length <= 1) { + props.navigation.navigate("FluidPanelsView" as any); + return true; + } + Navigation.goBack(); + return true; + }, [props.navigation]); + + useEffect(() => { + const sub = BackHandler.addEventListener("hardwareBackPress", () => { + return handleBackNavigation(); + }); + return () => sub.remove(); + }, [handleBackNavigation]); + const { colors, isDark } = useThemeColors(); const weekFormat = useSettingStore((state) => state.weekFormat); const [reminderMode, setReminderMode] = useState( @@ -267,6 +285,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) {
void; inboxEnabled: boolean; setInboxEnabled: (inboxEnabled: boolean) => void; + pendingShortcut: ShortcutItem | null; + pendingShortcutLoaded: boolean; } const { width, height } = Dimensions.get("window"); @@ -269,5 +272,7 @@ export const useSettingStore = create((set, get) => ({ }); }, inboxEnabled: false, - setInboxEnabled: (inboxEnabled) => set({ inboxEnabled }) + setInboxEnabled: (inboxEnabled) => set({ inboxEnabled }), + pendingShortcut: null, + pendingShortcutLoaded: false })); From c3c9b1fbf4634873bc8d96e2982ef1ad3e516b28 Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Tue, 14 Jul 2026 09:18:56 +0500 Subject: [PATCH 04/11] mobile: fix app shortcut navigation, flash, and back-nav issues Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/app.tsx | 9 ++++--- .../app/components/fluid-panels/index.tsx | 4 ++- .../app/navigation/fluid-panels-view.tsx | 3 ++- .../app/navigation/navigation-stack.tsx | 27 ++++--------------- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/apps/mobile/app/app.tsx b/apps/mobile/app/app.tsx index b8fd22ba3..e549ae93a 100644 --- a/apps/mobile/app/app.tsx +++ b/apps/mobile/app/app.tsx @@ -57,10 +57,6 @@ const { appLockEnabled, appLockMode } = SettingsService.get(); if (appLockEnabled || appLockMode !== "none") { useUserStore.getState().lockApp(true); } -initShortcutListener(); -Linking.getInitialURL().then((url) => { - useSettingStore.setState({ initialUrl: url }); -}); Shortcuts.getInitialShortcut().then((shortcut) => { useSettingStore.setState({ @@ -68,6 +64,11 @@ Shortcuts.getInitialShortcut().then((shortcut) => { pendingShortcutLoaded: true }); RNBootSplash.hide({ fade: true }); + initShortcutListener(); +}); + +Linking.getInitialURL().then((url) => { + useSettingStore.setState({ initialUrl: url }); }); const App = (props: { configureMode: "note-preview" }) => { diff --git a/apps/mobile/app/components/fluid-panels/index.tsx b/apps/mobile/app/components/fluid-panels/index.tsx index 072be6f7e..45913a669 100644 --- a/apps/mobile/app/components/fluid-panels/index.tsx +++ b/apps/mobile/app/components/fluid-panels/index.tsx @@ -90,7 +90,9 @@ export const FluidPanels = forwardRef(function FluidTabs( initialPage === "editor" ? editorStartPosition : widths ? widths.sidebar : 0 ); const startX = useSharedValue(0); - const currentTab = useSharedValue(initialPage === "editor" ? 2 : 1); + const currentTab = useSharedValue( + initialPage === "editor" && deviceMode !== "tablet" ? 2 : 1 + ); const previousTab = useSharedValue(1); const isDrawerOpen = useSharedValue(false); const gestureStartValue = useSharedValue({ diff --git a/apps/mobile/app/navigation/fluid-panels-view.tsx b/apps/mobile/app/navigation/fluid-panels-view.tsx index 677f6ac31..0a5472555 100644 --- a/apps/mobile/app/navigation/fluid-panels-view.tsx +++ b/apps/mobile/app/navigation/fluid-panels-view.tsx @@ -100,9 +100,10 @@ export const FluidPanelsView = React.memo( if (pendingShortcut?.type === "notesnook.action.newnote") { eSendEvent(eOnLoadNote, { newNote: true }); editorState().movedAway = false; + fluidTabsRef.current?.goToPage("editor", true); useSettingStore.setState({ pendingShortcut: null }); } - }, []); + }, [pendingShortcut]); useDeviceOrientationChange((o) => { if ( diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index 7dc5cac78..6639c8a10 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -309,7 +309,7 @@ export const RootNavigation = () => { ); const clearSelection = useSelectionStore((state) => state.clearSelection); const resetTimer = React.useRef(undefined); - const isFirstRun = React.useRef(true); + const isNavigationLoaded = React.useRef(true); const onStateChange = React.useCallback( (state: any) => { if (useSelectionStore.getState().selectionMode) { @@ -331,34 +331,17 @@ export const RootNavigation = () => { }, []); React.useEffect(() => { - if (isFirstRun.current) { - isFirstRun.current = false; + if (isNavigationLoaded.current) { + isNavigationLoaded.current = false; return; } if (!pendingShortcut) return; - const routes = rootNavigatorRef.current?.getState()?.routes; - const currentRoute = routes?.[routes.length - 1]?.name; - if (pendingShortcut.type === "notesnook.action.newreminder") { - if (currentRoute !== "AddReminder") { - rootNavigatorRef.current?.navigate("AddReminder" as any); - } + rootNavigatorRef.current?.navigate("AddReminder" as any); useSettingStore.setState({ pendingShortcut: null }); } else if (pendingShortcut.type === "notesnook.action.newnote") { - if (fluidTabsRef.current) { - if (currentRoute !== "FluidPanelsView") { - rootNavigatorRef.current?.navigate("FluidPanelsView" as any); - } - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current.goToPage("editor", true); - useSettingStore.setState({ pendingShortcut: null }); - } else { - if (currentRoute !== "FluidPanelsView") { - rootNavigatorRef.current?.navigate("FluidPanelsView" as any); - } - } + rootNavigatorRef.current?.navigate("FluidPanelsView" as any); } }, [pendingShortcut]); From 3e2847f84478ffd06a2e250a17d4dbb3c0a5973a Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Tue, 14 Jul 2026 19:06:56 +0500 Subject: [PATCH 05/11] mobile: add fluid panels initialRoute to simplified navigation Signed-off-by: kashaf-ansari-dev --- .../app/navigation/fluid-panels-view.tsx | 20 +++---------------- .../app/navigation/navigation-stack.tsx | 17 +++++++++++++++- .../mobile/app/stores/use-navigation-store.ts | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/apps/mobile/app/navigation/fluid-panels-view.tsx b/apps/mobile/app/navigation/fluid-panels-view.tsx index 0a5472555..1292c6452 100644 --- a/apps/mobile/app/navigation/fluid-panels-view.tsx +++ b/apps/mobile/app/navigation/fluid-panels-view.tsx @@ -58,7 +58,6 @@ import { eCloseFullscreenEditor, eOnEnterEditor, eOnExitEditor, - eOnLoadNote, eOpenFullscreenEditor, eUnlockNote } from "../utils/events"; @@ -66,7 +65,7 @@ import { valueLimiter } from "../utils/functions"; import { fluidTabsRef } from "../utils/global-refs"; import { AppNavigationStack } from "./navigation-stack"; import type { PaneWidths } from "../screens/editor/wrapper"; -import Navigation from "../services/navigation"; +import { NavigationProps } from "../services/navigation"; const MOBILE_SIDEBAR_SIZE = 0.85; @@ -74,7 +73,7 @@ let SideMenu: any = null; let EditorWrapper: any = null; export const FluidPanelsView = React.memo( - () => { + ({ route }: NavigationProps<"FluidPanelsView">) => { const { colors } = useThemeColors(); const deviceMode = useSettingStore((state) => state.deviceMode); const setFullscreen = useSettingStore((state) => state.setFullscreen); @@ -91,19 +90,6 @@ export const FluidPanelsView = React.memo( ); const appLoading = useSettingStore((state) => state.isAppLoading); const [isLoading, setIsLoading] = useState(false); - const pendingShortcut = useSettingStore((state) => state.pendingShortcut); - const [initialPane] = useState(() => - pendingShortcut?.type === "notesnook.action.newnote" ? "editor" : "home" - ); - - useEffect(() => { - if (pendingShortcut?.type === "notesnook.action.newnote") { - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current?.goToPage("editor", true); - useSettingStore.setState({ pendingShortcut: null }); - } - }, [pendingShortcut]); useDeviceOrientationChange((o) => { if ( @@ -346,7 +332,7 @@ export const FluidPanelsView = React.memo( dimensions={dimensions} widths={PANE_WIDTHS[deviceMode as keyof typeof PANE_WIDTHS]} enabled={deviceMode !== "tablet" && !fullscreen} - initialPage={initialPane} + initialPage={route.params?.initialPage} onScroll={onScroll} onChangeTab={onChangeTab} onDrawerStateChange={(state) => { diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index 6639c8a10..486bedf14 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -341,7 +341,16 @@ export const RootNavigation = () => { rootNavigatorRef.current?.navigate("AddReminder" as any); useSettingStore.setState({ pendingShortcut: null }); } else if (pendingShortcut.type === "notesnook.action.newnote") { - rootNavigatorRef.current?.navigate("FluidPanelsView" as any); + rootNavigatorRef.current?.navigate("FluidPanelsView" as any, { + initialPage: !fluidTabsRef.current ? "editor" : undefined + }); + + if (fluidTabsRef.current) { + eSendEvent(eOnLoadNote, { newNote: true }); + editorState().movedAway = false; + fluidTabsRef.current.goToPage("editor", true); + useSettingStore.setState({ pendingShortcut: null }); + } } }, [pendingShortcut]); @@ -384,6 +393,12 @@ export const RootNavigation = () => { require("../navigation/fluid-panels-view").default; return FluidPanelsView; }} + initialParams={{ + initialPage: + pendingShortcut?.type === "notesnook.action.newnote" + ? "editor" + : undefined + }} /> Date: Thu, 16 Jul 2026 13:27:58 +0500 Subject: [PATCH 06/11] mobile: resolve app shortcut navigation and add reminder feature gate Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/components/dialog/index.tsx | 3 -- .../app/navigation/navigation-stack.tsx | 41 ++++++++++++++++--- .../mobile/app/screens/add-reminder/index.tsx | 20 ++++++++- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/apps/mobile/app/components/dialog/index.tsx b/apps/mobile/app/components/dialog/index.tsx index c4e6a1080..3eaa4d092 100644 --- a/apps/mobile/app/components/dialog/index.tsx +++ b/apps/mobile/app/components/dialog/index.tsx @@ -143,9 +143,6 @@ export const Dialog = ({ context = "global" }: { context?: string }) => { }, [hide, show]); const onNegativePress = async () => { - if (dialogInfo?.onClose) { - await dialogInfo.onClose(); - } hide(); }; diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index 486bedf14..f1255a6aa 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -29,11 +29,14 @@ import { useSelectionStore } from "../stores/use-selection-store"; import { useSettingStore } from "../stores/use-setting-store"; import { fluidTabsRef, rootNavigatorRef } from "../utils/global-refs"; import Navigation from "../services/navigation"; -import { isFeatureAvailable } from "@notesnook/common"; +import { isFeatureAvailable, useIsFeatureAvailable } from "@notesnook/common"; import { isInternalLink, parseInternalLink } from "@notesnook/core"; import { eSendEvent } from "../services/event-manager"; import { editorState } from "../screens/editor/tiptap/utils"; import { eOnLoadNote } from "../utils/events"; +import { strings } from "@notesnook/intl"; +import PaywallSheet from "../components/sheets/paywall"; +import { presentDialog } from "../components/dialog/functions"; const RootStack = createNativeStackNavigator(); const AppStack = createNativeStackNavigator(); @@ -307,6 +310,7 @@ export const RootNavigation = () => { const pendingShortcutLoaded = useSettingStore( (state) => state.pendingShortcutLoaded ); + const reminderFeature = useIsFeatureAvailable("activeReminders"); const clearSelection = useSelectionStore((state) => state.clearSelection); const resetTimer = React.useRef(undefined); const isNavigationLoaded = React.useRef(true); @@ -338,18 +342,43 @@ export const RootNavigation = () => { if (!pendingShortcut) return; if (pendingShortcut.type === "notesnook.action.newreminder") { + if (reminderFeature === undefined) return; + + if (!reminderFeature.isAllowed) { + presentDialog({ + title: strings.upgrade(), + paragraph: reminderFeature.error, + positiveText: strings.upgrade(), + negativeText: strings.cancel(), + positivePress: async () => { + PaywallSheet.present(reminderFeature); + } + }); + useSettingStore.setState({ pendingShortcut: null }); + return; + } + rootNavigatorRef.current?.navigate("AddReminder" as any); useSettingStore.setState({ pendingShortcut: null }); } else if (pendingShortcut.type === "notesnook.action.newnote") { - rootNavigatorRef.current?.navigate("FluidPanelsView" as any, { - initialPage: !fluidTabsRef.current ? "editor" : undefined - }); + rootNavigatorRef.current?.navigate("FluidPanelsView" as any); - if (fluidTabsRef.current) { + const runNoteLoad = () => { eSendEvent(eOnLoadNote, { newNote: true }); editorState().movedAway = false; - fluidTabsRef.current.goToPage("editor", true); + fluidTabsRef.current?.goToPage("editor", true); useSettingStore.setState({ pendingShortcut: null }); + }; + + if (fluidTabsRef.current) { + runNoteLoad(); + } else { + const unsub = useSettingStore.subscribe((state) => { + if (state.deviceMode && fluidTabsRef.current) { + unsub(); + runNoteLoad(); + } + }); } } }, [pendingShortcut]); diff --git a/apps/mobile/app/screens/add-reminder/index.tsx b/apps/mobile/app/screens/add-reminder/index.tsx index d5aadf7c6..30c29995b 100644 --- a/apps/mobile/app/screens/add-reminder/index.tsx +++ b/apps/mobile/app/screens/add-reminder/index.tsx @@ -64,6 +64,7 @@ import FormInput, { validators } from "../../components/ui/input/form-input"; import AppIcon from "../../components/ui/AppIcon"; +import { presentDialog } from "../../components/dialog/functions"; const ReminderModes = Platform.OS === "ios" @@ -145,6 +146,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) { const [repeatFrequency, setRepeatFrequency] = useState(1); const referencedItem = reference ? (reference as Note) : null; const recurringReminderFeature = useIsFeatureAvailable("recurringReminders"); + const activeReminderFeature = useIsFeatureAvailable("activeReminders"); const formRef = useRef( createFormRef({ title: reminder?.title || referencedItem?.title || "", @@ -171,6 +173,23 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) { ); const [dateError, setDateError] = useState(); const [selectDayError, setSelectDayError] = useState(); + useEffect(() => { + if (activeReminderFeature === undefined) return; + if (!activeReminderFeature.isAllowed) { + presentDialog({ + title: strings.upgrade(), + paragraph: activeReminderFeature.error, + positiveText: strings.upgrade(), + negativeText: strings.cancel(), + positivePress: async () => { + PaywallSheet.present(activeReminderFeature); + }, + onClose: () => { + props.navigation.navigate("FluidPanelsView" as any); + } + }); + } + }, [activeReminderFeature]); const showDatePicker = () => { setDatePickerVisibility(true); @@ -291,7 +310,6 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) { onPress: saveReminder }} /> - Date: Fri, 17 Jul 2026 09:44:25 +0500 Subject: [PATCH 07/11] mobile: simplified new editor load logic Signed-off-by: kashaf-ansari-dev --- .../app/navigation/navigation-stack.tsx | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index f1255a6aa..d590c7ac1 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -37,6 +37,7 @@ import { eOnLoadNote } from "../utils/events"; import { strings } from "@notesnook/intl"; import PaywallSheet from "../components/sheets/paywall"; import { presentDialog } from "../components/dialog/functions"; +import { useTabStore } from "../screens/editor/tiptap/use-tab-store"; const RootStack = createNativeStackNavigator(); const AppStack = createNativeStackNavigator(); @@ -361,25 +362,33 @@ export const RootNavigation = () => { rootNavigatorRef.current?.navigate("AddReminder" as any); useSettingStore.setState({ pendingShortcut: null }); } else if (pendingShortcut.type === "notesnook.action.newnote") { - rootNavigatorRef.current?.navigate("FluidPanelsView" as any); - - const runNoteLoad = () => { - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current?.goToPage("editor", true); - useSettingStore.setState({ pendingShortcut: null }); - }; + let tabId; if (fluidTabsRef.current) { - runNoteLoad(); + rootNavigatorRef.current?.navigate("FluidPanelsView" as any); + eSendEvent(eOnLoadNote, { newNote: true }); + editorState().movedAway = false; + fluidTabsRef.current.goToPage("editor", true); } else { - const unsub = useSettingStore.subscribe((state) => { - if (state.deviceMode && fluidTabsRef.current) { - unsub(); - runNoteLoad(); + const currentTab = useTabStore + .getState() + .getTab(useTabStore.getState().currentTab as string); + + if (useTabStore.getState().tabs.length === 0 || currentTab?.pinned) { + tabId = useTabStore.getState().newTab(); + } else { + tabId = useTabStore.getState().currentTab; + if (useTabStore.getState().getTab(tabId)?.session?.noteId) { + useTabStore.getState().newTabSession(tabId, {}); } + } + + rootNavigatorRef.current?.navigate("FluidPanelsView" as any, { + initialPage: "editor" }); } + + useSettingStore.setState({ pendingShortcut: null }); } }, [pendingShortcut]); From ca19fd923099de46b6b8509d9e5fc7d2a1e9a3bb Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Tue, 21 Jul 2026 17:41:24 +0500 Subject: [PATCH 08/11] mobile: fix new note loading from shortcut on cold launch --- apps/mobile/app/app.tsx | 5 +++++ apps/mobile/app/hooks/use-shortcut-manager.ts | 18 ++++++++++++++++++ .../mobile/app/navigation/navigation-stack.tsx | 18 ++---------------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/apps/mobile/app/app.tsx b/apps/mobile/app/app.tsx index e549ae93a..bee37dbe5 100644 --- a/apps/mobile/app/app.tsx +++ b/apps/mobile/app/app.tsx @@ -46,6 +46,7 @@ import AppLocked from "./components/app-lock"; import { useSettingStore } from "./stores/use-setting-store"; import { initShortcutListener, + launchNewNoteTab, registerAppShortcuts } from "./hooks/use-shortcut-manager"; import Shortcuts from "react-native-actions-shortcuts"; @@ -59,6 +60,10 @@ if (appLockEnabled || appLockMode !== "none") { } Shortcuts.getInitialShortcut().then((shortcut) => { + if (shortcut?.type === "notesnook.action.newnote") { + launchNewNoteTab(); + } + useSettingStore.setState({ pendingShortcut: shortcut ?? null, pendingShortcutLoaded: true diff --git a/apps/mobile/app/hooks/use-shortcut-manager.ts b/apps/mobile/app/hooks/use-shortcut-manager.ts index 224ac9a09..9b990560c 100644 --- a/apps/mobile/app/hooks/use-shortcut-manager.ts +++ b/apps/mobile/app/hooks/use-shortcut-manager.ts @@ -21,6 +21,7 @@ import { NativeEventEmitter, NativeModule, Platform } from "react-native"; import deviceInfoModule from "react-native-device-info"; import { strings } from "@notesnook/intl"; import { useSettingStore } from "../stores/use-setting-store"; +import { useTabStore } from "../screens/editor/tiptap/use-tab-store"; const ShortcutsEmitter = new NativeEventEmitter( Shortcuts as unknown as NativeModule @@ -59,7 +60,24 @@ export function initShortcutListener() { ShortcutsEmitter.addListener( "onShortcutItemPressed", (shortcut: ShortcutItem) => { + console.time("shortcut"); useSettingStore.setState({ pendingShortcut: shortcut }); } ); } + +export function launchNewNoteTab() { + let tabId; + const currentTab = useTabStore + .getState() + .getTab(useTabStore.getState().currentTab as string); + + if (useTabStore.getState().tabs.length === 0 || currentTab?.pinned) { + tabId = useTabStore.getState().newTab(); + } else { + tabId = useTabStore.getState().currentTab; + if (useTabStore.getState().getTab(tabId)?.session?.noteId) { + useTabStore.getState().newTabSession(tabId, {}); + } + } +} diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index d590c7ac1..ab1ebf957 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -37,7 +37,7 @@ import { eOnLoadNote } from "../utils/events"; import { strings } from "@notesnook/intl"; import PaywallSheet from "../components/sheets/paywall"; import { presentDialog } from "../components/dialog/functions"; -import { useTabStore } from "../screens/editor/tiptap/use-tab-store"; +import { launchNewNoteTab } from "../hooks/use-shortcut-manager"; const RootStack = createNativeStackNavigator(); const AppStack = createNativeStackNavigator(); @@ -358,30 +358,16 @@ export const RootNavigation = () => { useSettingStore.setState({ pendingShortcut: null }); return; } - rootNavigatorRef.current?.navigate("AddReminder" as any); useSettingStore.setState({ pendingShortcut: null }); } else if (pendingShortcut.type === "notesnook.action.newnote") { - let tabId; - if (fluidTabsRef.current) { rootNavigatorRef.current?.navigate("FluidPanelsView" as any); eSendEvent(eOnLoadNote, { newNote: true }); editorState().movedAway = false; fluidTabsRef.current.goToPage("editor", true); } else { - const currentTab = useTabStore - .getState() - .getTab(useTabStore.getState().currentTab as string); - - if (useTabStore.getState().tabs.length === 0 || currentTab?.pinned) { - tabId = useTabStore.getState().newTab(); - } else { - tabId = useTabStore.getState().currentTab; - if (useTabStore.getState().getTab(tabId)?.session?.noteId) { - useTabStore.getState().newTabSession(tabId, {}); - } - } + launchNewNoteTab(); rootNavigatorRef.current?.navigate("FluidPanelsView" as any, { initialPage: "editor" From 97749729adbeb3a1a751a37be971dcee754294c4 Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Tue, 21 Jul 2026 22:03:30 +0500 Subject: [PATCH 09/11] mobile: improve startup loading shortcuts Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/app.tsx | 55 +++++++---- .../app/navigation/fluid-panels-view.tsx | 9 ++ .../app/navigation/navigation-stack.tsx | 97 +++++++++---------- .../mobile/app/screens/add-reminder/index.tsx | 8 ++ apps/mobile/app/stores/use-setting-store.ts | 4 +- 5 files changed, 101 insertions(+), 72 deletions(-) diff --git a/apps/mobile/app/app.tsx b/apps/mobile/app/app.tsx index bee37dbe5..40d17fe49 100644 --- a/apps/mobile/app/app.tsx +++ b/apps/mobile/app/app.tsx @@ -23,7 +23,7 @@ import { THEME_COMPATIBILITY_VERSION, useThemeEngineStore } from "@notesnook/theme"; -import React, { PropsWithChildren, useEffect } from "react"; +import React, { PropsWithChildren, useEffect, useState } from "react"; import { Appearance, I18nManager, Linking, StatusBar } from "react-native"; import "react-native-gesture-handler"; import { GestureHandlerRootView } from "react-native-gesture-handler"; @@ -59,23 +59,6 @@ if (appLockEnabled || appLockMode !== "none") { useUserStore.getState().lockApp(true); } -Shortcuts.getInitialShortcut().then((shortcut) => { - if (shortcut?.type === "notesnook.action.newnote") { - launchNewNoteTab(); - } - - useSettingStore.setState({ - pendingShortcut: shortcut ?? null, - pendingShortcutLoaded: true - }); - RNBootSplash.hide({ fade: true }); - initShortcutListener(); -}); - -Linking.getInitialURL().then((url) => { - useSettingStore.setState({ initialUrl: url }); -}); - const App = (props: { configureMode: "note-preview" }) => { useAppEvents(); //@ts-ignore @@ -203,4 +186,38 @@ export const withTheme = ( }; }; -export default withTheme(withErrorBoundry(App, "App")); +export const withStartupBoundry = ( + Element: (props: PropsWithChildren) => JSX.Element +) => { + return function AppWithStartupBoundary(props: PropsWithChildren) { + const [ready, setReady] = useState(false); + + useEffect(() => { + async function init() { + const [url, shortcut] = await Promise.all([ + Linking.getInitialURL(), + Shortcuts.getInitialShortcut() + ]); + if (shortcut?.type === "notesnook.action.newnote") { + launchNewNoteTab(); + } + useSettingStore.setState({ + initialUrl: url, + pendingShortcut: shortcut ?? null + }); + + initShortcutListener(); + await RNBootSplash.hide({ fade: true }); + setReady(true); + } + + init(); + }, []); + + if (!ready) return null; + + return ; + }; +}; + +export default withStartupBoundry(withTheme(withErrorBoundry(App, "App"))); diff --git a/apps/mobile/app/navigation/fluid-panels-view.tsx b/apps/mobile/app/navigation/fluid-panels-view.tsx index 1292c6452..e2650aef9 100644 --- a/apps/mobile/app/navigation/fluid-panels-view.tsx +++ b/apps/mobile/app/navigation/fluid-panels-view.tsx @@ -101,6 +101,15 @@ export const FluidPanelsView = React.memo( setOrientation(o); } }); + React.useEffect(() => { + const shortcut = useSettingStore.getState().pendingShortcut; + + if (shortcut?.type === "notesnook.action.newnote") { + useSettingStore.setState({ + pendingShortcut: null + }); + } + }, []); useEffect(() => { if (!appLoading) { diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index ab1ebf957..486b4150c 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -307,14 +307,15 @@ export const RootNavigation = () => { const introCompleted = useSettingStore( (state) => state.settings.introCompleted ); - const pendingShortcut = useSettingStore((state) => state.pendingShortcut); - const pendingShortcutLoaded = useSettingStore( - (state) => state.pendingShortcutLoaded - ); + + const initialShortcut = React.useRef( + useSettingStore.getState().pendingShortcut + ).current; + const reminderFeature = useIsFeatureAvailable("activeReminders"); const clearSelection = useSelectionStore((state) => state.clearSelection); const resetTimer = React.useRef(undefined); - const isNavigationLoaded = React.useRef(true); + const onStateChange = React.useCallback( (state: any) => { if (useSelectionStore.getState().selectionMode) { @@ -330,59 +331,55 @@ export const RootNavigation = () => { ); React.useEffect(() => { - if (pendingShortcut?.type === "notesnook.action.newreminder") { - useSettingStore.setState({ pendingShortcut: null }); - } - }, []); + const unsubscribe = useSettingStore.subscribe((state, prevState) => { + const pendingShortcut = state.pendingShortcut; - React.useEffect(() => { - if (isNavigationLoaded.current) { - isNavigationLoaded.current = false; - return; - } - if (!pendingShortcut) return; - - if (pendingShortcut.type === "notesnook.action.newreminder") { - if (reminderFeature === undefined) return; - - if (!reminderFeature.isAllowed) { - presentDialog({ - title: strings.upgrade(), - paragraph: reminderFeature.error, - positiveText: strings.upgrade(), - negativeText: strings.cancel(), - positivePress: async () => { - PaywallSheet.present(reminderFeature); - } - }); - useSettingStore.setState({ pendingShortcut: null }); + if (pendingShortcut === prevState.pendingShortcut || !pendingShortcut) { return; } - rootNavigatorRef.current?.navigate("AddReminder" as any); - useSettingStore.setState({ pendingShortcut: null }); - } else if (pendingShortcut.type === "notesnook.action.newnote") { - if (fluidTabsRef.current) { - rootNavigatorRef.current?.navigate("FluidPanelsView" as any); - eSendEvent(eOnLoadNote, { newNote: true }); - editorState().movedAway = false; - fluidTabsRef.current.goToPage("editor", true); - } else { - launchNewNoteTab(); - rootNavigatorRef.current?.navigate("FluidPanelsView" as any, { - initialPage: "editor" - }); + if (pendingShortcut.type === "notesnook.action.newreminder") { + if (reminderFeature === undefined) return; + + if (!reminderFeature.isAllowed) { + presentDialog({ + title: strings.upgrade(), + paragraph: reminderFeature.error, + positiveText: strings.upgrade(), + negativeText: strings.cancel(), + positivePress: async () => { + PaywallSheet.present(reminderFeature); + } + }); + useSettingStore.setState({ + pendingShortcut: null + }); + return; + } + + rootNavigatorRef.current?.navigate("AddReminder" as any); + } else if (pendingShortcut.type === "notesnook.action.newnote") { + if (fluidTabsRef.current) { + rootNavigatorRef.current?.navigate("FluidPanelsView" as any); + eSendEvent(eOnLoadNote, { newNote: true }); + editorState().movedAway = false; + fluidTabsRef.current.goToPage("editor", true); + } else { + launchNewNoteTab(); + + rootNavigatorRef.current?.navigate("FluidPanelsView" as any, { + initialPage: "editor" + }); + } } + }); - useSettingStore.setState({ pendingShortcut: null }); - } - }, [pendingShortcut]); - - if (!pendingShortcutLoaded) return null; + return unsubscribe; + }, [reminderFeature]); const initialRouteName = !introCompleted ? "Welcome" - : pendingShortcut?.type === "notesnook.action.newreminder" + : initialShortcut?.type === "notesnook.action.newreminder" ? "AddReminder" : "FluidPanelsView"; @@ -419,7 +416,7 @@ export const RootNavigation = () => { }} initialParams={{ initialPage: - pendingShortcut?.type === "notesnook.action.newnote" + initialShortcut?.type === "notesnook.action.newnote" ? "editor" : undefined }} diff --git a/apps/mobile/app/screens/add-reminder/index.tsx b/apps/mobile/app/screens/add-reminder/index.tsx index 30c29995b..e6cc813fa 100644 --- a/apps/mobile/app/screens/add-reminder/index.tsx +++ b/apps/mobile/app/screens/add-reminder/index.tsx @@ -173,6 +173,14 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) { ); const [dateError, setDateError] = useState(); const [selectDayError, setSelectDayError] = useState(); + React.useEffect(() => { + const shortcut = useSettingStore.getState().pendingShortcut; + if (shortcut?.type === "notesnook.action.newreminder") { + useSettingStore.setState({ + pendingShortcut: null + }); + } + }, []); useEffect(() => { if (activeReminderFeature === undefined) return; if (!activeReminderFeature.isAllowed) { diff --git a/apps/mobile/app/stores/use-setting-store.ts b/apps/mobile/app/stores/use-setting-store.ts index 1726e7bb9..ea5aa5c28 100644 --- a/apps/mobile/app/stores/use-setting-store.ts +++ b/apps/mobile/app/stores/use-setting-store.ts @@ -151,7 +151,6 @@ export interface SettingStore { inboxEnabled: boolean; setInboxEnabled: (inboxEnabled: boolean) => void; pendingShortcut: ShortcutItem | null; - pendingShortcutLoaded: boolean; } const { width, height } = Dimensions.get("window"); @@ -273,6 +272,5 @@ export const useSettingStore = create((set, get) => ({ }, inboxEnabled: false, setInboxEnabled: (inboxEnabled) => set({ inboxEnabled }), - pendingShortcut: null, - pendingShortcutLoaded: false + pendingShortcut: null })); From e4fa0e9f0b3ffff6eb1c7b6c4125418af4194000 Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Sat, 25 Jul 2026 08:54:44 +0500 Subject: [PATCH 10/11] mobile: fix ios app does not load if awaited RNBootSplash.hide --- apps/mobile/app/app.tsx | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/apps/mobile/app/app.tsx b/apps/mobile/app/app.tsx index 40d17fe49..96b8a6fe7 100644 --- a/apps/mobile/app/app.tsx +++ b/apps/mobile/app/app.tsx @@ -74,6 +74,7 @@ const App = (props: { configureMode: "note-preview" }) => { }, [introCompleted]); useEffect(() => { + RNBootSplash.hide({ fade: true }); SettingsService.onFirstLaunch(); changeSystemBarColors(); SettingsService.setPrivacyScreen( @@ -194,21 +195,24 @@ export const withStartupBoundry = ( useEffect(() => { async function init() { - const [url, shortcut] = await Promise.all([ - Linking.getInitialURL(), - Shortcuts.getInitialShortcut() - ]); - if (shortcut?.type === "notesnook.action.newnote") { - launchNewNoteTab(); - } - useSettingStore.setState({ - initialUrl: url, - pendingShortcut: shortcut ?? null - }); + try { + const [url, shortcut] = await Promise.all([ + Linking.getInitialURL(), + Shortcuts.getInitialShortcut() + ]); + console.log(url, shortcut); + if (shortcut?.type === "notesnook.action.newnote") { + launchNewNoteTab(); + } + useSettingStore.setState({ + initialUrl: url, + pendingShortcut: shortcut ?? null + }); - initShortcutListener(); - await RNBootSplash.hide({ fade: true }); - setReady(true); + initShortcutListener(); + } finally { + setReady(true); + } } init(); From 57113e089641c4c55425a7311536b1ca5ee18f11 Mon Sep 17 00:00:00 2001 From: kashaf-ansari-dev Date: Fri, 31 Jul 2026 09:49:36 +0500 Subject: [PATCH 11/11] mobile: remove AddReminder from stack on navigation away to prevent stale form state Signed-off-by: kashaf-ansari-dev --- apps/mobile/app/screens/add-reminder/index.tsx | 2 +- apps/mobile/app/services/navigation.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/mobile/app/screens/add-reminder/index.tsx b/apps/mobile/app/screens/add-reminder/index.tsx index e6cc813fa..4315c047e 100644 --- a/apps/mobile/app/screens/add-reminder/index.tsx +++ b/apps/mobile/app/screens/add-reminder/index.tsx @@ -288,7 +288,7 @@ export default function AddReminder(props: NavigationProps<"AddReminder">) { Notifications.scheduleNotification(_reminder as Reminder); Navigation.queueRoutesForUpdate(); useRelationStore.getState().update(); - Navigation.goBack(); + handleBackNavigation(); } catch (e) { ToastManager.error(e as Error, undefined); } diff --git a/apps/mobile/app/services/navigation.ts b/apps/mobile/app/services/navigation.ts index f3b4114d0..4bcc58eb9 100755 --- a/apps/mobile/app/services/navigation.ts +++ b/apps/mobile/app/services/navigation.ts @@ -177,9 +177,11 @@ function resetRootState( if (state.routes.length < 2) return; - const routes = state.routes.filter( + let routes = state.routes.filter( (route) => - (route.name !== "Auth" && route.name !== "Welcome") || + (route.name !== "Auth" && + route.name !== "Welcome" && + route.name !== "AddReminder") || route.key === focusedRoute.key );