1import { type Params, useParams } from "@solidjs/router";
2import { createMemo, createResource, onMount, Show } from "solid-js";
3import { figureOutDid } from "../../../util/handle";
4import { toRelativeTime } from "../../../util/time";
5import type { Commit, DID } from "../../../util/types";
6import { Header } from "../components/header";
7import { useDid } from "../context";
8import { getRepoCommit } from "../main.data";
9import { buildTree, type TreeNode } from "./data";
10import { DiffView, Sidebar } from "./generic";
11
12export async function preloadRepoCommit({ params }: { params: Params }) {
13 const did = await figureOutDid(params.user!);
14 if (!did) return;
15 getRepoCommit(did, params.repo!, params.ref!);
16}
17
18export default function RepoCommit() {
19 const params = useParams() as { user: string; repo: string; ref: string };
20 const did = useDid();
21
22 const [commit] = createResource(
23 () => {
24 const d = did();
25 if (!d) return;
26 return [d, params.repo, params.ref] as const;
27 },
28 async ([did, repo, ref]) => {
29 const res = await getRepoCommit(did as DID, repo, ref);
30 if (!res.ok) return;
31 return res.data as Commit;
32 },
33 );
34
35 const commitDiffViewInput = createMemo(() => {
36 if (!commit()?.diff) return;
37 return commit()!.diff.diff.map((diff) => ({
38 oldName: diff.name.old,
39 newName: diff.name.new,
40 textFragments: diff.text_fragments,
41 }));
42 });
43
44 const sidebar = createMemo(() => {
45 if (!commit()?.diff.diff)
46 return { name: "", fullPath: "", type: "directory" } as TreeNode;
47 return buildTree(commit()!.diff.diff.map((v) => v.name.new));
48 });
49
50 const message = createMemo(() => {
51 const c = commit();
52 if (!c) return;
53
54 const titleEnd = c.diff.commit.message.indexOf("\n");
55 return {
56 title: c.diff.commit.message.slice(0, titleEnd),
57 content: c.diff.commit.message.slice(titleEnd + 1),
58 };
59 });
60
61 onMount(() => {
62 if (window.location.hash) {
63 const element = document.getElementById(window.location.hash.slice(1));
64 if (element)
65 element.scrollIntoView({ behavior: "instant", block: "start" });
66 }
67 });
68
69 return (
70 <div class="mx-auto max-w-10xl">
71 <Show when={commit() && message()}>
72 <CommitHeader
73 user={params.user}
74 repo={params.repo}
75 message={message()!}
76 commit={commit()!}
77 />
78 </Show>
79 <Show when={sidebar()?.children && commit() && commitDiffViewInput()}>
80 <div class="flex flex-col gap-1 md:flex-row">
81 <Sidebar
82 sidebar={sidebar()}
83 insertions={commit()!.diff.stat.insertions}
84 deletions={commit()!.diff.stat.deletions}
85 />
86 <div class="min-w-0 flex-1 flex-col gap-1 p-1 max-md:pt-0 md:pl-0">
87 <DiffView diff={commitDiffViewInput()!} />
88 </div>
89 </div>
90 </Show>
91 </div>
92 );
93}
94
95function CommitHeader(props: {
96 user: string;
97 repo: string;
98 message: { title: string; content: string };
99 commit: Commit;
100}) {
101 const date = () => new Date(props.commit.diff.commit.author.When);
102 return (
103 <div>
104 <Header user={props.user} repo={props.repo} />
105 <div class="mx-1 flex flex-col gap-2 rounded bg-white p-4 dark:bg-gray-800">
106 <div>{props.message.title}</div>
107 <Show when={props.message.content}>
108 <div class="text-xs">{props.message.content}</div>
109 </Show>
110 <div class="text-gray-500 text-xs dark:text-gray-300">
111 <span
112 title={date().toLocaleString(undefined, {
113 dateStyle: "full",
114 timeStyle: "short",
115 })}
116 >{`${toRelativeTime(date())}`}</span>
117 <span class="select-none px-1 before:content-['\00B7']" />
118 <span>{`${props.commit.diff.commit.author.Name} <${props.commit.diff.commit.author.Email}>`}</span>
119 <span class="select-none px-1 before:content-['\00B7']" />
120 <a
121 class="hover:text-gray-600 hover:underline hover:dark:text-gray-200"
122 href={`/${props.user}/${props.repo}/commit/${props.commit.ref}`}
123 >
124 {props.commit.ref.slice(0, 8)}
125 </a>
126 <Show when={props.commit.diff.commit.parent}>
127 <div class="iconify gravity-ui--arrow-left mx-1 text-[0.6rem]" />
128 <a
129 class="hover:text-gray-600 hover:underline hover:dark:text-gray-200"
130 href={`/${props.user}/${props.repo}/commit/${props.commit.diff.commit.parent}`}
131 >
132 {props.commit.diff.commit.parent.slice(0, 8)}
133 </a>
134 </Show>
135 </div>
136 </div>
137 </div>
138 );
139}