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 { 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()
 | 
			
		||||
 | 
			
		||||
@ -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: (
 | 
			
		||||
      <CommandBarProvider>
 | 
			
		||||
        <SettingsAuthProvider>
 | 
			
		||||
          <LspProvider>
 | 
			
		||||
            <ProjectsContextProvider>
 | 
			
		||||
              <KclContextProvider>
 | 
			
		||||
                <AppStateProvider>
 | 
			
		||||
                  <Outlet />
 | 
			
		||||
                </AppStateProvider>
 | 
			
		||||
              </KclContextProvider>
 | 
			
		||||
            </ProjectsContextProvider>
 | 
			
		||||
          </LspProvider>
 | 
			
		||||
        </SettingsAuthProvider>
 | 
			
		||||
      </CommandBarProvider>
 | 
			
		||||
      <ProtocolHandler>
 | 
			
		||||
        <CommandBarProvider>
 | 
			
		||||
          <SettingsAuthProvider>
 | 
			
		||||
            <LspProvider>
 | 
			
		||||
              <ProjectsContextProvider>
 | 
			
		||||
                <KclContextProvider>
 | 
			
		||||
                  <AppStateProvider>
 | 
			
		||||
                    <Outlet />
 | 
			
		||||
                  </AppStateProvider>
 | 
			
		||||
                </KclContextProvider>
 | 
			
		||||
              </ProjectsContextProvider>
 | 
			
		||||
            </LspProvider>
 | 
			
		||||
          </SettingsAuthProvider>
 | 
			
		||||
        </CommandBarProvider>
 | 
			
		||||
      </ProtocolHandler>
 | 
			
		||||
    ),
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
    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 { 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])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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 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, [
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user