atproto explorer pdsls.dev
atproto tool
at main 8.3 kB view raw
1import { A, Params } from "@solidjs/router"; 2import { createEffect, createSignal, Show } from "solid-js"; 3import { isTouchDevice } from "../layout"; 4import { didDocCache } from "../utils/api"; 5import { addToClipboard } from "../utils/copy"; 6import Tooltip from "./tooltip"; 7 8export const [pds, setPDS] = createSignal<string>(); 9 10const CopyButton = (props: { content: string; label: string }) => { 11 return ( 12 <Show when={!isTouchDevice}> 13 <Tooltip text={props.label}> 14 <button 15 type="button" 16 onclick={(e) => { 17 e.preventDefault(); 18 e.stopPropagation(); 19 addToClipboard(props.content); 20 }} 21 class={`-mr-2 hidden items-center rounded px-2 py-1.5 text-neutral-500 transition-all duration-200 group-hover:flex hover:bg-neutral-200/70 hover:text-neutral-600 active:bg-neutral-300/70 dark:text-neutral-400 dark:hover:bg-neutral-700/70 dark:hover:text-neutral-300 dark:active:bg-neutral-600/70`} 22 aria-label="Copy to clipboard" 23 > 24 <span class="iconify lucide--link"></span> 25 </button> 26 </Tooltip> 27 </Show> 28 ); 29}; 30 31export const NavBar = (props: { params: Params }) => { 32 const [handle, setHandle] = createSignal(props.params.repo); 33 const [showHandle, setShowHandle] = createSignal(localStorage.showHandle === "true"); 34 35 createEffect(() => { 36 if (pds() !== undefined && props.params.repo) { 37 const hdl = 38 didDocCache[props.params.repo]?.alsoKnownAs 39 ?.filter((alias) => alias.startsWith("at://"))[0] 40 ?.split("at://")[1] ?? props.params.repo; 41 if (hdl !== handle()) setHandle(hdl); 42 } 43 }); 44 45 return ( 46 <nav class="flex w-full flex-col text-sm wrap-anywhere sm:text-base"> 47 {/* PDS Level */} 48 <div class="group relative flex items-center justify-between gap-1 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"> 49 <div class="flex min-h-6 basis-full items-center gap-2 sm:min-h-7"> 50 <Tooltip text="PDS"> 51 <span 52 classList={{ 53 "iconify shrink-0 transition-colors duration-200": true, 54 "lucide--alert-triangle text-red-500 dark:text-red-400": pds() === "Missing PDS", 55 "lucide--hard-drive text-neutral-500 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200": 56 pds() !== "Missing PDS", 57 }} 58 ></span> 59 </Tooltip> 60 <Show when={pds()}> 61 <Show 62 when={pds() === "Missing PDS"} 63 fallback={ 64 <Show 65 when={props.params.repo} 66 fallback={<span class="py-0.5 font-medium">{pds()}</span>} 67 > 68 <A 69 end 70 href={pds()!} 71 inactiveClass="text-blue-400 py-0.5 w-full font-medium hover:text-blue-500 transition-colors duration-150 dark:hover:text-blue-300" 72 > 73 {pds()} 74 </A> 75 </Show> 76 } 77 > 78 <span class="py-0.5 font-medium text-red-500 dark:text-red-400">{pds()}</span> 79 </Show> 80 </Show> 81 </div> 82 <Show when={pds() && pds() !== "Missing PDS"}> 83 <CopyButton content={pds()!} label="Copy PDS" /> 84 </Show> 85 </div> 86 87 <div class="flex flex-col"> 88 <Show when={props.params.repo}> 89 {/* Repository Level */} 90 <div class="group relative flex items-center justify-between gap-1 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"> 91 <div class="flex basis-full items-center gap-2"> 92 <Tooltip text="Repository"> 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> 94 </Tooltip> 95 {props.params.collection ? 96 <A 97 end 98 href={`/at://${props.params.repo}`} 99 inactiveClass="text-blue-400 w-full py-0.5 font-medium hover:text-blue-500 transition-colors duration-150 dark:hover:text-blue-300" 100 > 101 {showHandle() ? handle() : props.params.repo} 102 </A> 103 : <span class="py-0.5 font-medium"> 104 {showHandle() ? handle() : props.params.repo} 105 </span> 106 } 107 </div> 108 <div class="flex"> 109 <Tooltip text={showHandle() ? "Show DID" : "Show handle"}> 110 <button 111 type="button" 112 class={`items-center rounded px-1.25 py-1.25 text-neutral-500 transition-all duration-200 hover:bg-neutral-200/70 hover:text-neutral-700 active:bg-neutral-300/70 sm:px-2 sm:py-1.5 dark:text-neutral-400 dark:hover:bg-neutral-700/70 dark:hover:text-neutral-200 dark:active:bg-neutral-600/70 ${isTouchDevice ? "flex" : "hidden group-hover:flex"}`} 113 onclick={() => { 114 localStorage.showHandle = !showHandle(); 115 setShowHandle(!showHandle()); 116 }} 117 aria-label="Switch DID/Handle" 118 > 119 <span 120 class={`iconify shrink-0 duration-200 ${showHandle() ? "rotate-y-180" : ""} lucide--arrow-left-right`} 121 ></span> 122 </button> 123 </Tooltip> 124 <CopyButton content={props.params.repo!} label="Copy DID" /> 125 </div> 126 </div> 127 </Show> 128 129 {/* Collection Level */} 130 <Show when={props.params.collection}> 131 <div class="group flex items-center justify-between 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"> 132 <div class="flex basis-full items-center gap-2"> 133 <Tooltip text="Collection"> 134 <span class="iconify lucide--folder-open text-neutral-500 transition-colors duration-200 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200"></span> 135 </Tooltip> 136 <Show 137 when={props.params.rkey} 138 fallback={<span class="py-0.5 font-medium">{props.params.collection}</span>} 139 > 140 <A 141 end 142 href={`/at://${props.params.repo}/${props.params.collection}`} 143 inactiveClass="text-blue-400 grow py-0.5 font-medium hover:text-blue-500 transition-colors duration-150 dark:hover:text-blue-300" 144 > 145 {props.params.collection} 146 </A> 147 </Show> 148 </div> 149 <CopyButton 150 content={`at://${props.params.repo}/${props.params.collection}`} 151 label="Copy AT URI" 152 /> 153 </div> 154 </Show> 155 156 {/* Record Level */} 157 <Show when={props.params.rkey}> 158 <div class="group flex items-center justify-between 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"> 159 <div class="flex basis-full items-center gap-2"> 160 <Tooltip text="Record"> 161 <span class="iconify lucide--file-json text-neutral-500 transition-colors duration-200 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200"></span> 162 </Tooltip> 163 <span class="py-0.5 font-medium">{props.params.rkey}</span> 164 </div> 165 <CopyButton 166 content={`at://${props.params.repo}/${props.params.collection}/${props.params.rkey}`} 167 label="Copy AT URI" 168 /> 169 </div> 170 </Show> 171 </div> 172 </nav> 173 ); 174};