wip
This commit is contained in:
15
src/App.tsx
15
src/App.tsx
@ -23,11 +23,24 @@ import { CoreDumpManager } from 'lib/coredump'
|
|||||||
import { UnitsMenu } from 'components/UnitsMenu'
|
import { UnitsMenu } from 'components/UnitsMenu'
|
||||||
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
||||||
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
||||||
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
const { project, file } = useLoaderData() as IndexLoaderData
|
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
|
// 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')
|
useRefreshSettings(PATHS.FILE + 'SETTINGS')
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const filePath = useAbsoluteFilePath()
|
const filePath = useAbsoluteFilePath()
|
||||||
|
@ -43,6 +43,7 @@ import { useMemo } from 'react'
|
|||||||
import { AppStateProvider } from 'AppState'
|
import { AppStateProvider } from 'AppState'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
|
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
|
||||||
|
import { ProtocolHandler } from 'components/ProtocolHandler'
|
||||||
|
|
||||||
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
||||||
|
|
||||||
@ -53,19 +54,21 @@ const router = createRouter([
|
|||||||
/* Make sure auth is the outermost provider or else we will have
|
/* Make sure auth is the outermost provider or else we will have
|
||||||
* inefficient re-renders, use the react profiler to see. */
|
* inefficient re-renders, use the react profiler to see. */
|
||||||
element: (
|
element: (
|
||||||
<CommandBarProvider>
|
<ProtocolHandler>
|
||||||
<SettingsAuthProvider>
|
<CommandBarProvider>
|
||||||
<LspProvider>
|
<SettingsAuthProvider>
|
||||||
<ProjectsContextProvider>
|
<LspProvider>
|
||||||
<KclContextProvider>
|
<ProjectsContextProvider>
|
||||||
<AppStateProvider>
|
<KclContextProvider>
|
||||||
<Outlet />
|
<AppStateProvider>
|
||||||
</AppStateProvider>
|
<Outlet />
|
||||||
</KclContextProvider>
|
</AppStateProvider>
|
||||||
</ProjectsContextProvider>
|
</KclContextProvider>
|
||||||
</LspProvider>
|
</ProjectsContextProvider>
|
||||||
</SettingsAuthProvider>
|
</LspProvider>
|
||||||
</CommandBarProvider>
|
</SettingsAuthProvider>
|
||||||
|
</CommandBarProvider>
|
||||||
|
</ProtocolHandler>
|
||||||
),
|
),
|
||||||
errorElement: <ErrorPage />,
|
errorElement: <ErrorPage />,
|
||||||
children: [
|
children: [
|
||||||
|
41
src/components/ProtocolHandler.tsx
Normal file
41
src/components/ProtocolHandler.tsx
Normal file
@ -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 ? <div className="flex items-center justify-center h-full">
|
||||||
|
<div style={{
|
||||||
|
background: `url(${pathLogomarkSvg})`,
|
||||||
|
backgroundRepeat: 'repeat',
|
||||||
|
backgroundSize: '100%',
|
||||||
|
transform: 'rotate(45deg)',
|
||||||
|
filter: `brightness(${getSystemTheme() === 'light' ? 97 : 0}%)`,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
position: 'absolute',
|
||||||
|
}} className="flex items-center justify-center h-full"
|
||||||
|
></div>
|
||||||
|
<div className="flex items-center justify-center h-full" style={{ zIndex: 10 }}>
|
||||||
|
<div className="p-4 mx-auto border rounded rounded-tl-none shadow-lg bg-chalkboard-10 dark:bg-chalkboard-100 dark:border-chalkboard-70">
|
||||||
|
<span>Loading model into Zoo Design Studio, </span><a className="cursor-pointer" onClick={continueToWebApp}>or continue to the web app</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> : props.children
|
||||||
|
}
|
@ -1,24 +1,30 @@
|
|||||||
import { base64ToString } from 'lib/base64'
|
import { base64ToString } from 'lib/base64'
|
||||||
import { CREATE_FILE_URL_PARAM, DEFAULT_FILE_NAME } from 'lib/constants'
|
import { CREATE_FILE_URL_PARAM, DEFAULT_FILE_NAME } from 'lib/constants'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useLocation, useSearchParams } from 'react-router-dom'
|
import { useSearchParams } from 'react-router-dom'
|
||||||
import { useCommandsContext } from './useCommandsContext'
|
|
||||||
import { useSettingsAuthContext } from './useSettingsAuthContext'
|
import { useSettingsAuthContext } from './useSettingsAuthContext'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { FileLinkParams } from 'lib/createFileLink'
|
import { FileLinkParams } from 'lib/createFileLink'
|
||||||
import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig'
|
import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig'
|
||||||
import { baseUnitsUnion } from 'lib/settings/settingsTypes'
|
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
|
* 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"
|
* 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
|
* command if it is present, loading the command's default values from the other
|
||||||
* URL parameters.
|
* URL parameters.
|
||||||
*/
|
*/
|
||||||
export function useCreateFileLinkQuery() {
|
export function useCreateFileLinkQuery(callback: (args: CreateFileSchemaMethodOptional) => void) {
|
||||||
const location = useLocation()
|
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
const { commandBarSend } = useCommandsContext()
|
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -44,15 +50,6 @@ export function useCreateFileLinkQuery() {
|
|||||||
}
|
}
|
||||||
console.log('createFileParam', { ...params, location })
|
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 = {
|
const argDefaultValues: CreateFileSchemaMethodOptional = {
|
||||||
name: params.name
|
name: params.name
|
||||||
? isDesktop()
|
? isDesktop()
|
||||||
@ -66,14 +63,7 @@ export function useCreateFileLinkQuery() {
|
|||||||
method: isDesktop() ? undefined : 'existingProject',
|
method: isDesktop() ? undefined : 'existingProject',
|
||||||
}
|
}
|
||||||
|
|
||||||
commandBarSend({
|
callback(argDefaultValues)
|
||||||
type: 'Find and select command',
|
|
||||||
data: {
|
|
||||||
groupId: 'projects',
|
|
||||||
name: 'Import file from URL',
|
|
||||||
argDefaultValues,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [searchParams])
|
}, [searchParams])
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,32 @@ export interface FileLinkParams {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a file's code, name, and units, creates shareable link
|
* 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
|
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(
|
`/?${CREATE_FILE_URL_PARAM}&name=${encodeURIComponent(
|
||||||
name
|
name
|
||||||
)}&units=${units}&code=${encodeURIComponent(stringToBase64(code))}`,
|
)}&units=${units}&code=${encodeURIComponent(stringToBase64(code))}`,
|
||||||
origin
|
origin
|
||||||
).href
|
).toString()
|
||||||
|
|
||||||
|
const resp = await fetch(urlUserShortlinks, {
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({ url: urlFileToShare }),
|
||||||
|
})
|
||||||
|
const shortlink = await resp.json()
|
||||||
|
|
||||||
|
return shortlink.url
|
||||||
}
|
}
|
||||||
|
3
src/lib/link.ts
Normal file
3
src/lib/link.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@ import minimist from 'minimist'
|
|||||||
import getCurrentProjectFile from 'lib/getCurrentProjectFile'
|
import getCurrentProjectFile from 'lib/getCurrentProjectFile'
|
||||||
import os from 'node:os'
|
import os from 'node:os'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { ZOO_STUDIO_PROTOCOL } from 'lib/link'
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null
|
let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
@ -52,9 +53,8 @@ if (require('electron-squirrel-startup')) {
|
|||||||
app.quit()
|
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.defaultApp) {
|
||||||
if (process.argv.length >= 2) {
|
if (process.argv.length >= 2) {
|
||||||
app.setAsDefaultProtocolClient(ZOO_STUDIO_PROTOCOL, process.execPath, [
|
app.setAsDefaultProtocolClient(ZOO_STUDIO_PROTOCOL, process.execPath, [
|
||||||
|
@ -28,8 +28,6 @@ import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
|||||||
// This route only opens in the desktop context for now,
|
// This route only opens in the desktop context for now,
|
||||||
// as defined in Router.tsx, so we can use the desktop APIs and types.
|
// as defined in Router.tsx, so we can use the desktop APIs and types.
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
// Keep a lookout for a URL query string that invokes the 'import file from URL' command
|
|
||||||
useCreateFileLinkQuery()
|
|
||||||
const { state, send } = useProjectsContext()
|
const { state, send } = useProjectsContext()
|
||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarSend } = useCommandsContext()
|
||||||
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
|
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
|
||||||
|
Reference in New Issue
Block a user