diff --git a/Cargo.lock b/Cargo.lock index 11545f634..a5a16daf8 100644 Binary files a/Cargo.lock and b/Cargo.lock differ diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 8b689b3c8..cd777d3d4 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -6,6 +6,7 @@ import { createPortal } from 'react-dom'; import { RspcProvider } from '@sd/client'; import { createRoutes, + DeeplinkEvent, ErrorPage, KeybindEvent, PlatformProvider, @@ -17,6 +18,10 @@ import { RouteTitleContext } from '@sd/interface/hooks/useRouteTitle'; import '@sd/ui/style/style.scss'; +import SuperTokens from 'supertokens-web-js'; +import EmailPassword from 'supertokens-web-js/recipe/emailpassword'; +import Session from 'supertokens-web-js/recipe/session'; +import ThirdParty from 'supertokens-web-js/recipe/thirdparty'; // TODO: Bring this back once upstream is fixed up. // const client = hooks.createClient({ // links: [ @@ -26,10 +31,6 @@ import '@sd/ui/style/style.scss'; // tauriLink() // ] // }); -import SuperTokens from 'supertokens-web-js'; -import EmailPassword from 'supertokens-web-js/recipe/emailpassword'; -import Session from 'supertokens-web-js/recipe/session'; -import ThirdParty from 'supertokens-web-js/recipe/thirdparty'; import getCookieHandler from '@sd/interface/app/$libraryId/settings/client/account/handlers/cookieHandler'; import getWindowHandler from '@sd/interface/app/$libraryId/settings/client/account/handlers/windowHandler'; import { useLocale } from '@sd/interface/hooks'; @@ -67,18 +68,21 @@ export default function App() { const keybindListener = listen('keybind', (input) => { document.dispatchEvent(new KeybindEvent(input.payload as string)); }); - - return () => { - keybindListener.then((unlisten) => unlisten()); - }; - }, []); - - useEffect(() => { - const deeplinkListener = listen('deeplink', (data) => { - console.log('deeplink', data.payload); + const deeplinkListener = listen('deeplink', async (data) => { + const payload = (data.payload as any).data as string; + if (!payload) return; + const json = JSON.parse(payload)[0]; + if (!json) return; + //json output: "spacedrive://-/URL" + if (typeof json !== 'string') return; + if (!json.startsWith('spacedrive://-')) return; + const url = (json as string).split('://-/')[1]; + if (!url) return; + document.dispatchEvent(new DeeplinkEvent(url)); }); return () => { + keybindListener.then((unlisten) => unlisten()); deeplinkListener.then((unlisten) => unlisten()); }; }, []); diff --git a/interface/app/$libraryId/Layout/index.tsx b/interface/app/$libraryId/Layout/index.tsx index dd9e76c83..cf04f0865 100644 --- a/interface/app/$libraryId/Layout/index.tsx +++ b/interface/app/$libraryId/Layout/index.tsx @@ -15,6 +15,7 @@ import { useRootContext } from '~/app/RootContext'; import { LibraryIdParamsSchema } from '~/app/route-schemas'; import ErrorFallback, { BetterErrorBoundary } from '~/ErrorFallback'; import { + useDeeplinkEventHandler, useKeybindEventHandler, useOperatingSystem, useRedirectToNewLocation, @@ -40,6 +41,7 @@ const Layout = () => { const windowState = useWindowState(); useKeybindEventHandler(library?.uuid); + useDeeplinkEventHandler(); const layoutRef = useRef(null); diff --git a/interface/app/$libraryId/settings/Sidebar.tsx b/interface/app/$libraryId/settings/Sidebar.tsx index 08de4d525..9ff0c3496 100644 --- a/interface/app/$libraryId/settings/Sidebar.tsx +++ b/interface/app/$libraryId/settings/Sidebar.tsx @@ -37,7 +37,7 @@ export default () => { // const isPairingEnabled = useFeatureFlag('p2pPairing'); const isBackupsEnabled = useFeatureFlag('backups'); - const cloudSync = useFeatureFlag('cloudSync'); + // const cloudSync = useFeatureFlag('cloudSync'); const { t } = useLocale(); @@ -123,12 +123,12 @@ export default () => { Saved Searches */} - {cloudSync && ( + {/* {cloudSync && ( {t('sync')} - )} + )} */} {t('clouds')} diff --git a/interface/hooks/index.ts b/interface/hooks/index.ts index 6f2fbdf72..8d1b54ac1 100644 --- a/interface/hooks/index.ts +++ b/interface/hooks/index.ts @@ -31,3 +31,4 @@ export * from './useWindowState'; export * from './useZodParams'; export * from './useZodRouteParams'; export * from './useZodSearchParams'; +export * from './useDeeplinkEventHandler'; diff --git a/interface/hooks/useDeeplinkEventHandler.ts b/interface/hooks/useDeeplinkEventHandler.ts new file mode 100644 index 000000000..728241ee0 --- /dev/null +++ b/interface/hooks/useDeeplinkEventHandler.ts @@ -0,0 +1,20 @@ +import { useEffect } from "react"; +import { useNavigate } from "react-router"; +import { DeeplinkEvent } from "~/util/events"; + +export const useDeeplinkEventHandler = () => { + const navigate = useNavigate(); + useEffect(() => { + const handler = (e: DeeplinkEvent) => { + e.preventDefault(); + + const url = e.detail.url; + if (!url) return; + + navigate(url); + }; + + document.addEventListener('deeplink', handler); + return () => document.removeEventListener('deeplink', handler); + }, [navigate]); +} diff --git a/interface/hooks/useKeybindEventHandler.ts b/interface/hooks/useKeybindEventHandler.ts index 779689646..4cf1f20a6 100644 --- a/interface/hooks/useKeybindEventHandler.ts +++ b/interface/hooks/useKeybindEventHandler.ts @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router'; -import { KeybindEvent } from '../util/keybind'; +import { KeybindEvent } from '../util/events'; import { useQuickRescan } from './useQuickRescan'; import { getWindowState } from './useWindowState'; diff --git a/interface/index.tsx b/interface/index.tsx index 1d4d4a8a2..edef96f33 100644 --- a/interface/index.tsx +++ b/interface/index.tsx @@ -15,8 +15,6 @@ import { import { toast, TooltipProvider } from '@sd/ui'; import { createRoutes } from './app'; -import getCookieHandler from './app/$libraryId/settings/client/account/handlers/cookieHandler'; -import getWindowHandler from './app/$libraryId/settings/client/account/handlers/windowHandler'; import { SpacedropProvider } from './app/$libraryId/Spacedrop'; import i18n from './app/I18n'; import { Devtools } from './components/Devtools'; @@ -28,7 +26,7 @@ import { RouterContext, RoutingContext } from './RoutingContext'; export * from './app'; export { ErrorPage } from './ErrorFallback'; export * from './TabsContext'; -export * from './util/keybind'; +export * from './util/events'; export * from './util/Platform'; dayjs.extend(advancedFormat); diff --git a/interface/util/keybind.ts b/interface/util/events.ts similarity index 58% rename from interface/util/keybind.ts rename to interface/util/events.ts index 3500f3c50..4b1e1a77c 100644 --- a/interface/util/keybind.ts +++ b/interface/util/events.ts @@ -1,6 +1,7 @@ declare global { interface GlobalEventHandlersEventMap { keybindexec: KeybindEvent; + deeplink: DeeplinkEvent; } } @@ -13,3 +14,13 @@ export class KeybindEvent extends CustomEvent<{ action: string }> { }); } } + +export class DeeplinkEvent extends CustomEvent<{ url: string }> { + constructor(url: string) { + super('deeplink', { + detail: { + url + } + }); + } +} diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index fa3ba139d..330841c84 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -512,9 +512,10 @@ export type NodeConfigP2P = { discovery?: P2PDiscoveryState; port: Port; disable * * All of these are valid values: * - `localhost` - * - `otbeaumont.me` or `otbeaumont.me:3000` + * - `spacedrive.com` or `spacedrive.com:3000` * - `127.0.0.1` or `127.0.0.1:300` * - `[::1]` or `[::1]:3000` + * * which is why we use `String` not `SocketAddr` */ manual_peers?: string[] }