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};