forked from pdsls.dev/pdsls
atproto explorer

pds tabs

juli.ee ecfae1d7 1fd48998

verified
Changed files
+78 -39
src
views
+78 -39
src/views/pds.tsx
··· 2 2 import { Client, CredentialManager } from "@atcute/client"; 3 3 import { InferXRPCBodyOutput } from "@atcute/lexicons"; 4 4 import * as TID from "@atcute/tid"; 5 - import { A, useParams } from "@solidjs/router"; 5 + import { A, useLocation, useParams } from "@solidjs/router"; 6 6 import { createResource, createSignal, For, Show } from "solid-js"; 7 7 import { Button } from "../components/button"; 8 8 import { Modal } from "../components/modal"; ··· 14 14 15 15 const PdsView = () => { 16 16 const params = useParams(); 17 + const location = useLocation(); 17 18 const [version, setVersion] = createSignal<string>(); 18 19 const [serverInfos, setServerInfos] = 19 20 createSignal<InferXRPCBodyOutput<ComAtprotoServerDescribeServer.mainSchema["output"]>>(); ··· 103 104 ); 104 105 }; 105 106 107 + const Tab = (props: { tab: "repos" | "info"; label: string }) => ( 108 + <div class="flex items-center gap-0.5"> 109 + <A 110 + classList={{ 111 + "flex items-center gap-1 border-b-2": true, 112 + "border-transparent hover:border-neutral-400 dark:hover:border-neutral-600": 113 + (!!location.hash && location.hash !== `#${props.tab}`) || 114 + (!location.hash && props.tab !== "repos"), 115 + }} 116 + href={`/${params.pds}#${props.tab}`} 117 + > 118 + {props.label} 119 + </A> 120 + </div> 121 + ); 122 + 106 123 return ( 107 124 <Show when={repos() || response()}> 108 - <div class="flex w-full flex-col px-2"> 109 - <Show when={version()}> 110 - {(version) => ( 111 - <div class="flex items-baseline gap-x-1"> 112 - <span class="font-semibold">Version</span> 113 - <span class="truncate text-sm">{version()}</span> 125 + <div class="flex w-full flex-col"> 126 + <div class="dark:shadow-dark-800 dark:bg-dark-300 mb-2 flex w-full justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700"> 127 + <div class="flex gap-3"> 128 + <Tab tab="repos" label="Repositories" /> 129 + <Tab tab="info" label="Info" /> 130 + </div> 131 + <Tooltip text="Firehose"> 132 + <A 133 + href={`/firehose?instance=wss://${params.pds}`} 134 + 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" 135 + > 136 + <span class="iconify lucide--radio-tower"></span> 137 + </A> 138 + </Tooltip> 139 + </div> 140 + <div class="flex flex-col gap-1 px-2"> 141 + <Show when={!location.hash || location.hash === "#repos"}> 142 + <div class="flex flex-col divide-y-[0.5px] divide-neutral-300 dark:divide-neutral-700"> 143 + <For each={repos()}>{(repo) => <RepoCard {...repo} />}</For> 114 144 </div> 115 - )} 116 - </Show> 117 - <Show when={serverInfos()}> 118 - {(server) => ( 119 - <> 120 - <Show when={server().inviteCodeRequired}> 121 - <span class="font-semibold">Invite Code Required</span> 122 - </Show> 123 - <Show when={server().phoneVerificationRequired}> 124 - <span class="font-semibold">Phone Verification Required</span> 125 - </Show> 126 - <Show when={server().availableUserDomains.length}> 127 - <div class="flex flex-col"> 128 - <span class="font-semibold">Available User Domains</span> 129 - <For each={server().availableUserDomains}> 130 - {(domain) => <span class="text-sm wrap-anywhere">{domain}</span>} 131 - </For> 145 + </Show> 146 + <Show when={location.hash === "#info"}> 147 + <Show when={version()}> 148 + {(version) => ( 149 + <div class="flex items-baseline gap-x-1"> 150 + <span class="font-semibold">Version</span> 151 + <span class="truncate text-sm">{version()}</span> 132 152 </div> 133 - </Show> 134 - </> 135 - )} 136 - </Show> 137 - <p class="w-full font-semibold">{repos()?.length} Repositories</p> 138 - <div class="flex flex-col divide-y-[0.5px] divide-neutral-300 dark:divide-neutral-700"> 139 - <For each={repos()}>{(repo) => <RepoCard {...repo} />}</For> 153 + )} 154 + </Show> 155 + <Show when={serverInfos()}> 156 + {(server) => ( 157 + <> 158 + <Show when={server().inviteCodeRequired}> 159 + <span class="font-semibold">Invite Code Required</span> 160 + </Show> 161 + <Show when={server().phoneVerificationRequired}> 162 + <span class="font-semibold">Phone Verification Required</span> 163 + </Show> 164 + <Show when={server().availableUserDomains.length}> 165 + <div class="flex flex-col"> 166 + <span class="font-semibold">Available User Domains</span> 167 + <For each={server().availableUserDomains}> 168 + {(domain) => <span class="text-sm wrap-anywhere">{domain}</span>} 169 + </For> 170 + </div> 171 + </Show> 172 + </> 173 + )} 174 + </Show> 175 + </Show> 140 176 </div> 141 177 </div> 142 - <Show when={cursor()}> 143 - <div class="dark:bg-dark-500 fixed bottom-0 z-5 flex w-screen justify-center bg-neutral-100 py-3"> 144 - <Show when={!response.loading}> 145 - <Button onClick={() => refetch()}>Load More</Button> 146 - </Show> 147 - <Show when={response.loading}> 148 - <span class="iconify lucide--loader-circle animate-spin py-3.5 text-xl"></span> 149 - </Show> 178 + <Show when={!location.hash || location.hash === "#repos"}> 179 + <div class="dark:bg-dark-500 fixed bottom-0 z-5 flex w-screen justify-center bg-neutral-100 py-2"> 180 + <div class="flex flex-col items-center gap-1 pb-2"> 181 + <p>{repos()?.length} loaded</p> 182 + <Show when={!response.loading && cursor()}> 183 + <Button onClick={() => refetch()}>Load More</Button> 184 + </Show> 185 + <Show when={response.loading}> 186 + <span class="iconify lucide--loader-circle animate-spin py-3.5 text-xl"></span> 187 + </Show> 188 + </div> 150 189 </div> 151 190 </Show> 152 191 </Show>