1import {
2 createAsync,
3 type Params,
4 useNavigate,
5 useParams,
6} from "@solidjs/router";
7import { createMemo, createResource, onMount, Show } from "solid-js";
8import { CodeBlock } from "../../elements/code_block";
9import { formatBytes } from "../../util/bytes";
10import { getLanguage } from "../../util/get_language";
11import { figureOutDid } from "../../util/handle";
12import { Header } from "./components/header";
13import { PathBar } from "./components/pathbar";
14import { useDid } from "./context";
15import { getRepoBlob, getRepoBlobUrl, getRepoDefaultBranch } from "./main.data";
16
17export async function preloadRepoBlob({ params }: { params: Params }) {
18 const did = await figureOutDid(params.user!);
19 if (!did) return;
20 getRepoBlob(did, params.repo!, params.ref!, params.path!);
21}
22
23export default function RepoBlob() {
24 const params = useParams() as {
25 user: string;
26 repo: string;
27 ref: string;
28 path: string;
29 };
30 const did = useDid();
31 const navigate = useNavigate();
32
33 const blob = createAsync(() =>
34 (async () => {
35 const d = did();
36 if (!d) return;
37 const res = await getRepoBlob(d, params.repo, params.ref, params.path);
38 if (res.status === 404) {
39 const split = window.location.pathname.split("/");
40 split[3] = "tree";
41 navigate(split.join("/"), { replace: true });
42 }
43 if (!res.ok) return;
44 return res.data;
45 })(),
46 );
47
48 const blobUrl = createAsync(async () => {
49 const d = did();
50 if (!d) return;
51 return await getRepoBlobUrl(d, params.repo, params.ref, params.path);
52 });
53
54 const codeBlock = createMemo(() => {
55 if (!blob()?.content) return;
56 return (
57 <CodeBlock
58 code={blob()!.content!}
59 language={getLanguage(blob()!.path.split("/").pop()) || "text"}
60 />
61 );
62 });
63
64 const [defaultBranch] = createResource(did, async (did) => {
65 const res = await getRepoDefaultBranch(did, params.repo);
66 if (!res.ok) return;
67 return res.data.name;
68 });
69
70 const ref = () => params.ref || defaultBranch();
71
72 onMount(() => {
73 if (window.location.hash) {
74 const element = document.getElementById(window.location.hash.slice(1));
75 if (element)
76 element.scrollIntoView({ behavior: "instant", block: "start" });
77 }
78 });
79
80 return (
81 <div class="mx-auto max-w-5xl">
82 <Header user={params.user} repo={params.repo} />
83 <div class="flex flex-col rounded bg-white py-2 dark:bg-gray-800">
84 <div class="mx-5 flex flex-col justify-between gap-1 border-gray-300 border-b py-2 md:flex-row md:items-center dark:border-gray-700">
85 <PathBar
86 user={params.user}
87 repo={params.repo}
88 gitref={ref()!}
89 path={params.path}
90 is_file={true}
91 />
92 <div class="text-gray-500 text-xs dark:text-gray-400">
93 <Show when={blobUrl() && ref() && blob()}>
94 <span>
95 {"at "}
96 <a
97 href={`/${params.user}/${params.repo}/tree/${ref()!}`}
98 class="text-black hover:text-gray-700 hover:underline dark:text-white hover:dark:text-gray-300"
99 >
100 {ref()!}
101 </a>
102 </span>
103 <span class="select-none px-1 before:content-['\00B7']" />
104 <span>{formatBytes(blob()!.size!)}</span>
105 <span class="select-none px-1 before:content-['\00B7']" />
106 <a
107 href={blobUrl()}
108 class="text-black hover:text-gray-700 hover:underline dark:text-white hover:dark:text-gray-300"
109 >
110 view raw
111 </a>
112 </Show>
113 </div>
114 </div>
115 <div class="flex flex-col p-4">{codeBlock()}</div>
116 </div>
117 </div>
118 );
119}