1import { createEffect, ErrorBoundary, Show, Suspense } from "solid-js";
2import { A, RouteSectionProps, useLocation, useNavigate, useParams } from "@solidjs/router";
3import { agent } from "./components/login.jsx";
4import { RecordEditor } from "./components/create.jsx";
5import Tooltip from "./components/tooltip.jsx";
6import { NavBar } from "./components/navbar.jsx";
7import { Search } from "./components/search.jsx";
8import { AccountManager } from "./components/account.jsx";
9import { resolveHandle } from "./utils/api.js";
10import { Meta, MetaProvider, Title } from "@solidjs/meta";
11import { kawaii, Settings } from "./components/settings.jsx";
12import { Handle } from "@atcute/lexicons";
13import { copyNotice } from "./utils/copy.js";
14
15const customTitle: Record<string, string> = {
16 "did:plc:hx53snho72xoj7zqt5uice4u": "wrenls",
17};
18
19const Layout = (props: RouteSectionProps<unknown>) => {
20 const params = useParams();
21 const location = useLocation();
22 const navigate = useNavigate();
23 if (location.search.includes("kawaii=true")) localStorage.kawaii = "true";
24
25 createEffect(async () => {
26 if (params.repo && !params.repo.startsWith("did:")) {
27 const did = await resolveHandle(params.repo as Handle);
28 navigate(location.pathname.replace(params.repo, did));
29 }
30 });
31
32 return (
33 <div id="main" class="m-4 flex flex-col items-center text-slate-900 dark:text-slate-100">
34 <MetaProvider>
35 <Show when={location.pathname !== "/"}>
36 <Meta name="robots" content="noindex, nofollow" />
37 </Show>
38 <Title>{customTitle[params.repo] ?? "PDSls"}</Title>
39 </MetaProvider>
40 <div class="mb-2 flex w-[21rem] items-center sm:w-[24rem]">
41 <div class="flex basis-1/3 gap-x-2">
42 <A href="/jetstream">
43 <Tooltip text="Relay">
44 <div class="i-lucide-radio-tower text-xl" />
45 </Tooltip>
46 </A>
47 <AccountManager />
48 </div>
49 <div class="flex basis-1/3 items-center justify-center text-center">
50 <A href="/" class="font-mono font-bold hover:underline">
51 {customTitle[params.repo] ?? "PDSls"}
52 </A>
53 <Show when={localStorage.kawaii === "true" || kawaii()}>
54 <a
55 href="https://bsky.app/profile/ninikyuu.bsky.social/post/3l3tq5xwqf22o"
56 target="_blank"
57 class="h-25px sm:fixed sm:bottom-4 sm:left-0 sm:h-auto"
58 >
59 <img
60 src="/bluetan.png"
61 title="Art by nico ღ (ninikyuu.bsky.social)"
62 class="w-45px sm:w-150px md:w-200px z-0"
63 />
64 </a>
65 </Show>
66 </div>
67 <div class="justify-right flex basis-1/3 items-center gap-x-2">
68 <Show when={agent()}>
69 <RecordEditor create={true} />
70 </Show>
71 <Settings />
72 </div>
73 </div>
74 <div class="min-w-21rem sm:min-w-24rem z-1 dark:bg-dark-500 mb-4 flex max-w-full flex-col items-center text-pretty bg-zinc-100 md:max-w-screen-md">
75 <Show when={location.pathname !== "/jetstream" && location.pathname !== "/firehose"}>
76 <Search />
77 </Show>
78 <Show when={params.pds}>
79 <NavBar params={params} />
80 </Show>
81 <Show keyed when={location.pathname}>
82 <ErrorBoundary
83 fallback={(err) => (
84 <div class="mt-3 break-words text-red-500 dark:text-red-400">
85 Error: {err.message}
86 </div>
87 )}
88 >
89 <Suspense fallback={<div class="i-lucide-loader-circle mt-3 animate-spin text-xl" />}>
90 {props.children}
91 </Suspense>
92 </ErrorBoundary>
93 </Show>
94 </div>
95 <Show when={copyNotice()}>
96 <div class="backdrop-blur-xs border-0.5 dark:shadow-dark-900/80 dark:bg-dark-100/70 fixed bottom-10 z-50 flex items-center rounded-lg border-neutral-300 bg-zinc-100/70 p-2 shadow-md dark:border-neutral-700">
97 <div class="i-lucide-clipboard-check mr-1 text-xl" />
98 Copied to clipboard
99 </div>
100 </Show>
101 </div>
102 );
103};
104
105export { Layout };