this repo has no description

new

handle.invalid bc07dfe1 7acbe3a1

verified
+48 -64
+3 -24
src/components/navbar.tsx
··· 1 - import { A, Params, useLocation } from "@solidjs/router"; 1 + import { A, Params } from "@solidjs/router"; 2 2 import { createEffect, createSignal, Show } from "solid-js"; 3 3 import { isTouchDevice } from "../layout"; 4 - import { didDocCache, labelerCache } from "../utils/api"; 4 + import { didDocCache } from "../utils/api"; 5 5 import { addToClipboard } from "../utils/copy"; 6 6 import Tooltip from "./tooltip"; 7 7 ··· 29 29 }; 30 30 31 31 export const NavBar = (props: { params: Params }) => { 32 - const location = useLocation(); 33 32 const [handle, setHandle] = createSignal(props.params.repo); 34 33 const [showHandle, setShowHandle] = createSignal(localStorage.showHandle === "true"); 35 34 ··· 93 92 <Tooltip text="Repository"> 94 93 <span class="iconify lucide--book-user shrink-0 text-neutral-500 transition-colors duration-200 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200"></span> 95 94 </Tooltip> 96 - {props.params.collection || location.pathname.includes("/labels") ? 95 + {props.params.collection ? 97 96 <A 98 97 end 99 98 href={`/at://${props.params.repo}`} ··· 124 123 </Tooltip> 125 124 <CopyButton content={props.params.repo} label="Copy DID" /> 126 125 </div> 127 - </div> 128 - </Show> 129 - 130 - {/* Labels Level */} 131 - <Show 132 - when={ 133 - !props.params.collection && 134 - (props.params.repo in labelerCache || location.pathname.endsWith("/labels")) 135 - } 136 - > 137 - <div class="group flex items-center gap-2 rounded-md border-[0.5px] border-transparent bg-transparent px-2 transition-all duration-200 hover:border-neutral-300 hover:bg-neutral-50/40 dark:hover:border-neutral-600 dark:hover:bg-neutral-800/40"> 138 - <span class="iconify lucide--tag text-neutral-500 transition-colors duration-200 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200"></span> 139 - <A 140 - end 141 - href={`/at://${props.params.repo}/labels`} 142 - class="py-0.5 font-medium" 143 - inactiveClass="text-blue-400 grow hover:text-blue-500 transition-colors duration-150 dark:hover:text-blue-300" 144 - > 145 - labels 146 - </A> 147 126 </div> 148 127 </Show> 149 128
+1 -1
src/index.tsx
··· 17 17 <Router root={Layout}> 18 18 <Route path="/" component={Home} /> 19 19 <Route path={["/jetstream", "/firehose"]} component={StreamView} /> 20 + <Route path="/labels" component={LabelView} /> 20 21 <Route path="/settings" component={Settings} /> 21 22 <Route path="/:pds" component={PdsView} /> 22 23 <Route path="/:pds/:repo" component={RepoView} /> 23 - <Route path="/:pds/:repo/labels" component={LabelView} /> 24 24 <Route path="/:pds/:repo/:collection" component={CollectionView} /> 25 25 <Route path="/:pds/:repo/:collection/:rkey" component={RecordView} /> 26 26 </Router>
+1
src/layout.tsx
··· 145 145 > 146 146 <NavMenu href="/jetstream" label="Jetstream" /> 147 147 <NavMenu href="/firehose" label="Firehose" /> 148 + <NavMenu href="/labels" label="Labels" /> 148 149 <NavMenu href="/settings" label="Settings" /> 149 150 <NavMenu 150 151 href="https://bsky.app/profile/did:plc:6q5daed5gutiyerimlrnojnz"
+43 -39
src/views/labels.tsx
··· 1 1 import { ComAtprotoLabelDefs } from "@atcute/atproto"; 2 2 import { Client, CredentialManager } from "@atcute/client"; 3 - import { A, useParams, useSearchParams } from "@solidjs/router"; 4 - import { createResource, createSignal, For, onMount, Show } from "solid-js"; 3 + import { A, useSearchParams } from "@solidjs/router"; 4 + import { createSignal, For, onMount, Show } from "solid-js"; 5 5 import { Button } from "../components/button.jsx"; 6 6 import { StickyOverlay } from "../components/sticky.jsx"; 7 7 import { TextInput } from "../components/text-input.jsx"; 8 8 import { labelerCache, resolvePDS } from "../utils/api.js"; 9 9 import { localDateFromTimestamp } from "../utils/date.js"; 10 10 11 - const LabelView = () => { 12 - const params = useParams(); 11 + export const LabelView = () => { 13 12 const [searchParams, setSearchParams] = useSearchParams(); 14 13 const [cursor, setCursor] = createSignal<string>(); 15 14 const [labels, setLabels] = createSignal<ComAtprotoLabelDefs.Label[]>([]); 16 15 const [filter, setFilter] = createSignal<string>(); 17 16 const [labelCount, setLabelCount] = createSignal(0); 18 - const did = params.repo; 17 + const [loading, setLoading] = createSignal(false); 19 18 let rpc: Client; 19 + let formRef!: HTMLFormElement; 20 20 21 21 onMount(async () => { 22 + const formData = new FormData(); 23 + if (searchParams.did) formData.append("did", searchParams.did.toString()); 24 + if (searchParams.did) fetchLabels(formData); 25 + }); 26 + 27 + const fetchLabels = async (formData: FormData, reset?: boolean) => { 28 + if (reset) { 29 + setLabels([]); 30 + setCursor(undefined); 31 + } 32 + 33 + const did = formData.get("did")?.toString(); 34 + if (!did) return; 22 35 await resolvePDS(did); 23 36 rpc = new Client({ 24 37 handler: new CredentialManager({ service: labelerCache[did] }), 25 38 }); 26 - refetch(); 27 - }); 28 39 29 - const fetchLabels = async () => { 30 - const uriPatterns = (document.getElementById("patterns") as HTMLInputElement).value; 40 + const uriPatterns = formData.get("uriPatterns")?.toString(); 31 41 if (!uriPatterns) return; 42 + 43 + setSearchParams({ 44 + did: formData.get("did")?.toString(), 45 + uriPatterns: formData.get("uriPatterns")?.toString(), 46 + }); 47 + 48 + setLoading(true); 32 49 const res = await rpc.get("com.atproto.label.queryLabels", { 33 50 params: { 34 51 uriPatterns: uriPatterns.toString().trim().split(","), ··· 36 53 cursor: cursor(), 37 54 }, 38 55 }); 56 + setLoading(false); 39 57 if (!res.ok) throw new Error(res.data.error); 40 58 setCursor(res.data.labels.length < 50 ? undefined : res.data.cursor); 41 59 setLabels(labels().concat(res.data.labels) ?? res.data.labels); 42 60 return res.data.labels; 43 61 }; 44 62 45 - const [response, { refetch }] = createResource(fetchLabels); 46 - 47 - const initQuery = async () => { 48 - setLabels([]); 49 - setCursor(""); 50 - setSearchParams({ 51 - uriPatterns: (document.getElementById("patterns") as HTMLInputElement).value, 52 - }); 53 - refetch(); 54 - }; 55 - 56 63 const filterLabels = () => { 57 64 const newFilter = labels().filter((label) => (filter() ? filter() === label.val : true)); 58 65 setLabelCount(newFilter.length); ··· 61 68 62 69 return ( 63 70 <div class="flex w-full flex-col items-center"> 64 - <form 65 - class="flex w-full flex-col items-center gap-y-1 px-2" 66 - onsubmit={(e) => { 67 - e.preventDefault(); 68 - initQuery(); 69 - }} 70 - > 71 - <label for="patterns" class="ml-2 w-full text-sm"> 71 + <form ref={formRef} class="flex w-full flex-col items-center gap-y-1 px-2"> 72 + <label class="flex w-full items-center gap-x-2 px-1"> 73 + <span class="">DID</span> 74 + <TextInput name="did" value={searchParams.did ?? ""} class="grow" /> 75 + </label> 76 + <label for="uriPatterns" class="ml-2 w-full text-sm"> 72 77 URI Patterns (comma-separated) 73 78 </label> 74 79 <div class="flex w-full items-center gap-x-1 px-1"> 75 80 <textarea 76 - id="patterns" 77 - name="patterns" 81 + id="uriPatterns" 82 + name="uriPatterns" 78 83 spellcheck={false} 79 84 rows={2} 80 85 value={searchParams.uriPatterns ?? "*"} 81 86 class="dark:bg-dark-100 dark:shadow-dark-700 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 text-sm shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 82 87 /> 83 88 <div class="flex justify-center"> 84 - <Show when={!response.loading}> 89 + <Show when={!loading()}> 85 90 <button 86 - type="submit" 91 + type="button" 92 + onClick={() => fetchLabels(new FormData(formRef), true)} 87 93 class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 88 94 > 89 95 <span class="iconify lucide--search text-lg"></span> 90 96 </button> 91 97 </Show> 92 - <Show when={response.loading}> 98 + <Show when={loading()}> 93 99 <div class="m-1 flex items-center"> 94 100 <span class="iconify lucide--loader-circle animate-spin text-lg"></span> 95 101 </div> ··· 114 120 </Show> 115 121 <Show when={cursor()}> 116 122 <div class="flex h-8 w-22 items-center justify-center text-nowrap"> 117 - <Show when={!response.loading}> 118 - <Button onClick={() => refetch()}>Load More</Button> 123 + <Show when={!loading()}> 124 + <Button onClick={() => fetchLabels(new FormData(formRef))}>Load More</Button> 119 125 </Show> 120 - <Show when={response.loading}> 126 + <Show when={loading()}> 121 127 <div class="iconify lucide--loader-circle animate-spin text-xl" /> 122 128 </Show> 123 129 </div> ··· 170 176 </For> 171 177 </div> 172 178 </Show> 173 - <Show when={!labels().length && !response.loading && searchParams.uriPatterns}> 179 + <Show when={!labels().length && !loading() && searchParams.uriPatterns}> 174 180 <div class="mt-2">No results</div> 175 181 </Show> 176 182 </div> 177 183 ); 178 184 }; 179 - 180 - export { LabelView };