forked from pdsls.dev/pdsls
atproto explorer

create record new form

juli.ee 58e82dd2 f6ebca6f

verified
Changed files
+71 -48
src
components
+2 -1
src/components/account.tsx
··· 14 14 import { agent, Login, retrieveSession, Sessions, setAgent } from "./login.jsx"; 15 15 import { Modal } from "./modal.jsx"; 16 16 17 + export const [sessions, setSessions] = createStore<Sessions>(); 18 + 17 19 export const AccountManager = () => { 18 20 const [openManager, setOpenManager] = createSignal(false); 19 - const [sessions, setSessions] = createStore<Sessions>(); 20 21 const [avatars, setAvatars] = createStore<Record<Did, string>>(); 21 22 22 23 onMount(async () => {
+69 -47
src/components/create.tsx
··· 1 1 import { Client } from "@atcute/client"; 2 + import { Did } from "@atcute/lexicons"; 3 + import { getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 2 4 import { remove } from "@mary/exif-rm"; 3 5 import { useNavigate, useParams } from "@solidjs/router"; 4 - import { createSignal, onCleanup, onMount, Show } from "solid-js"; 6 + import { createSignal, For, onCleanup, onMount, Show } from "solid-js"; 5 7 import { Editor, editorView } from "../components/editor.jsx"; 6 8 import { agent } from "../components/login.jsx"; 7 9 import { setNotif } from "../layout.jsx"; 10 + import { sessions } from "./account.jsx"; 8 11 import { Button } from "./button.jsx"; 9 12 import { Modal } from "./modal.jsx"; 10 13 import { TextInput } from "./text-input.jsx"; ··· 18 21 const [openDialog, setOpenDialog] = createSignal(false); 19 22 const [notice, setNotice] = createSignal(""); 20 23 const [openUpload, setOpenUpload] = createSignal(false); 24 + const [validate, setValidate] = createSignal<boolean | undefined>(undefined); 21 25 let blobInput!: HTMLInputElement; 22 26 let formRef!: HTMLFormElement; 23 27 ··· 36 40 langs: ["en"], 37 41 createdAt: new Date().toISOString(), 38 42 }; 43 + }; 44 + 45 + const getValidateIcon = () => { 46 + return ( 47 + validate() === true ? "lucide--circle-check" 48 + : validate() === false ? "lucide--circle-x" 49 + : "lucide--circle" 50 + ); 51 + }; 52 + 53 + const getValidateLabel = () => { 54 + return ( 55 + validate() === true ? "True" 56 + : validate() === false ? "False" 57 + : "Unset" 58 + ); 39 59 }; 40 60 41 61 const createRecord = async (formData: FormData) => { 42 - const rpc = new Client({ handler: agent()! }); 62 + const repo = formData.get("repo")?.toString(); 63 + if (!repo) return; 64 + const rpc = new Client({ handler: new OAuthUserAgent(await getSession(repo as Did)) }); 43 65 const collection = formData.get("collection"); 44 66 const rkey = formData.get("rkey"); 45 - const validate = formData.get("validate")?.toString(); 46 67 let record: any; 47 68 try { 48 69 record = JSON.parse(editorView.state.doc.toString()); ··· 52 73 } 53 74 const res = await rpc.post("com.atproto.repo.createRecord", { 54 75 input: { 55 - repo: agent()!.sub, 76 + repo: repo as Did, 56 77 collection: collection ? collection.toString() : record.$type, 57 78 rkey: rkey?.toString().length ? rkey?.toString() : undefined, 58 79 record: record, 59 - validate: 60 - validate === "true" ? true 61 - : validate === "false" ? false 62 - : undefined, 80 + validate: validate(), 63 81 }, 64 82 }); 65 83 if (!res.ok) { ··· 71 89 navigate(`/${res.data.uri}`); 72 90 }; 73 91 74 - const editRecord = async (formData: FormData, recreate?: boolean) => { 92 + const editRecord = async (recreate?: boolean) => { 75 93 const record = editorView.state.doc.toString(); 76 - const validate = 77 - formData.get("validate")?.toString() === "true" ? true 78 - : formData.get("validate")?.toString() === "false" ? false 79 - : undefined; 80 94 if (!record) return; 81 95 const rpc = new Client({ handler: agent()! }); 82 96 try { ··· 85 99 const res = await rpc.post("com.atproto.repo.applyWrites", { 86 100 input: { 87 101 repo: agent()!.sub, 88 - validate: validate, 102 + validate: validate(), 89 103 writes: [ 90 104 { 91 105 collection: params.collection as `${string}.${string}.${string}`, ··· 112 126 collection: params.collection as `${string}.${string}.${string}`, 113 127 rkey: params.rkey, 114 128 record: editedRecord, 115 - validate: validate, 129 + validate: validate(), 116 130 }, 117 131 }); 118 132 if (!res.ok) { ··· 326 340 </div> 327 341 <form ref={formRef} class="flex flex-col gap-y-2"> 328 342 <div class="flex w-fit flex-col gap-y-1 text-sm"> 329 - <Show when={props.create}> 330 - <div class="flex items-center gap-x-2"> 331 - <label for="collection" class="min-w-20 select-none"> 332 - Collection 333 - </label> 343 + <div class="flex flex-wrap items-center gap-1"> 344 + <Show when={props.create}> 345 + <span>at://</span> 346 + <select 347 + class="dark:bg-dark-100 dark:shadow-dark-700 max-w-[10rem] truncate rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs select-none focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 348 + name="repo" 349 + id="repo" 350 + > 351 + <For each={Object.keys(sessions)}> 352 + {(session) => ( 353 + <option value={session} selected={session === agent()?.sub}> 354 + {sessions[session].handle ?? session} 355 + </option> 356 + )} 357 + </For> 358 + </select> 359 + <span>/</span> 334 360 <TextInput 335 361 id="collection" 336 362 name="collection" 337 363 placeholder="Optional (default: $type)" 338 - class="w-[15rem]" 364 + class="w-[10rem] placeholder:text-xs lg:w-[13rem]" 339 365 /> 340 - </div> 341 - <div class="flex items-center gap-x-2"> 342 - <label for="rkey" class="min-w-20 select-none"> 343 - Record key 344 - </label> 366 + <span>/</span> 345 367 <TextInput 346 368 id="rkey" 347 369 name="rkey" 348 370 placeholder="Optional (default: TID)" 349 - class="w-[15rem]" 371 + class="w-[10rem] placeholder:text-xs lg:w-[13rem]" 350 372 /> 351 - </div> 352 - </Show> 353 - <div class="flex items-center gap-x-2"> 354 - <label for="validate" class="min-w-20 select-none"> 355 - Validate 356 - </label> 357 - <select 358 - name="validate" 359 - id="validate" 360 - class="dark:bg-dark-100 dark:shadow-dark-700 rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs select-none focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 373 + </Show> 374 + </div> 375 + <div class="flex items-center gap-1"> 376 + <button 377 + type="button" 378 + class="flex items-center gap-1 rounded-sm p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 379 + onClick={() => 380 + setValidate( 381 + validate() === true ? false 382 + : validate() === false ? undefined 383 + : true, 384 + ) 385 + } 361 386 > 362 - <option value="unset">Unset</option> 363 - <option value="true">True</option> 364 - <option value="false">False</option> 365 - </select> 387 + <Tooltip text={getValidateLabel()}> 388 + <span class={`iconify text-base ${getValidateIcon()}`}></span> 389 + </Tooltip> 390 + <span>Validate</span> 391 + </button> 366 392 </div> 367 393 </div> 368 394 <Editor ··· 406 432 </Modal> 407 433 <div class="flex items-center justify-end gap-2"> 408 434 <Show when={!props.create}> 409 - <Button onClick={() => editRecord(new FormData(formRef), true)}> 410 - Recreate 411 - </Button> 435 + <Button onClick={() => editRecord(true)}>Recreate</Button> 412 436 </Show> 413 437 <Button 414 438 onClick={() => 415 - props.create ? 416 - createRecord(new FormData(formRef)) 417 - : editRecord(new FormData(formRef)) 439 + props.create ? createRecord(new FormData(formRef)) : editRecord() 418 440 } 419 441 > 420 442 {props.create ? "Create" : "Edit"}