atproto explorer

repo tabs as hash links

+16 -14
+16 -14
src/views/repo.tsx
··· 1 1 import { createSignal, For, Show, createResource, Suspense, ErrorBoundary } from "solid-js"; 2 2 import { Client, CredentialManager } from "@atcute/client"; 3 - import { A, useParams } from "@solidjs/router"; 3 + import { A, useLocation, useNavigate, useParams } from "@solidjs/router"; 4 4 import { didDocCache, resolvePDS } from "../utils/api.js"; 5 5 import { Backlinks } from "../components/backlinks.jsx"; 6 6 import { ActorIdentifier } from "@atcute/lexicons"; ··· 19 19 import { Button } from "../components/button.jsx"; 20 20 import { parsePublicMultikey } from "@atcute/crypto"; 21 21 22 - type Tab = "collections" | "backlinks" | "doc" | "blobs"; 22 + type Tab = "collections" | "backlinks" | "identity" | "blobs"; 23 23 type PlcEvent = "handle" | "rotation_key" | "service" | "verification_method"; 24 24 25 25 const PlcLogView = (props: { ··· 154 154 155 155 const RepoView = () => { 156 156 const params = useParams(); 157 + const location = useLocation(); 158 + const navigate = useNavigate(); 157 159 const [error, setError] = createSignal<string>(); 158 160 const [downloading, setDownloading] = createSignal(false); 159 161 const [didDoc, setDidDoc] = createSignal<DidDocument>(); 160 162 const [nsids, setNsids] = createSignal<Record<string, { hidden: boolean; nsids: string[] }>>(); 161 - const [tab, setTab] = createSignal<Tab>("collections"); 162 163 const [filter, setFilter] = createSignal<string>(); 163 164 const [plcOps, setPlcOps] = 164 165 createSignal<[IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]>(); ··· 170 171 const did = params.repo; 171 172 172 173 const RepoTab = (props: { tab: Tab; label: string; icon: string }) => ( 173 - <button 174 + <A 174 175 classList={{ 175 - "border-b-2 flex items-center gap-1 py-1": true, 176 + "flex items-center border-b-2 gap-1 py-1": true, 176 177 "border-transparent hover:border-neutral-400 dark:hover:border-neutral-600": 177 - tab() !== props.tab, 178 + (location.hash !== `#${props.tab}` && !!location.hash) || 179 + (!location.hash && props.tab !== "collections"), 178 180 }} 179 - onclick={() => setTab(props.tab)} 181 + href={`/at://${params.repo}#${props.tab}`} 180 182 > 181 183 <div class={"iconify " + props.icon} /> 182 184 {props.label} 183 - </button> 185 + </A> 184 186 ); 185 187 186 188 const fetchRepo = async () => { ··· 216 218 default: 217 219 setError("This repository is unreachable"); 218 220 } 219 - setTab("doc"); 221 + navigate("#identity"); 220 222 } 221 223 222 224 return res.data; ··· 267 269 <Show when={!error()}> 268 270 <RepoTab tab="collections" label="Collections" icon="lucide--folder-open" /> 269 271 </Show> 270 - <RepoTab tab="doc" label="Identity" icon="lucide--id-card" /> 272 + <RepoTab tab="identity" label="Identity" icon="lucide--id-card" /> 271 273 <Show when={!error()}> 272 274 <RepoTab tab="blobs" label="Blobs" icon="lucide--file-digit" /> 273 275 </Show> 274 276 <RepoTab tab="backlinks" label="Backlinks" icon="lucide--send-to-back" /> 275 277 </div> 276 - <Show when={tab() === "backlinks"}> 278 + <Show when={location.hash === "#backlinks"}> 277 279 <ErrorBoundary fallback={(err) => <div class="break-words">Error: {err.message}</div>}> 278 280 <Suspense 279 281 fallback={ ··· 284 286 </Suspense> 285 287 </ErrorBoundary> 286 288 </Show> 287 - <Show when={tab() === "blobs"}> 289 + <Show when={location.hash === "#blobs"}> 288 290 <ErrorBoundary fallback={(err) => <div class="break-words">Error: {err.message}</div>}> 289 291 <Suspense 290 292 fallback={ ··· 295 297 </Suspense> 296 298 </ErrorBoundary> 297 299 </Show> 298 - <Show when={nsids() && tab() === "collections"}> 300 + <Show when={nsids() && (!location.hash || location.hash === "#collections")}> 299 301 <TextInput 300 302 placeholder="Filter collections" 301 303 onInput={(e) => setFilter(e.currentTarget.value)} ··· 352 354 </div> 353 355 </div> 354 356 </Show> 355 - <Show when={tab() === "doc"}> 357 + <Show when={location.hash === "#identity"}> 356 358 <Show when={didDoc()}> 357 359 {(didDocument) => ( 358 360 <div class="flex flex-col gap-y-2 wrap-anywhere">