kopia lustrzana https://github.com/Tldraw/Tldraw
155 wiersze
4.1 KiB
TypeScript
155 wiersze
4.1 KiB
TypeScript
import { fileOpen, fileSave } from 'browser-fs-access'
|
|
import type { FileSystemHandle } from 'browser-fs-access'
|
|
import { get as getFromIdb, set as setToIdb } from 'idb-keyval'
|
|
import { IMAGE_EXTENSIONS, VIDEO_EXTENSIONS } from '~constants'
|
|
import type { TDDocument, TDFile } from '~types'
|
|
|
|
const options = { mode: 'readwrite' as const }
|
|
|
|
const checkPermissions = async (handle: FileSystemFileHandle) => {
|
|
return (
|
|
(await (handle as unknown as FileSystemHandle).queryPermission(options)) === 'granted' ||
|
|
(await (handle as unknown as FileSystemHandle).requestPermission(options)) === 'granted'
|
|
)
|
|
}
|
|
|
|
export async function loadFileHandle() {
|
|
if (typeof Window === 'undefined' || !('_location' in Window)) return
|
|
const fileHandle = await getFromIdb(`Tldraw_file_handle_${window.location.origin}`)
|
|
if (!fileHandle) return null
|
|
return fileHandle
|
|
}
|
|
|
|
export async function saveFileHandle(fileHandle: FileSystemFileHandle | null) {
|
|
return setToIdb(`Tldraw_file_handle_${window.location.origin}`, fileHandle)
|
|
}
|
|
|
|
export async function saveToFileSystem(
|
|
document: TDDocument,
|
|
fileHandle: FileSystemFileHandle | null
|
|
) {
|
|
// Create the saved file data
|
|
const file: TDFile = {
|
|
name: document.name || 'New Document',
|
|
fileHandle: fileHandle ?? null,
|
|
document,
|
|
assets: {},
|
|
}
|
|
|
|
// Serialize to JSON
|
|
const json = JSON.stringify(file, null, 2)
|
|
|
|
// Create blob
|
|
const blob = new Blob([json], {
|
|
type: 'application/vnd.Tldraw+json',
|
|
})
|
|
|
|
if (fileHandle) {
|
|
const hasPermissions = await checkPermissions(fileHandle)
|
|
if (!hasPermissions) return null
|
|
}
|
|
|
|
// Save to file system
|
|
const newFileHandle = await fileSave(
|
|
blob,
|
|
{
|
|
fileName: `${file.name}.tldr`,
|
|
description: 'Tldraw File',
|
|
extensions: [`.tldr`],
|
|
},
|
|
fileHandle
|
|
)
|
|
|
|
await saveFileHandle(newFileHandle)
|
|
|
|
// Return true
|
|
return newFileHandle
|
|
}
|
|
|
|
export async function openFromFileSystem(): Promise<null | {
|
|
fileHandle: FileSystemFileHandle | null
|
|
document: TDDocument
|
|
}> {
|
|
// Get the blob
|
|
const blob = await fileOpen({
|
|
description: 'Tldraw File',
|
|
extensions: [`.tldr`],
|
|
multiple: false,
|
|
})
|
|
|
|
if (!blob) return null
|
|
|
|
// Get JSON from blob
|
|
const json: string = await new Promise((resolve) => {
|
|
const reader = new FileReader()
|
|
reader.onloadend = () => {
|
|
if (reader.readyState === FileReader.DONE) {
|
|
resolve(reader.result as string)
|
|
}
|
|
}
|
|
reader.readAsText(blob, 'utf8')
|
|
})
|
|
|
|
// Parse
|
|
const file: TDFile = JSON.parse(json)
|
|
|
|
const fileHandle = blob.handle ?? null
|
|
|
|
await saveFileHandle(fileHandle)
|
|
|
|
return {
|
|
fileHandle,
|
|
document: file.document,
|
|
}
|
|
}
|
|
|
|
export async function openAssetsFromFileSystem() {
|
|
return fileOpen({
|
|
description: 'Image or Video',
|
|
extensions: [...IMAGE_EXTENSIONS, ...VIDEO_EXTENSIONS],
|
|
multiple: true,
|
|
})
|
|
}
|
|
|
|
export function fileToBase64(file: Blob): Promise<string | ArrayBuffer | null> {
|
|
return new Promise((resolve, reject) => {
|
|
if (file) {
|
|
const reader = new FileReader()
|
|
reader.readAsDataURL(file)
|
|
reader.onload = () => resolve(reader.result)
|
|
reader.onerror = (error) => reject(error)
|
|
reader.onabort = (error) => reject(error)
|
|
}
|
|
})
|
|
}
|
|
|
|
export function fileToText(file: Blob): Promise<string | ArrayBuffer | null> {
|
|
return new Promise((resolve, reject) => {
|
|
if (file) {
|
|
const reader = new FileReader()
|
|
reader.readAsText(file)
|
|
reader.onload = () => resolve(reader.result)
|
|
reader.onerror = (error) => reject(error)
|
|
reader.onabort = (error) => reject(error)
|
|
}
|
|
})
|
|
}
|
|
|
|
export function getImageSizeFromSrc(src: string): Promise<number[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image()
|
|
img.onload = () => resolve([img.width, img.height])
|
|
img.onerror = () => reject(new Error('Could not get image size'))
|
|
img.src = src
|
|
})
|
|
}
|
|
|
|
export function getVideoSizeFromSrc(src: string): Promise<number[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const video = document.createElement('video')
|
|
video.onloadedmetadata = () => resolve([video.videoWidth, video.videoHeight])
|
|
video.onerror = () => reject(new Error('Could not get video size'))
|
|
video.src = src
|
|
})
|
|
}
|