Improved link handing in some apps
This commit is contained in:
parent
b03f725eb8
commit
207df87f89
11 changed files with 113 additions and 56 deletions
|
@ -7,6 +7,11 @@ type Bookmark = {
|
|||
};
|
||||
|
||||
export const bookmarks: Bookmark[] = [
|
||||
{
|
||||
icon: FAVICON_BASE_PATH,
|
||||
name: "daedalOS",
|
||||
url: "https://dustinbrett.com/",
|
||||
},
|
||||
{
|
||||
icon: "/System/Icons/Favicons/google.webp",
|
||||
name: "Google",
|
||||
|
@ -22,11 +27,6 @@ export const bookmarks: Bookmark[] = [
|
|||
name: "Internet Archive",
|
||||
url: "https://archive.org/",
|
||||
},
|
||||
{
|
||||
icon: FAVICON_BASE_PATH,
|
||||
name: "daedalOS",
|
||||
url: "https://dustinbrett.com/",
|
||||
},
|
||||
{
|
||||
icon: "/System/Icons/Favicons/win96.webp",
|
||||
name: "Windows 96",
|
||||
|
@ -37,11 +37,6 @@ export const bookmarks: Bookmark[] = [
|
|||
name: "AaronOS",
|
||||
url: "https://aaronos.dev/",
|
||||
},
|
||||
{
|
||||
icon: "/System/Icons/Favicons/diablo.webp",
|
||||
name: "Diablo",
|
||||
url: "https://d07riv.github.io/diabloweb/",
|
||||
},
|
||||
];
|
||||
|
||||
export const HOME_PAGE = "https://www.google.com/webhp?igu=1";
|
||||
|
|
|
@ -30,7 +30,7 @@ const Browser: FC<ComponentProcessProps> = ({ id }) => {
|
|||
processes: { [id]: process },
|
||||
} = useProcesses();
|
||||
const { prependFileToTitle } = useTitle(id);
|
||||
const { url = "" } = process || {};
|
||||
const { initialTitle = "", url = "" } = process || {};
|
||||
const initialUrl = url || HOME_PAGE;
|
||||
const { canGoBack, canGoForward, history, moveHistory, position } =
|
||||
useHistory(initialUrl, id);
|
||||
|
@ -67,7 +67,7 @@ const Browser: FC<ComponentProcessProps> = ({ id }) => {
|
|||
if (addressUrl.startsWith(GOOGLE_SEARCH_QUERY)) {
|
||||
prependFileToTitle(`${addressInput} - Google Search`);
|
||||
} else {
|
||||
const { name = "" } =
|
||||
const { name = initialTitle } =
|
||||
bookmarks?.find(
|
||||
({ url: bookmarkUrl }) => bookmarkUrl === addressInput
|
||||
) || {};
|
||||
|
@ -76,7 +76,7 @@ const Browser: FC<ComponentProcessProps> = ({ id }) => {
|
|||
}
|
||||
|
||||
if (addressInput.startsWith("ipfs://")) {
|
||||
setIcon(id, "/System/Icons/Favicons/ipfs.webp");
|
||||
setIcon(id, "/System/Icons/Favicons/ipfs.png");
|
||||
} else {
|
||||
const favicon = new Image();
|
||||
const faviconUrl = `${
|
||||
|
@ -105,7 +105,7 @@ const Browser: FC<ComponentProcessProps> = ({ id }) => {
|
|||
}
|
||||
}
|
||||
},
|
||||
[exists, id, prependFileToTitle, readFile, setIcon]
|
||||
[exists, id, initialTitle, prependFileToTitle, readFile, setIcon]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -4,7 +4,8 @@ import { type ContainerHookProps } from "components/system/Apps/AppContainer";
|
|||
import useTitle from "components/system/Window/useTitle";
|
||||
import { useFileSystem } from "contexts/fileSystem";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import { haltEvent, isYouTubeUrl, loadFiles } from "utils/functions";
|
||||
import { loadFiles } from "utils/functions";
|
||||
import { useLinkHandler } from "hooks/useLinkHandler";
|
||||
|
||||
type MarkedOptions = {
|
||||
headerIds?: boolean;
|
||||
|
@ -30,7 +31,8 @@ const useMarked = ({
|
|||
}: ContainerHookProps): void => {
|
||||
const { readFile } = useFileSystem();
|
||||
const { prependFileToTitle } = useTitle(id);
|
||||
const { open, processes: { [id]: { libs = [] } = {} } = {} } = useProcesses();
|
||||
const { processes: { [id]: { libs = [] } = {} } = {} } = useProcesses();
|
||||
const openLink = useLinkHandler();
|
||||
const loadFile = useCallback(async () => {
|
||||
const markdownFile = await readFile(url);
|
||||
const container = containerRef.current?.querySelector(
|
||||
|
@ -43,22 +45,23 @@ const useMarked = ({
|
|||
headerIds: false,
|
||||
})
|
||||
);
|
||||
container.querySelectorAll("a").forEach((link) =>
|
||||
link.addEventListener("click", (event) => {
|
||||
haltEvent(event);
|
||||
|
||||
if (isYouTubeUrl(link.href)) {
|
||||
open("VideoPlayer", { url: link.href });
|
||||
} else {
|
||||
window.open(link.href, "_blank", "noopener, noreferrer");
|
||||
}
|
||||
})
|
||||
);
|
||||
container
|
||||
.querySelectorAll("a")
|
||||
.forEach((link) =>
|
||||
link.addEventListener("click", (event) =>
|
||||
openLink(
|
||||
event,
|
||||
link.href || "",
|
||||
link.pathname,
|
||||
link.textContent || ""
|
||||
)
|
||||
)
|
||||
);
|
||||
container.scrollTop = 0;
|
||||
}
|
||||
|
||||
prependFileToTitle(basename(url));
|
||||
}, [containerRef, open, prependFileToTitle, readFile, url]);
|
||||
}, [containerRef, openLink, prependFileToTitle, readFile, url]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
|
|
|
@ -20,3 +20,18 @@ export const setReadOnlyMode = (editor: Editor): void => {
|
|||
|
||||
editor.mode.set("readonly");
|
||||
};
|
||||
|
||||
const allowedCorsDomains = new Set(["wikipedia.org", "archive.org"]);
|
||||
|
||||
export const isCorsUrl = (url?: string): boolean => {
|
||||
if (!url) return false;
|
||||
|
||||
try {
|
||||
const { hostname } = new URL(url);
|
||||
const [, domain, tld] = hostname.split(".");
|
||||
|
||||
return allowedCorsDomains.has(`${domain}.${tld}`);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { basename, dirname, extname, relative } from "path";
|
||||
import { basename, dirname, extname } from "path";
|
||||
import { type Editor, type NotificationSpec } from "tinymce";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { DEFAULT_SAVE_PATH, config } from "components/apps/TinyMCE/config";
|
||||
|
@ -8,17 +8,15 @@ import {
|
|||
} from "components/apps/TinyMCE/functions";
|
||||
import { type IRTFJS } from "components/apps/TinyMCE/types";
|
||||
import { type ContainerHookProps } from "components/system/Apps/AppContainer";
|
||||
import {
|
||||
getModifiedTime,
|
||||
getProcessByFileExtension,
|
||||
} from "components/system/Files/FileEntry/functions";
|
||||
import { getModifiedTime } from "components/system/Files/FileEntry/functions";
|
||||
import useFileDrop from "components/system/Files/FileManager/useFileDrop";
|
||||
import useTitle from "components/system/Window/useTitle";
|
||||
import { useFileSystem } from "contexts/fileSystem";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import { useSession } from "contexts/session";
|
||||
import { DEFAULT_LOCALE } from "utils/constants";
|
||||
import { getExtension, haltEvent, loadFiles } from "utils/functions";
|
||||
import { getExtension, loadFiles } from "utils/functions";
|
||||
import { useLinkHandler } from "hooks/useLinkHandler";
|
||||
|
||||
type OptionSetter = <K, T>(name: K, value: T) => void;
|
||||
|
||||
|
@ -28,11 +26,8 @@ const useTinyMCE = ({
|
|||
setLoading,
|
||||
url,
|
||||
}: ContainerHookProps): void => {
|
||||
const {
|
||||
open,
|
||||
processes: { [id]: { libs = [] } = {} } = {},
|
||||
url: setUrl,
|
||||
} = useProcesses();
|
||||
const { processes: { [id]: { libs = [] } = {} } = {}, url: setUrl } =
|
||||
useProcesses();
|
||||
const [editor, setEditor] = useState<Editor>();
|
||||
const { prependFileToTitle } = useTitle(id);
|
||||
const { readFile, stat, updateFolder, writeFile } = useFileSystem();
|
||||
|
@ -53,31 +48,25 @@ const useTinyMCE = ({
|
|||
},
|
||||
[prependFileToTitle, stat]
|
||||
);
|
||||
const openLink = useLinkHandler();
|
||||
const linksToProcesses = useCallback(() => {
|
||||
const iframe = containerRef.current?.querySelector("iframe");
|
||||
|
||||
if (iframe?.contentWindow) {
|
||||
[...iframe.contentWindow.document.links].forEach((link) =>
|
||||
link.addEventListener("click", (event) => {
|
||||
const mceHref = link.dataset.mceHref || "";
|
||||
const isRelative =
|
||||
relative(
|
||||
mceHref.startsWith("/") ? mceHref : `/${mceHref}`,
|
||||
link.pathname
|
||||
) === "";
|
||||
if (isRelative && editor?.mode.isReadOnly()) {
|
||||
haltEvent(event);
|
||||
|
||||
const defaultProcess = getProcessByFileExtension(
|
||||
getExtension(link.pathname)
|
||||
if (editor?.mode.isReadOnly()) {
|
||||
openLink(
|
||||
event,
|
||||
link.dataset.mceHref || "",
|
||||
link.pathname,
|
||||
link.textContent || ""
|
||||
);
|
||||
|
||||
if (defaultProcess) open(defaultProcess, { url: link.pathname });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [containerRef, editor?.mode, open]);
|
||||
}, [containerRef, editor?.mode, openLink]);
|
||||
const loadFile = useCallback(async () => {
|
||||
if (editor) {
|
||||
setReadOnlyMode(editor);
|
||||
|
|
|
@ -26,8 +26,8 @@ const directory: Processes = {
|
|||
Component: dynamic(() => import("components/apps/Browser")),
|
||||
backgroundColor: "#FFF",
|
||||
defaultSize: {
|
||||
height: 480,
|
||||
width: 640,
|
||||
height: 500,
|
||||
width: 600,
|
||||
},
|
||||
icon: "/System/Icons/chromium.webp",
|
||||
title: "Browser",
|
||||
|
|
|
@ -6,6 +6,10 @@ import {
|
|||
} from "components/system/Dialogs/Transfer/useTransferDialog";
|
||||
import { type Size } from "components/system/Window/RndWindow/useResizable";
|
||||
|
||||
type BrowserProcessArguments = {
|
||||
initialTitle?: string;
|
||||
};
|
||||
|
||||
type DialogProcessArguments = {
|
||||
fileReaders?: FileReaders | ObjectReaders;
|
||||
progress?: number;
|
||||
|
@ -47,6 +51,7 @@ type BaseProcessArguments = {
|
|||
};
|
||||
|
||||
export type ProcessArguments = BaseProcessArguments &
|
||||
BrowserProcessArguments &
|
||||
DialogProcessArguments &
|
||||
MonacoProcessArguments &
|
||||
PdfProcessArguments;
|
||||
|
|
47
hooks/useLinkHandler.ts
Normal file
47
hooks/useLinkHandler.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { relative } from "path";
|
||||
import { useCallback } from "react";
|
||||
import { isCorsUrl } from "components/apps/TinyMCE/functions";
|
||||
import { getProcessByFileExtension } from "components/system/Files/FileEntry/functions";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import { haltEvent, isYouTubeUrl, getExtension } from "utils/functions";
|
||||
|
||||
type LinkHandler = (
|
||||
event: Event,
|
||||
url: string,
|
||||
pathName: string,
|
||||
title?: string
|
||||
) => void;
|
||||
|
||||
export const useLinkHandler = (): LinkHandler => {
|
||||
const { open } = useProcesses();
|
||||
|
||||
return useCallback(
|
||||
(event: Event, url: string, pathName: string, title?: string) => {
|
||||
haltEvent(event);
|
||||
|
||||
if (isYouTubeUrl(url)) open("VideoPlayer", { url });
|
||||
else if (isCorsUrl(url)) open("Browser", { initialTitle: title, url });
|
||||
else if (
|
||||
!pathName ||
|
||||
relative(
|
||||
decodeURI(
|
||||
(url.startsWith("/") ? url : `/${url}`).replace(
|
||||
window.location.origin,
|
||||
""
|
||||
)
|
||||
),
|
||||
decodeURI(pathName)
|
||||
) === ""
|
||||
) {
|
||||
const defaultProcess = getProcessByFileExtension(
|
||||
getExtension(pathName)
|
||||
);
|
||||
|
||||
if (defaultProcess) open(defaultProcess, { url: decodeURI(pathName) });
|
||||
} else {
|
||||
window.open(url, "_blank", "noopener, noreferrer");
|
||||
}
|
||||
},
|
||||
[open]
|
||||
);
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 196 B |
Binary file not shown.
Before Width: | Height: | Size: 142 B |
|
@ -760,7 +760,10 @@ export const label = (value: string): React.HTMLAttributes<HTMLElement> => ({
|
|||
});
|
||||
|
||||
export const isYouTubeUrl = (url: string): boolean =>
|
||||
url.includes("youtube.com/") || url.includes("youtu.be/");
|
||||
(url.includes("youtube.com/") || url.includes("youtu.be/")) &&
|
||||
!url.includes("youtube.com/@") &&
|
||||
!url.includes("/channel/") &&
|
||||
!url.includes("/c/");
|
||||
|
||||
export const getYouTubeUrlId = (url: string): string => {
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue