forked from pdsls.dev/pdsls
atproto explorer

add create/edit/delete notif

Changed files
+35 -22
src
+3
src/components/create.tsx
··· 10 10 import { TextInput } from "./text-input.jsx"; 11 11 import { Modal } from "./modal.jsx"; 12 12 import { Button } from "./button.jsx"; 13 + import { setNotif } from "../layout.jsx"; 13 14 14 15 export const RecordEditor = (props: { create: boolean; record?: any; refetch?: any }) => { 15 16 const navigate = useNavigate(); ··· 66 67 return; 67 68 } 68 69 setOpenDialog(false); 70 + setNotif({ show: true, icon: "lucide--file-check", text: "Record created" }); 69 71 navigate(`/${res.data.uri}`); 70 72 }; 71 73 ··· 119 121 } 120 122 } 121 123 setOpenDialog(false); 124 + setNotif({ show: true, icon: "lucide--file-check", text: "Record edited" }); 122 125 props.refetch(); 123 126 } catch (err: any) { 124 127 setNotice(err.message);
+18 -5
src/layout.tsx
··· 1 - import { createEffect, ErrorBoundary, Show, Suspense } from "solid-js"; 1 + import { createEffect, createSignal, ErrorBoundary, Show, Suspense } from "solid-js"; 2 2 import { A, RouteSectionProps, useLocation, useNavigate, useParams } from "@solidjs/router"; 3 3 import { agent } from "./components/login.jsx"; 4 4 import { RecordEditor } from "./components/create.jsx"; ··· 10 10 import { Meta, MetaProvider } from "@solidjs/meta"; 11 11 import { Settings } from "./components/settings.jsx"; 12 12 import { Handle } from "@atcute/lexicons"; 13 - import { copyNotice } from "./utils/copy.js"; 13 + 14 + export const [notif, setNotif] = createSignal<{ 15 + show: boolean; 16 + icon?: string; 17 + text?: string; 18 + }>({ show: false }); 14 19 15 20 const Layout = (props: RouteSectionProps<unknown>) => { 16 21 const params = useParams(); 17 22 const location = useLocation(); 18 23 const navigate = useNavigate(); 24 + let timeout: number; 19 25 20 26 createEffect(async () => { 21 27 if (params.repo && !params.repo.startsWith("did:")) { 22 28 const did = await resolveHandle(params.repo as Handle); 23 29 navigate(location.pathname.replace(params.repo, did)); 30 + } 31 + }); 32 + 33 + createEffect(() => { 34 + if (notif().show) { 35 + clearTimeout(timeout); 36 + timeout = setTimeout(() => setNotif({ show: false }), 3000); 24 37 } 25 38 }); 26 39 ··· 80 93 </ErrorBoundary> 81 94 </Show> 82 95 </div> 83 - <Show when={copyNotice()}> 96 + <Show when={notif().show}> 84 97 <div class="dark:shadow-dark-900/80 dark:bg-dark-100/70 fixed bottom-10 z-50 flex items-center rounded-lg border-[0.5px] border-neutral-300 bg-neutral-100/70 p-2 shadow-md backdrop-blur-xs dark:border-neutral-700"> 85 - <span class="iconify lucide--clipboard-check mr-1"></span> 86 - Copied to clipboard 98 + <span class={`iconify ${notif().icon} mr-1`}></span> 99 + {notif().text} 87 100 </div> 88 101 </Show> 89 102 </div>
+2 -8
src/utils/copy.ts
··· 1 - import { createSignal } from "solid-js"; 2 - 3 - export const [copyNotice, setCopyNotice] = createSignal(false); 4 - 5 - let timeout: number; 1 + import { setNotif } from "../layout"; 6 2 7 3 export const addToClipboard = (text: string) => { 8 4 navigator.clipboard.writeText(text); 9 - setCopyNotice(true); 10 - clearTimeout(timeout); 11 - timeout = setTimeout(() => setCopyNotice(false), 3000); 5 + setNotif({ show: true, icon: "lucide--clipboard-check", text: "Copied to clipboard" }); 12 6 };
+10 -9
src/views/collection.tsx
··· 12 12 import { ComAtprotoRepoApplyWrites, ComAtprotoRepoGetRecord } from "@atcute/atproto"; 13 13 import { TextInput } from "../components/text-input.jsx"; 14 14 import { Button } from "../components/button.jsx"; 15 + import { setNotif } from "../layout.jsx"; 15 16 16 17 interface AtprotoRecord { 17 18 rkey: string; ··· 106 107 const [response, { refetch }] = createResource(fetchRecords); 107 108 108 109 const deleteRecords = async () => { 109 - const writes = records 110 - .filter((record) => record.toDelete) 111 - .map((record): $type.enforce<ComAtprotoRepoApplyWrites.Delete> => { 112 - return { 113 - $type: "com.atproto.repo.applyWrites#delete", 114 - collection: params.collection as `${string}.${string}.${string}`, 115 - rkey: record.rkey, 116 - }; 117 - }); 110 + const recsToDel = records.filter((record) => record.toDelete); 111 + const writes = recsToDel.map((record): $type.enforce<ComAtprotoRepoApplyWrites.Delete> => { 112 + return { 113 + $type: "com.atproto.repo.applyWrites#delete", 114 + collection: params.collection as `${string}.${string}.${string}`, 115 + rkey: record.rkey, 116 + }; 117 + }); 118 118 119 119 const BATCHSIZE = 200; 120 120 rpc = new Client({ handler: agent()! }); ··· 126 126 }, 127 127 }); 128 128 } 129 + setNotif({ show: true, icon: "lucide--trash-2", text: `${recsToDel.length} records deleted` }); 129 130 setBatchDelete(false); 130 131 setRecords([]); 131 132 setCursor(undefined);
+2
src/views/record.tsx
··· 19 19 import Tooltip from "../components/tooltip.jsx"; 20 20 import { Modal } from "../components/modal.jsx"; 21 21 import { Button } from "../components/button.jsx"; 22 + import { setNotif } from "../layout.jsx"; 22 23 23 24 export const RecordView = () => { 24 25 const navigate = useNavigate(); ··· 98 99 rkey: params.rkey, 99 100 }, 100 101 }); 102 + setNotif({ show: true, icon: "lucide--trash-2", text: "Record deleted" }); 101 103 navigate(`/at://${params.repo}/${params.collection}`); 102 104 }; 103 105