import { Client } from "@atcute/client"; import { remove } from "@mary/exif-rm"; import { useNavigate, useParams } from "@solidjs/router"; import { createSignal, 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 [uploading, setUploading] = createSignal(false); 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 uploadBlob = async () => { setNotice(""); let blob: Blob; const file = (document.getElementById("blob") as HTMLInputElement)?.files?.[0]; if (!file) return; const mimetype = (document.getElementById("mimetype") as HTMLInputElement)?.value; (document.getElementById("mimetype") as HTMLInputElement).value = ""; if (mimetype) blob = new Blob([file], { type: mimetype }); else blob = 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); (document.getElementById("blob") as HTMLInputElement).value = ""; if (!res.ok) { setNotice(res.data.error); return; } editorView.dispatch({ changes: { from: editorView.state.selection.main.head, insert: JSON.stringify(res.data.blob, null, 2), }, }); }; return ( <> setOpenDialog(false)} closeOnClick={false}> {props.create ? "Creating" : "Editing"} record setOpenDialog(false)} class="flex items-center"> Collection Record key Validate Unset True False uploadBlob()} /> Upload Metadata will be pasted after the cursor Uploading... MIME type Remove EXIF data {notice()} Recreate record props.create ? createRecord(new FormData(formRef)) : editRecord(new FormData(formRef)) } > {props.create ? "Create" : "Edit"} { setNotice(""); setOpenDialog(true); }} > > ); };
Metadata will be pasted after the cursor
Uploading...