ReleaseList.tsx
Raw
1import type { ReleaseRow, RepositoryRow } from "../../db/index.ts";
2import { formatDate } from "../../lib/formatDate.ts";
3import type { SessionUser } from "../../middleware/session.ts";
4import {
5 markdownToPlaintext,
6 renderMarkdown,
7} from "../../services/markdown.ts";
8import { Layout } from "../layout.tsx";
9import { Pagination, type PaginationInfo } from "../Pagination.tsx";
10import { RepoHeader } from "../repos/RepoHeader.tsx";
11import { RepoNav } from "../repos/RepoNav.tsx";
12
13interface ReleaseListProps {
14 user: SessionUser | null;
15 repo: RepositoryRow;
16 releases: (ReleaseRow & { asset_count: number })[];
17 pagination: PaginationInfo;
18}
19
20function NotesPreview({ notes }: { notes: string }) {
21 const plaintext = markdownToPlaintext(notes);
22 return (
23 <details class="notes-expand">
24 <summary class="notes-toggle">
25 <div class="notes-preview">{plaintext}</div>
26 <span class="notes-toggle-label" />
27 </summary>
28 <div class="notes-full markdown-body">{renderMarkdown(notes)}</div>
29 </details>
30 );
31}
32
33export function ReleaseList({
34 user,
35 repo,
36 releases,
37 pagination,
38}: ReleaseListProps) {
39 return (
40 <Layout user={user} title={`Releases — ${repo.name}`}>
41 <div class="container">
42 <RepoHeader repo={repo} />
43 <RepoNav repo={repo} active="releases" user={user} />
44 <div class="list-header">
45 <h2 class="list-heading">Releases</h2>
46 {user?.isAdmin && (
47 <a
48 href={`/${repo.name}/releases/new`}
49 class="btn btn-primary btn-sm"
50 >
51 New release
52 </a>
53 )}
54 </div>
55 {releases.length === 0 ? (
56 <div class="empty-state">
57 <p>No releases yet.</p>
58 </div>
59 ) : (
60 <ul class="issue-list">
61 {releases.map((release) => (
62 <li class="issue-item">
63 <div class="release-item-header">
64 <div class="release-item-main">
65 <a
66 href={`/${repo.name}/releases/${release.id}`}
67 class="release-item-title"
68 style={`view-transition-name: release-title-${release.id}`}
69 >
70 {release.name || release.tag_name}
71 </a>
72 <div class="release-item-meta">
73 <a
74 href={`/${repo.name}/tree/${release.commit_hash}`}
75 class="monospace"
76 >
77 {release.commit_hash.slice(
78 0,
79 8,
80 )}
81 </a>
82 {release.asset_count > 0 && (
83 <span>
84 {release.asset_count} asset
85 {release.asset_count !== 1
86 ? "s"
87 : ""}
88 </span>
89 )}
90 {release.include_source_code ===
91 1 && (
92 <span>source archives</span>
93 )}
94 </div>
95 </div>
96 <div class="release-item-date">
97 <span class="badge">
98 {release.tag_name}
99 </span>
100 <time datetime={release.created_at}>
101 {formatDate(release.created_at)}
102 </time>
103 </div>
104 </div>
105 {release.notes && (
106 <div class="release-notes-section">
107 <p class="release-notes-label">
108 Release Notes
109 </p>
110 <NotesPreview notes={release.notes} />
111 </div>
112 )}
113 </li>
114 ))}
115 </ul>
116 )}
117 <Pagination {...pagination} />
118 </div>
119 </Layout>
120 );
121}
122