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