Files
modeling-app/src/lib/exportSave.ts
Frank Noirot 66bbbf81e2 Pass current file name through to export command (#4503)
* Pass current file name through to export command

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* Oops I needed a couple other things, not just that one line change

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* Undo overriding of internal zipped file names
That was liable to cause conflicts and whatnot per @jessfraz feedback

* Update E2E test that was still looking for `output.gltf`

* Missed one other test my bad

* Should've just grepped for output.gltf to begin with

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Paul Tagliamonte <paul@zoo.dev>
2024-11-19 11:30:23 -05:00

99 lines
2.8 KiB
TypeScript

import { isDesktop } from './isDesktop'
import { deserialize_files } from '../wasm-lib/pkg/wasm_lib'
import { browserSaveFile } from './browserSaveFile'
import JSZip from 'jszip'
import ModelingAppFile from './modelingAppFile'
import toast from 'react-hot-toast'
import { EXPORT_TOAST_MESSAGES } from './constants'
const save_ = async (file: ModelingAppFile, toastId: string) => {
try {
if (isDesktop()) {
const extension = file.name.split('.').pop() || null
let extensions: string[] = []
if (extension !== null) {
extensions.push(extension)
}
if (window.electron.process.env.IS_PLAYWRIGHT) {
// skip file picker, save to default location
await window.electron.writeFile(
file.name,
new Uint8Array(file.contents)
)
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS, { id: toastId })
return
}
// Open a dialog to save the file.
const filePathMeta = await window.electron.save({
defaultPath: file.name,
filters: [
{
name: 'model',
extensions: extensions,
},
],
})
// The user canceled the save.
// Return early.
if (filePathMeta.canceled) {
toast.dismiss(toastId)
return
}
// Write the file.
await window.electron.writeFile(
filePathMeta.filePath,
new Uint8Array(file.contents)
)
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS, { id: toastId })
} else {
// Download the file to the user's computer.
// Now we need to download the files to the user's downloads folder.
// Or the destination they choose.
// Iterate over the files.
// Create a new blob.
const blob = new Blob([new Uint8Array(file.contents)])
// Save the file.
await browserSaveFile(blob, file.name, toastId)
}
} catch (e) {
// TODO: do something real with the error.
console.error('export error', e)
toast.error(EXPORT_TOAST_MESSAGES.FAILED, { id: toastId })
}
}
// Saves files locally from an export call.
// We override the file's name with one passed in from the client side.
export async function exportSave({
data,
fileName,
toastId,
}: {
data: ArrayBuffer
fileName: string
toastId: string
}) {
// This converts the ArrayBuffer to a Rust equivalent Vec<u8>.
let uintArray = new Uint8Array(data)
let files: ModelingAppFile[] = deserialize_files(uintArray)
if (files.length > 1) {
let zip = new JSZip()
for (const file of files) {
zip.file(file.name, new Uint8Array(file.contents), { binary: true })
}
return zip.generateAsync({ type: 'array' }).then((contents) => {
return save_({ name: `${fileName || 'output'}.zip`, contents }, toastId)
})
} else {
files[0].name = fileName || files[0].name
return save_(files[0], toastId)
}
}