diff --git a/src/App.tsx b/src/App.tsx
index 7496f32eb..d50e54ba0 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -23,11 +23,24 @@ import { CoreDumpManager } from 'lib/coredump'
import { UnitsMenu } from 'components/UnitsMenu'
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
+import { useCommandsContext } from 'hooks/useCommandsContext'
export function App() {
const { project, file } = useLoaderData() as IndexLoaderData
+ const { commandBarSend } = useCommandsContext()
+
// Keep a lookout for a URL query string that invokes the 'import file from URL' command
- useCreateFileLinkQuery()
+ useCreateFileLinkQuery((argDefaultValues) => {
+ commandBarSend({
+ type: 'Find and select command',
+ data: {
+ groupId: 'projects',
+ name: 'Import file from URL',
+ argDefaultValues,
+ },
+ })
+ })
+
useRefreshSettings(PATHS.FILE + 'SETTINGS')
const navigate = useNavigate()
const filePath = useAbsoluteFilePath()
diff --git a/src/Router.tsx b/src/Router.tsx
index 647d607f3..5c2dd9f8d 100644
--- a/src/Router.tsx
+++ b/src/Router.tsx
@@ -43,6 +43,7 @@ import { useMemo } from 'react'
import { AppStateProvider } from 'AppState'
import { reportRejection } from 'lib/trap'
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
+import { ProtocolHandler } from 'components/ProtocolHandler'
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
@@ -53,19 +54,21 @@ const router = createRouter([
/* Make sure auth is the outermost provider or else we will have
* inefficient re-renders, use the react profiler to see. */
element: (
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
),
errorElement: ,
children: [
diff --git a/src/components/ProtocolHandler.tsx b/src/components/ProtocolHandler.tsx
new file mode 100644
index 000000000..b5842e2f2
--- /dev/null
+++ b/src/components/ProtocolHandler.tsx
@@ -0,0 +1,41 @@
+import { getSystemTheme } from 'lib/theme'
+import { ZOO_STUDIO_PROTOCOL } from 'lib/link'
+import { useState } from 'react'
+import { useCreateFileLinkQuery, CreateFileSchemaMethodOptional } from 'hooks/useCreateFileLinkQueryWatcher'
+import { isDesktop } from 'lib/isDesktop'
+
+export const ProtocolHandler = (props: { children: ReactNode } ) => {
+ const [hasCustomProtocolScheme, setHasCustomProtocolScheme] = useState(false)
+ const [hasAsked, setHasAsked] = useState(false)
+ useCreateFileLinkQuery((args) => {
+ if (hasAsked) return
+ window.location.href = `zoo-studio:${JSON.stringify(args)}`
+ setHasAsked(true)
+ setHasCustomProtocolScheme(true)
+ })
+
+ const continueToWebApp = () => {
+ setHasCustomProtocolScheme(false)
+ }
+
+ const pathLogomarkSvg = `${isDesktop() ? '.' : ''}/zma-logomark.svg`
+
+ return hasCustomProtocolScheme ?
: props.children
+}
diff --git a/src/hooks/useCreateFileLinkQueryWatcher.ts b/src/hooks/useCreateFileLinkQueryWatcher.ts
index b365ef962..505d3fdb2 100644
--- a/src/hooks/useCreateFileLinkQueryWatcher.ts
+++ b/src/hooks/useCreateFileLinkQueryWatcher.ts
@@ -1,24 +1,30 @@
import { base64ToString } from 'lib/base64'
import { CREATE_FILE_URL_PARAM, DEFAULT_FILE_NAME } from 'lib/constants'
import { useEffect } from 'react'
-import { useLocation, useSearchParams } from 'react-router-dom'
-import { useCommandsContext } from './useCommandsContext'
+import { useSearchParams } from 'react-router-dom'
import { useSettingsAuthContext } from './useSettingsAuthContext'
import { isDesktop } from 'lib/isDesktop'
import { FileLinkParams } from 'lib/createFileLink'
import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig'
import { baseUnitsUnion } from 'lib/settings/settingsTypes'
+// For initializing the command arguments, we actually want `method` to be undefined
+// so that we don't skip it in the command palette.
+export type CreateFileSchemaMethodOptional = Omit<
+ ProjectsCommandSchema['Import file from URL'],
+ 'method'
+> & {
+ method?: 'newProject' | 'existingProject'
+}
+
/**
* companion to createFileLink. This hook runs an effect on mount that
* checks the URL for the CREATE_FILE_URL_PARAM and triggers the "Create file"
* command if it is present, loading the command's default values from the other
* URL parameters.
*/
-export function useCreateFileLinkQuery() {
- const location = useLocation()
+export function useCreateFileLinkQuery(callback: (args: CreateFileSchemaMethodOptional) => void) {
const [searchParams] = useSearchParams()
- const { commandBarSend } = useCommandsContext()
const { settings } = useSettingsAuthContext()
useEffect(() => {
@@ -44,15 +50,6 @@ export function useCreateFileLinkQuery() {
}
console.log('createFileParam', { ...params, location })
- // For initializing the command arguments, we actually want `method` to be undefined
- // so that we don't skip it in the command palette.
- type CreateFileSchemaMethodOptional = Omit<
- ProjectsCommandSchema['Import file from URL'],
- 'method'
- > & {
- method?: 'newProject' | 'existingProject'
- }
-
const argDefaultValues: CreateFileSchemaMethodOptional = {
name: params.name
? isDesktop()
@@ -66,14 +63,7 @@ export function useCreateFileLinkQuery() {
method: isDesktop() ? undefined : 'existingProject',
}
- commandBarSend({
- type: 'Find and select command',
- data: {
- groupId: 'projects',
- name: 'Import file from URL',
- argDefaultValues,
- },
- })
+ callback(argDefaultValues)
}
}, [searchParams])
}
diff --git a/src/lib/createFileLink.ts b/src/lib/createFileLink.ts
index 2dc5b545e..ccde85eba 100644
--- a/src/lib/createFileLink.ts
+++ b/src/lib/createFileLink.ts
@@ -10,14 +10,32 @@ export interface FileLinkParams {
/**
* Given a file's code, name, and units, creates shareable link
- * TODO: make the app respect this link
*/
-export function createFileLink({ code, name, units }: FileLinkParams) {
+export async function createFileLink({ code, name, units }: FileLinkParams) {
+ const token = await getAndSyncStoredToken(input)
+ const urlUserShortlinks = withBaseURL('/user/shortlinks')
+
const origin = globalThis.window.location.origin
- return new URL(
+
+ if (!token && isDesktop()) return Promise.reject(new Error('No token found'))
+
+ let headers = {
+ 'Content-Type': 'application/json',
+ }
+ if (token) headers['Authorization'] = `Bearer ${token}`
+
+ const urlFileToShare = new URL(
`/?${CREATE_FILE_URL_PARAM}&name=${encodeURIComponent(
name
)}&units=${units}&code=${encodeURIComponent(stringToBase64(code))}`,
origin
- ).href
+ ).toString()
+
+ const resp = await fetch(urlUserShortlinks, {
+ headers,
+ body: JSON.stringify({ url: urlFileToShare }),
+ })
+ const shortlink = await resp.json()
+
+ return shortlink.url
}
diff --git a/src/lib/link.ts b/src/lib/link.ts
new file mode 100644
index 000000000..8c61e060d
--- /dev/null
+++ b/src/lib/link.ts
@@ -0,0 +1,3 @@
+export const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
+
+
diff --git a/src/main.ts b/src/main.ts
index eb5c6691a..65760af0c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -21,6 +21,7 @@ import minimist from 'minimist'
import getCurrentProjectFile from 'lib/getCurrentProjectFile'
import os from 'node:os'
import { reportRejection } from 'lib/trap'
+import { ZOO_STUDIO_PROTOCOL } from 'lib/link'
let mainWindow: BrowserWindow | null = null
@@ -52,9 +53,8 @@ if (require('electron-squirrel-startup')) {
app.quit()
}
-const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
-/// Register our application to handle all "zoo-studio://" protocols.
+/// Register our application to handle all "zoo-studio:" protocols.
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient(ZOO_STUDIO_PROTOCOL, process.execPath, [
diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx
index 8f51a3875..6883c00f9 100644
--- a/src/routes/Home.tsx
+++ b/src/routes/Home.tsx
@@ -28,8 +28,6 @@ import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
// This route only opens in the desktop context for now,
// as defined in Router.tsx, so we can use the desktop APIs and types.
const Home = () => {
- // Keep a lookout for a URL query string that invokes the 'import file from URL' command
- useCreateFileLinkQuery()
const { state, send } = useProjectsContext()
const { commandBarSend } = useCommandsContext()
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)