forked from pdsls.dev/pdsls
atproto explorer

add create/edit/delete notif

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