Get primary user flow working on desktop

This commit is contained in:
Frank Noirot
2025-01-08 16:56:48 -05:00
parent 5cbd11cec8
commit ca09224c92
9 changed files with 93 additions and 61 deletions

View File

@ -8,3 +8,5 @@ VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
#VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"
# Add a prod token if you want to use the share URL feature in local dev
#VITE_KC_PROD_TOKEN="your token from prod.zoo.dev should go in .env.development.local"

1
interface.d.ts vendored
View File

@ -62,6 +62,7 @@ export interface IElectronAPI {
TEST_SETTINGS_FILE_KEY: string
IS_PLAYWRIGHT: string
VITE_KC_DEV_TOKEN: string
VITE_KC_PROD_TOKEN: string
VITE_KC_API_WS_MODELING_URL: string
VITE_KC_API_BASE_URL: string
VITE_KC_SITE_BASE_URL: string

View File

@ -47,7 +47,6 @@ import { AppStateProvider } from 'AppState'
import { reportRejection } from 'lib/trap'
import { RouteProvider } from 'components/RouteProvider'
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
import { ProtocolHandler } from 'components/ProtocolHandler'
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
@ -59,7 +58,6 @@ 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: (
<ProtocolHandler>
<CommandBarProvider>
<RouteProvider>
<SettingsAuthProvider>
@ -77,7 +75,6 @@ const router = createRouter([
</SettingsAuthProvider>
</RouteProvider>
</CommandBarProvider>
</ProtocolHandler>
),
errorElement: <ErrorPage />,
children: [

View File

@ -18,6 +18,8 @@ import Tooltip from './Tooltip'
import { createFileLink } from 'lib/createFileLink'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import toast from 'react-hot-toast'
import { DEV, VITE_KC_PROD_TOKEN } from 'env'
import { err } from 'lib/trap'
const ProjectSidebarMenu = ({
project,
@ -189,21 +191,41 @@ function ProjectMenuPopover({
Element: 'button',
children: 'Share link to file',
onClick: async () => {
if (!auth.context.token) {
/**
* We don't have a dev shortlink API service,
* so we need to hit the prod API even in local dev.
* This override allows us to shim in an environment variable
* for the prod token.
*/
const token = DEV ? VITE_KC_PROD_TOKEN : auth.context.token
if (DEV && !VITE_KC_PROD_TOKEN) {
toast.error(
'You need to set a prod token in your environment to share a file in development.',
{
duration: 5000,
}
)
return
} else if (!token) {
toast.error('You need to be signed in to share a file.', {
duration: 5000,
})
return
}
const shareUrl = await createFileLink(auth.context.token, {
const shareUrl = await createFileLink(token, {
code: codeManager.code,
name: file?.name || '',
name: project?.name || '',
units: settings.context.modeling.defaultUnit.current,
})
console.log(shareUrl)
if (err(shareUrl)) {
toast.error(shareUrl.message, {
duration: 5000,
})
return
}
await globalThis.navigator.clipboard.writeText(shareUrl)
await globalThis.navigator.clipboard.writeText(shareUrl.url)
toast.success(
'Link copied to clipboard. Anyone who clicks this link will get a copy of this file. Share carefully!',
{

View File

@ -14,6 +14,7 @@ export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined
export const VITE_KC_CONNECTION_TIMEOUT_MS =
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined
export const VITE_KC_DEV_TOKEN = env.VITE_KC_DEV_TOKEN as string | undefined
export const VITE_KC_PROD_TOKEN = env.VITE_KC_PROD_TOKEN as string | undefined
export const PROD = env.PROD as string | undefined
export const TEST = env.TEST as string | undefined
export const DEV = env.DEV as string | undefined

View File

@ -1,10 +1,7 @@
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
import { postUserShortlink } from 'lib/desktop'
import { CREATE_FILE_URL_PARAM } from './constants'
import { stringToBase64 } from './base64'
import withBaseURL from 'lib/withBaseURL'
import { isDesktop } from 'lib/isDesktop'
import { ZOO_STUDIO_PROTOCOL } from './link'
export interface FileLinkParams {
code: string
name: string
@ -13,41 +10,44 @@ export interface FileLinkParams {
/**
* Given a file's code, name, and units, creates shareable link
* TODO: update the return type to use TS library after its updated
*/
export async function createFileLink(
token: string,
{ code, name, units }: FileLinkParams
) {
let urlUserShortlinks = withBaseURL('/users/shortlinks')
): Promise<Error | { key: string; url: string }> {
// During development, the "handler" needs to first be the web app version,
// which exists on localhost:3000 typically.
let origin = 'http://localhost:3000'
let urlFileToShare = new URL(
`/?${CREATE_FILE_URL_PARAM}&name=${encodeURIComponent(
`?${CREATE_FILE_URL_PARAM}&name=${encodeURIComponent(
name
)}&units=${units}&code=${encodeURIComponent(stringToBase64(code))}`,
origin
).toString()
// Remove this monkey patching
function fixTheBrokenShitUntilItsFixedOnDev() {
urlUserShortlinks = urlUserShortlinks.replace(
'https://api.dev.zoo.dev',
'https://api.zoo.dev'
)
console.log(urlUserShortlinks)
}
fixTheBrokenShitUntilItsFixedOnDev()
return await fetch(urlUserShortlinks, {
/**
* We don't use our `withBaseURL` function here because
* there is no URL shortener service in the dev API.
*/
const response = await fetch('https://api.zoo.dev/user/shortlinks', {
method: 'POST',
headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ url: urlFileToShare }),
}).then((resp) => resp.json())
body: JSON.stringify({
url: urlFileToShare,
// In future we can support org-scoped and password-protected shortlinks here
// https://zoo.dev/docs/api/shortlinks/create-a-shortlink-for-a-user?lang=typescript
}),
})
console.log('response', response)
if (!response.ok) {
const error = await response.json()
return new Error(`Failed to create shortlink: ${error.message}`)
} else {
return response.json()
}
}

View File

@ -1 +1 @@
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio:'

View File

@ -67,7 +67,7 @@ if (process.defaultApp) {
// Must be done before ready event.
registerStartupListeners()
const createWindow = (filePath?: string, reuse?: boolean): BrowserWindow => {
const createWindow = (pathToOpen?: string, reuse?: boolean): BrowserWindow => {
let newWindow
if (reuse) {
@ -92,12 +92,27 @@ const createWindow = (filePath?: string, reuse?: boolean): BrowserWindow => {
})
}
const pathIsCustomProtocolLink = pathToOpen?.startsWith(ZOO_STUDIO_PROTOCOL) ?? false
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
newWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL).catch(reportRejection)
const filteredPath = pathToOpen ? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL, '')) : ''
const fullHashBasedUrl = `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/#/${filteredPath}`
newWindow.loadURL(fullHashBasedUrl).catch(reportRejection)
} else {
console.log('Loading from file', filePath)
getProjectPathAtStartup(filePath)
if (pathIsCustomProtocolLink && pathToOpen) {
// We're trying to open a custom protocol link
const filteredPath = pathToOpen ? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL, '')) : ''
const startIndex = path.join(
__dirname,
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`
)
newWindow.loadFile(startIndex, {
hash: filteredPath,
}).catch(reportRejection)
} else {
// otherwise we're trying to open a local file from the command line
getProjectPathAtStartup(pathToOpen)
.then(async (projectPath) => {
const startIndex = path.join(
__dirname,
@ -109,8 +124,6 @@ const createWindow = (filePath?: string, reuse?: boolean): BrowserWindow => {
return
}
console.log('Loading file', projectPath)
const fullUrl = `/file/${encodeURIComponent(projectPath)}`
console.log('Full URL', fullUrl)
@ -120,6 +133,7 @@ const createWindow = (filePath?: string, reuse?: boolean): BrowserWindow => {
})
.catch(reportRejection)
}
}
// Open the DevTools.
// mainWindow.webContents.openDevTools()
@ -467,12 +481,6 @@ function registerStartupListeners() {
) {
event.preventDefault()
console.log('open-url', url)
fs.writeFileSync(
'/Users/frankjohnson/open-url.txt',
`at ${new Date().toLocaleTimeString()} opened url: ${url}`
)
// If we have a mainWindow, lets open another window.
if (mainWindow) {
createWindow(url)

View File

@ -188,6 +188,7 @@ contextBridge.exposeInMainWorld('electron', {
'VITE_KC_SKIP_AUTH',
'VITE_KC_CONNECTION_TIMEOUT_MS',
'VITE_KC_DEV_TOKEN',
'VITE_KC_PROD_TOKEN',
'IS_PLAYWRIGHT',
// Really we shouldn't use these and our code should use NODE_ENV