mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 10:09:26 +02:00
desktop: add support for opening file links (#9784)
* desktop: add support for opening file links Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * web: unescape paths with spaces * web: wrap message in confirm dialog Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * desktop: show message box if path doesn't exist when opening file link Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * desktop: disable opening file links on flatpak Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * desktop: wrap long file paths in electron dialog * on linux, long messages in dialog aren't wrapped, so we need to wrap manually Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> * desktop: fix persistent "path not found" error * intl: update strings --------- Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
This commit is contained in:
@@ -33,7 +33,7 @@ import { AutoLaunch } from "../utils/autolaunch";
|
||||
import { config, DesktopIntegration } from "../utils/config";
|
||||
import { bringToFront } from "../utils/bring-to-front";
|
||||
import { getTheme, setTheme, Theme } from "../utils/theme";
|
||||
import { mkdirSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
||||
import { dirname } from "path";
|
||||
import { resolvePath } from "../utils/resolve-path";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
@@ -43,6 +43,7 @@ import { setupDesktopIntegration } from "../utils/desktop-integration";
|
||||
import { rm } from "fs/promises";
|
||||
import { disableCustomDns, enableCustomDns } from "../utils/custom-dns";
|
||||
import type { MenuItem as NNMenuItem } from "@notesnook/ui";
|
||||
import { platform } from "os";
|
||||
|
||||
const t = initTRPC.create();
|
||||
|
||||
@@ -192,9 +193,28 @@ export const osIntegrationRouter = t.router({
|
||||
}),
|
||||
openPath: t.procedure
|
||||
.input(z.object({ type: z.literal("path"), link: z.string() }))
|
||||
.query(({ input }) => {
|
||||
.query(async ({ input }) => {
|
||||
if (isFlatpak()) return;
|
||||
|
||||
const { type, link } = input;
|
||||
if (type === "path") return shell.openPath(resolvePath(link));
|
||||
if (type !== "path") return;
|
||||
|
||||
const resolvedPath = resolvePath(
|
||||
// remove leading slash from path on windows
|
||||
platform() === "win32" ? link.slice(1) : link
|
||||
);
|
||||
if (!existsSync(resolvedPath)) {
|
||||
if (globalThis.window) {
|
||||
await dialog.showMessageBox(globalThis.window, {
|
||||
type: "error",
|
||||
title: "Path not found",
|
||||
message: `The path does not exist:\n${wrapPath(resolvedPath)}`
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await shell.openPath(resolvedPath);
|
||||
}),
|
||||
bringToFront: t.procedure.query(() => bringToFront()),
|
||||
changeTheme: t.procedure
|
||||
@@ -299,3 +319,7 @@ function toMenuItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function wrapPath(path: string, maxLineLength = 100): string {
|
||||
return path.replace(new RegExp(`(.{${maxLineLength}})`, "g"), "$1\n");
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import { useStore as useThemeStore } from "../../stores/theme-store";
|
||||
import { writeToClipboard } from "../../utils/clipboard";
|
||||
import { useEditorStore } from "../../stores/editor-store";
|
||||
import { DayFormat, parseInternalLink } from "@notesnook/core";
|
||||
import { desktop } from "../../common/desktop-bridge";
|
||||
import Skeleton from "react-loading-skeleton";
|
||||
import useMobile from "../../hooks/use-mobile";
|
||||
import useTablet from "../../hooks/use-tablet";
|
||||
@@ -68,6 +69,7 @@ import { showFeatureNotAllowedToast } from "../../common/toasts";
|
||||
import { UpgradeDialog } from "../../dialogs/buy-dialog/upgrade-dialog";
|
||||
import { ConfirmDialog } from "../../dialogs/confirm";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { showToast } from "../../utils/toast";
|
||||
|
||||
export type OnChangeHandler = (
|
||||
content: () => string,
|
||||
@@ -421,13 +423,32 @@ function TipTap(props: TipTapProps) {
|
||||
previewAttachment: onPreviewAttachment,
|
||||
createInternalLink: onInsertInternalLink,
|
||||
getAttachmentData: onGetAttachmentData,
|
||||
openLink: (url, openInNewTab) => {
|
||||
openLink: async (url, openInNewTab) => {
|
||||
const link = parseInternalLink(url);
|
||||
if (link && link.type === "note") {
|
||||
useEditorStore.getState().openSession(link.id, {
|
||||
activeBlockId: link.params?.blockId || undefined,
|
||||
openInNewTab: openInNewTab
|
||||
});
|
||||
} else if (url.startsWith("file:")) {
|
||||
if (!IS_DESKTOP_APP) {
|
||||
showToast("error", strings.cantOpenFileLinksInBrowsers());
|
||||
return;
|
||||
}
|
||||
|
||||
const path = new URL(url).pathname;
|
||||
const ok = await ConfirmDialog.show({
|
||||
title: strings.openingLocalFile(),
|
||||
message: strings.openingLocalFileDesc(path),
|
||||
positiveButtonText: strings.open(),
|
||||
negativeButtonText: strings.cancel()
|
||||
});
|
||||
if (!ok) return;
|
||||
|
||||
await desktop?.integration.openPath.query({
|
||||
type: "path",
|
||||
link: decodeURIComponent(path)
|
||||
});
|
||||
} else window.open(url, "_blank");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,6 +123,7 @@ export const ConfirmDialog = DialogManager.register(function ConfirmDialog(
|
||||
<Text
|
||||
as="div"
|
||||
variant="body"
|
||||
sx={{ overflowWrap: "break-word" }}
|
||||
dangerouslySetInnerHTML={{ __html: mdToHtml(message) }}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -948,6 +948,10 @@ msgstr "Are you sure you want to logout and clear all data stored on THIS DEVICE
|
||||
msgid "Are you sure you want to logout from this device? Any unsynced changes will be lost."
|
||||
msgstr "Are you sure you want to logout from this device? Any unsynced changes will be lost."
|
||||
|
||||
#: src/strings.ts:2687
|
||||
msgid "Are you sure you want to open this file: {filePath}?"
|
||||
msgstr "Are you sure you want to open this file: {filePath}?"
|
||||
|
||||
#: src/strings.ts:1046
|
||||
msgid "Are you sure you want to remove your name?"
|
||||
msgstr "Are you sure you want to remove your name?"
|
||||
@@ -2933,6 +2937,10 @@ msgstr "File length is 0. Please upload this file again from the attachment mana
|
||||
msgid "File length mismatch. Expected {expectedSize} but got {currentSize} bytes. Please upload this file again from the attachment manager."
|
||||
msgstr "File length mismatch. Expected {expectedSize} but got {currentSize} bytes. Please upload this file again from the attachment manager."
|
||||
|
||||
#: src/strings.ts:2689
|
||||
msgid "File links cannot be opened in browsers. Please use the Notesnook desktop app."
|
||||
msgstr "File links cannot be opened in browsers. Please use the Notesnook desktop app."
|
||||
|
||||
#: src/strings.ts:949
|
||||
msgid "File mismatch"
|
||||
msgstr "File mismatch"
|
||||
@@ -4517,6 +4525,10 @@ msgstr "Open source."
|
||||
msgid "Open the two-factor authentication (TOTP) app to view your authentication code."
|
||||
msgstr "Open the two-factor authentication (TOTP) app to view your authentication code."
|
||||
|
||||
#: src/strings.ts:2685
|
||||
msgid "Opening local file"
|
||||
msgstr "Opening local file"
|
||||
|
||||
#: src/strings.ts:1857
|
||||
msgid "Optional"
|
||||
msgstr "Optional"
|
||||
|
||||
@@ -948,6 +948,10 @@ msgstr ""
|
||||
msgid "Are you sure you want to logout from this device? Any unsynced changes will be lost."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2687
|
||||
msgid "Are you sure you want to open this file: {filePath}?"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1046
|
||||
msgid "Are you sure you want to remove your name?"
|
||||
msgstr ""
|
||||
@@ -2922,6 +2926,10 @@ msgstr ""
|
||||
msgid "File length mismatch. Expected {expectedSize} but got {currentSize} bytes. Please upload this file again from the attachment manager."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2689
|
||||
msgid "File links cannot be opened in browsers. Please use the Notesnook desktop app."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:949
|
||||
msgid "File mismatch"
|
||||
msgstr ""
|
||||
@@ -4491,6 +4499,10 @@ msgstr ""
|
||||
msgid "Open the two-factor authentication (TOTP) app to view your authentication code."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2685
|
||||
msgid "Opening local file"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1857
|
||||
msgid "Optional"
|
||||
msgstr ""
|
||||
|
||||
@@ -2681,5 +2681,10 @@ Continue without attachments?`,
|
||||
encrypting: () => t`Encrypting`,
|
||||
fileSizeLimitExceededPleaseUpgrade: () =>
|
||||
t`File size limit exceeded. Please upgrade your plan.`,
|
||||
compressionFailed: () => t`Compression failed`
|
||||
compressionFailed: () => t`Compression failed`,
|
||||
openingLocalFile: () => t`Opening local file`,
|
||||
openingLocalFileDesc: (filePath: string) =>
|
||||
t`Are you sure you want to open this file: ${filePath}?`,
|
||||
cantOpenFileLinksInBrowsers: () =>
|
||||
t`File links cannot be opened in browsers. Please use the Notesnook desktop app.`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user