From 5909025976529c16f0e413eabe32aec76265f201 Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Fri, 27 Mar 2026 11:38:51 +0500 Subject: [PATCH] mobile: add support for internal links --- .../android/app/src/main/AndroidManifest.xml | 9 +++++ .../notesnook/NotePreviewWidget.java | 2 +- .../notesnook/RCTNNativeModule.java | 2 +- .../app/components/properties/items.tsx | 1 + apps/mobile/app/hooks/use-actions.tsx | 37 ++++++++++++------- apps/mobile/app/hooks/use-app-events.tsx | 29 ++++++++++----- .../app/navigation/navigation-stack.tsx | 30 +++++++++++---- apps/mobile/ios/Notesnook/Info.plist | 1 + 8 files changed, 78 insertions(+), 33 deletions(-) diff --git a/apps/mobile/android/app/src/main/AndroidManifest.xml b/apps/mobile/android/app/src/main/AndroidManifest.xml index 58f3080cd..2635ab38c 100644 --- a/apps/mobile/android/app/src/main/AndroidManifest.xml +++ b/apps/mobile/android/app/src/main/AndroidManifest.xml @@ -155,6 +155,15 @@ + + + + + + + + + { - Clipboard.setString(createInternalLink("note", item.id)); - ToastManager.show({ - heading: strings.linkCopied(), - message: createInternalLink("note", item.id), - context: "local", - type: "success" - }); - } - }, { id: "reminders", title: strings.dataTypesPluralCamelCase.reminder(), @@ -1271,5 +1257,28 @@ export const useActions = ({ }); } + if ( + item.type === "tag" || + item.type === "note" || + item.type === "notebook" || + item.type === "color" + ) { + actions.push({ + id: "copy-link", + title: strings.copyLink(), + icon: "link", + onPress: () => { + const link = createInternalLink(item.type, item.id); + Clipboard.setString(link); + ToastManager.show({ + heading: strings.linkCopied(), + message: link, + context: "local", + type: "success" + }); + } + }); + } + return actions; }; diff --git a/apps/mobile/app/hooks/use-app-events.tsx b/apps/mobile/app/hooks/use-app-events.tsx index e2f0fb115..b9a6d7a86 100644 --- a/apps/mobile/app/hooks/use-app-events.tsx +++ b/apps/mobile/app/hooks/use-app-events.tsx @@ -25,7 +25,9 @@ import { SYNC_CHECK_IDS, SubscriptionPlan, SyncStatusEvent, - User + User, + isInternalLink, + parseInternalLink } from "@notesnook/core"; import { strings } from "@notesnook/intl"; import notifee from "@notifee/react-native"; @@ -169,6 +171,8 @@ const onAppOpenedFromURL = async (event: { }) => { const url = event.url; + const parsedLink = isInternalLink(url) ? parseInternalLink(url) : undefined; + try { if (url.startsWith("https://app.notesnook.com/account/verified")) { await onUserEmailVerified(); @@ -178,8 +182,12 @@ const onAppOpenedFromURL = async (event: { eSendEvent(eOnLoadNote, { newNote: true }); fluidTabsRef.current?.goToPage("editor", false); return; - } else if (url.startsWith("https://app.notesnook.com/open_note?")) { - const id = new URL(url).searchParams.get("id"); + } else if ( + parsedLink?.type === "note" || + url.startsWith("https://app.notesnook.com/open_note?") + ) { + const id = parsedLink?.id || new URL(url).searchParams.get("id"); + if (id) { const note = await db.notes.note(id); if (note) { @@ -191,10 +199,11 @@ const onAppOpenedFromURL = async (event: { } } } else if ( - url.startsWith("https://app.notesnook.com/open_notebook?") && + (parsedLink?.type === "notebook" || + url.startsWith("https://app.notesnook.com/open_notebook?")) && !event.isInitialUrl ) { - const id = new URL(url).searchParams.get("id"); + const id = parsedLink?.id || new URL(url).searchParams.get("id"); if (id) { const notebook = await db.notebooks.notebook(id); if (notebook) { @@ -206,10 +215,11 @@ const onAppOpenedFromURL = async (event: { } } } else if ( - url.startsWith("https://app.notesnook.com/open_tag?") && + (parsedLink?.type === "tag" || + url.startsWith("https://app.notesnook.com/open_tag?")) && !event.isInitialUrl ) { - const id = new URL(url).searchParams.get("id"); + const id = parsedLink?.id || new URL(url).searchParams.get("id"); if (id) { const tag = await db.tags.tag(id); if (tag) { @@ -222,10 +232,11 @@ const onAppOpenedFromURL = async (event: { } } } else if ( - url.startsWith("https://app.notesnook.com/open_color?") && + (parsedLink?.type === "color" || + url.startsWith("https://app.notesnook.com/open_color?")) && !event.isInitialUrl ) { - const id = new URL(url).searchParams.get("id"); + const id = parsedLink?.id || new URL(url).searchParams.get("id"); if (id) { const color = await db.colors.color(id); if (color) { diff --git a/apps/mobile/app/navigation/navigation-stack.tsx b/apps/mobile/app/navigation/navigation-stack.tsx index dc4139f6e..ffb89919b 100644 --- a/apps/mobile/app/navigation/navigation-stack.tsx +++ b/apps/mobile/app/navigation/navigation-stack.tsx @@ -31,6 +31,7 @@ import { useSettingStore } from "../stores/use-setting-store"; import { rootNavigatorRef } from "../utils/global-refs"; import Navigation from "../services/navigation"; import { isFeatureAvailable } from "@notesnook/common"; +import { isInternalLink, parseInternalLink } from "@notesnook/core"; const RootStack = createNativeStackNavigator(); const AppStack = createNativeStackNavigator(); @@ -63,10 +64,17 @@ const AppNavigation = React.memo( React.useEffect(() => { if (!home) { - if (useSettingStore.getState().initialUrl) { - const url = useSettingStore.getState().initialUrl; - if (url?.startsWith("https://app.notesnook.com/open_notebook?")) { - const id = new URL(url).searchParams.get("id"); + const url = useSettingStore.getState().initialUrl; + if (url) { + const parsedLink = isInternalLink(url) + ? parseInternalLink(url) + : undefined; + + if ( + parsedLink?.type === "notebook" || + url?.startsWith("https://app.notesnook.com/open_notebook?") + ) { + const id = parsedLink?.id || new URL(url).searchParams.get("id"); if (id) { setHome({ name: "Notebook", @@ -76,8 +84,11 @@ const AppNavigation = React.memo( }); return; } - } else if (url?.startsWith("https://app.notesnook.com/open_tag?")) { - const id = new URL(url).searchParams.get("id"); + } else if ( + parsedLink?.type === "tag" || + url?.startsWith("https://app.notesnook.com/open_tag?") + ) { + const id = parsedLink?.id || new URL(url).searchParams.get("id"); if (id) { setHome({ name: "TaggedNotes", @@ -88,8 +99,11 @@ const AppNavigation = React.memo( }); return; } - } else if (url?.startsWith("https://app.notesnook.com/open_color?")) { - const id = new URL(url).searchParams.get("id"); + } else if ( + parsedLink?.type === "color" || + url?.startsWith("https://app.notesnook.com/open_color?") + ) { + const id = parsedLink?.id || new URL(url).searchParams.get("id"); if (id) { setHome({ name: "ColoredNotes", diff --git a/apps/mobile/ios/Notesnook/Info.plist b/apps/mobile/ios/Notesnook/Info.plist index baeaa1347..49fac7dc9 100644 --- a/apps/mobile/ios/Notesnook/Info.plist +++ b/apps/mobile/ios/Notesnook/Info.plist @@ -52,6 +52,7 @@ CFBundleURLSchemes ShareMedia + nn