import { Client } from "@atcute/client"; import { remove } from "@mary/exif-rm"; import { useNavigate, useParams } from "@solidjs/router"; import { createSignal, onCleanup, Show } from "solid-js"; import { Editor, editorView } from "../components/editor.jsx"; import { agent } from "../components/login.jsx"; import { setNotif } from "../layout.jsx"; import { Button } from "./button.jsx"; import { Modal } from "./modal.jsx"; import { TextInput } from "./text-input.jsx"; import Tooltip from "./tooltip.jsx"; export const RecordEditor = (props: { create: boolean; record?: any; refetch?: any }) => { const navigate = useNavigate(); const params = useParams(); const [openDialog, setOpenDialog] = createSignal(false); const [notice, setNotice] = createSignal(""); const [openUpload, setOpenUpload] = createSignal(false); let blobInput!: HTMLInputElement; let formRef!: HTMLFormElement; const placeholder = () => { return { $type: "app.bsky.feed.post", text: "This post was sent from PDSls", embed: { $type: "app.bsky.embed.external", external: { uri: "https://pdsls.dev", title: "PDSls", description: "Browse the public data on atproto", }, }, langs: ["en"], createdAt: new Date().toISOString(), }; }; const createRecord = async (formData: FormData) => { const rpc = new Client({ handler: agent()! }); const collection = formData.get("collection"); const rkey = formData.get("rkey"); const validate = formData.get("validate")?.toString(); let record: any; try { record = JSON.parse(editorView.state.doc.toString()); } catch (e: any) { setNotice(e.message); return; } const res = await rpc.post("com.atproto.repo.createRecord", { input: { repo: agent()!.sub, collection: collection ? collection.toString() : record.$type, rkey: rkey?.toString().length ? rkey?.toString() : undefined, record: record, validate: validate === "true" ? true : validate === "false" ? false : undefined, }, }); if (!res.ok) { setNotice(`${res.data.error}: ${res.data.message}`); return; } setOpenDialog(false); setNotif({ show: true, icon: "lucide--file-check", text: "Record created" }); navigate(`/${res.data.uri}`); }; const editRecord = async (formData: FormData) => { const record = editorView.state.doc.toString(); const validate = formData.get("validate")?.toString() === "true" ? true : formData.get("validate")?.toString() === "false" ? false : undefined; if (!record) return; const rpc = new Client({ handler: agent()! }); try { const editedRecord = JSON.parse(record); if (formData.get("recreate")) { const res = await rpc.post("com.atproto.repo.applyWrites", { input: { repo: agent()!.sub, validate: validate, writes: [ { collection: params.collection as `${string}.${string}.${string}`, rkey: params.rkey, $type: "com.atproto.repo.applyWrites#delete", }, { collection: params.collection as `${string}.${string}.${string}`, rkey: params.rkey, $type: "com.atproto.repo.applyWrites#create", value: editedRecord, }, ], }, }); if (!res.ok) { setNotice(`${res.data.error}: ${res.data.message}`); return; } } else { const res = await rpc.post("com.atproto.repo.putRecord", { input: { repo: agent()!.sub, collection: params.collection as `${string}.${string}.${string}`, rkey: params.rkey, record: editedRecord, validate: validate, }, }); if (!res.ok) { setNotice(`${res.data.error}: ${res.data.message}`); return; } } setOpenDialog(false); setNotif({ show: true, icon: "lucide--file-check", text: "Record edited" }); props.refetch(); } catch (err: any) { setNotice(err.message); } }; const FileUpload = (props: { file: File }) => { const [uploading, setUploading] = createSignal(false); const [error, setError] = createSignal(""); onCleanup(() => (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; } editorView.dispatch({ changes: { from: editorView.state.selection.main.head, insert: JSON.stringify(res.data.blob, null, 2), }, }); setOpenUpload(false); }; return (
{props.file.name} ({formatFileSize(props.file.size)})
Metadata will be pasted after the cursor