diff --git a/src/components/create/file-upload.tsx b/src/components/create/file-upload.tsx new file mode 100644 index 0000000..bb1a0c9 --- /dev/null +++ b/src/components/create/file-upload.tsx @@ -0,0 +1,109 @@ +import { Client } from "@atcute/client"; +import { remove } from "@mary/exif-rm"; +import { createSignal, onCleanup, Show } from "solid-js"; +import { agent } from "../../auth/state"; +import { Button } from "../button.jsx"; +import { TextInput } from "../text-input.jsx"; +import { editorInstance } from "./state"; + +export const FileUpload = (props: { + file: File; + blobInput: HTMLInputElement; + onClose: () => void; +}) => { + const [uploading, setUploading] = createSignal(false); + const [error, setError] = createSignal(""); + + onCleanup(() => (props.blobInput.value = "")); + + const formatFileSize = (bytes: number) => { + if (bytes === 0) return "0 Bytes"; + const k = 1024; + const sizes = ["Bytes", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i]; + }; + + const uploadBlob = async () => { + let blob: Blob; + + const mimetype = (document.getElementById("mimetype") as HTMLInputElement)?.value; + (document.getElementById("mimetype") as HTMLInputElement).value = ""; + if (mimetype) blob = new Blob([props.file], { type: mimetype }); + else blob = props.file; + + if ((document.getElementById("exif-rm") as HTMLInputElement).checked) { + const exifRemoved = remove(new Uint8Array(await blob.arrayBuffer())); + if (exifRemoved !== null) blob = new Blob([exifRemoved], { type: blob.type }); + } + + const rpc = new Client({ handler: agent()! }); + setUploading(true); + const res = await rpc.post("com.atproto.repo.uploadBlob", { + input: blob, + }); + setUploading(false); + if (!res.ok) { + setError(res.data.error); + return; + } + editorInstance.view.dispatch({ + changes: { + from: editorInstance.view.state.selection.main.head, + insert: JSON.stringify(res.data.blob, null, 2), + }, + }); + props.onClose(); + }; + + return ( +
+ {props.file.name} + + ({formatFileSize(props.file.size)}) + +
++ Metadata will be pasted after the cursor +
+- {props.file.name} - - ({formatFileSize(props.file.size)}) - -
-- Metadata will be pasted after the cursor -
-