diff --git a/src/components/ProjectSidebarMenu.tsx b/src/components/ProjectSidebarMenu.tsx index 4b12fdb05..e8a2650a5 100644 --- a/src/components/ProjectSidebarMenu.tsx +++ b/src/components/ProjectSidebarMenu.tsx @@ -15,7 +15,7 @@ import { MachineManagerContext } from 'components/MachineManagerProvider' import usePlatform from 'hooks/usePlatform' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' import Tooltip from './Tooltip' -import { createFileLink } from 'lib/createFileLink' +import { createCreateFileUrl, createShortlink } from 'lib/links' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import toast from 'react-hot-toast' import { DEV, VITE_KC_PROD_TOKEN } from 'env' @@ -212,20 +212,21 @@ function ProjectMenuPopover({ }) return } - const shareUrl = await createFileLink(token, { + const shareUrl = createCreateFileUrl({ code: codeManager.code, name: project?.name || '', units: settings.context.modeling.defaultUnit.current, }) + const shortlink = await createShortlink(token, shareUrl.toString()) - if (err(shareUrl)) { - toast.error(shareUrl.message, { + if (err(shortlink)) { + toast.error(shortlink.message, { duration: 5000, }) return } - await globalThis.navigator.clipboard.writeText(shareUrl.url) + await globalThis.navigator.clipboard.writeText(shortlink.url) toast.success( 'Link copied to clipboard. Anyone who clicks this link will get a copy of this file. Share carefully!', { diff --git a/src/hooks/useCreateFileLinkQueryWatcher.ts b/src/hooks/useCreateFileLinkQueryWatcher.ts index 299f41b01..9a5b57f69 100644 --- a/src/hooks/useCreateFileLinkQueryWatcher.ts +++ b/src/hooks/useCreateFileLinkQueryWatcher.ts @@ -4,7 +4,7 @@ import { useEffect } from 'react' import { useSearchParams } from 'react-router-dom' import { useSettingsAuthContext } from './useSettingsAuthContext' import { isDesktop } from 'lib/isDesktop' -import { FileLinkParams } from 'lib/createFileLink' +import { FileLinkParams } from 'lib/links' import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig' import { baseUnitsUnion } from 'lib/settings/settingsTypes' diff --git a/src/lib/createFileLink.test.ts b/src/lib/createFileLink.test.ts deleted file mode 100644 index d8c6c52ed..000000000 --- a/src/lib/createFileLink.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CREATE_FILE_URL_PARAM } from './constants' -import { createFileLink } from './createFileLink' - -describe(`createFileLink`, () => { - test(`with simple code`, async () => { - const code = `extrusionDistance = 12` - const name = `test` - const units = `mm` - - // Converted with external online tools - const expectedEncodedCode = `ZXh0cnVzaW9uRGlzdGFuY2UgPSAxMg%3D%3D` - const expectedLink = `http:/localhost:3000/?${CREATE_FILE_URL_PARAM}&name=test&units=mm&code=${expectedEncodedCode}` - - const result = createFileLink({ code, name, units }) - expect(result).toBe(expectedLink) - }) -}) diff --git a/src/lib/links.test.ts b/src/lib/links.test.ts new file mode 100644 index 000000000..500a72a89 --- /dev/null +++ b/src/lib/links.test.ts @@ -0,0 +1,17 @@ +import { ASK_TO_OPEN_QUERY_PARAM, CREATE_FILE_URL_PARAM } from './constants' +import { createCreateFileUrl } from './links' + +describe(`link creation tests`, () => { + test(`createCreateFileUrl happy path`, async () => { + const code = `extrusionDistance = 12` + const name = `test` + const units = `mm` + + // Converted with external online tools + const expectedEncodedCode = `ZXh0cnVzaW9uRGlzdGFuY2UgPSAxMg%3D%3D` + const expectedLink = `http:/localhost:3000/?${CREATE_FILE_URL_PARAM}&name=test&units=mm&code=${expectedEncodedCode}&${ASK_TO_OPEN_QUERY_PARAM}` + + const result = createCreateFileUrl({ code, name, units }) + expect(result.toString()).toBe(expectedLink) + }) +}) diff --git a/src/lib/createFileLink.ts b/src/lib/links.ts similarity index 70% rename from src/lib/createFileLink.ts rename to src/lib/links.ts index cd5b9dd79..587f9023f 100644 --- a/src/lib/createFileLink.ts +++ b/src/lib/links.ts @@ -1,7 +1,6 @@ import { UnitLength_type } from '@kittycad/lib/dist/types/src/models' import { ASK_TO_OPEN_QUERY_PARAM, CREATE_FILE_URL_PARAM, PROD_APP_URL } from './constants' import { stringToBase64 } from './base64' -import { ZOO_STUDIO_PROTOCOL } from './links' import { DEV } from 'env' export interface FileLinkParams { code: string @@ -9,26 +8,38 @@ export interface FileLinkParams { units: UnitLength_type } +/** + * Creates a URL with the necessary query parameters to trigger + * the "Import file from URL" command in the app. + * + * With the additional step of asking the user if they want to + * open the URL in the desktop app. + */ +export function createCreateFileUrl({ code, name, units }: FileLinkParams) { + // Use the dev server if we are in development mode + let origin = DEV ? 'http://localhost:3000' : PROD_APP_URL + const searchParams = new URLSearchParams({ + [CREATE_FILE_URL_PARAM]: '', + name, + units, + code: stringToBase64(code), + [ASK_TO_OPEN_QUERY_PARAM]: '', + }) + const createFileUrl = new URL(`?${searchParams.toString()}`, origin) + + return createFileUrl +} + /** * Given a file's code, name, and units, creates shareable link to the * web app with a query parameter that triggers a modal to "open in desktop app". * That modal is defined in the `OpenInDesktopAppHandler` component. * TODO: update the return type to use TS library after its updated */ -export async function createFileLink( +export async function createShortlink( token: string, - { code, name, units }: FileLinkParams + url: string ): Promise { - // Use the dev server if we are in development mode - let origin = DEV ? 'http://localhost:3000' : PROD_APP_URL - - let urlFileToShare = new URL( - `?${CREATE_FILE_URL_PARAM}&name=${encodeURIComponent( - name - )}&units=${units}&code=${encodeURIComponent(stringToBase64(code))}&${ASK_TO_OPEN_QUERY_PARAM}`, - origin - ).toString() - /** * We don't use our `withBaseURL` function here because * there is no URL shortener service in the dev API. @@ -40,12 +51,11 @@ export async function createFileLink( Authorization: `Bearer ${token}`, }, body: JSON.stringify({ - url: urlFileToShare, + url, // 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}`)