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" safe>
26 {plaintext}
27 </div>
28 <span class="notes-toggle-label" />
29 </summary>
30 <div class="notes-full markdown-body">{renderMarkdown(notes) as "safe"}</div>
31 </details>
32 );
33}
34
35export function ReleaseList({
36 user,
37 repo,
38 releases,
39 pagination,
40}: ReleaseListProps) {
41 return (
42 <Layout user={user} title={`Releases — ${repo.name}`}>
43 <div class="container">
44 <RepoHeader repo={repo} />
45 <RepoNav repo={repo} active="releases" user={user} />
46 <div class="list-header">
47 <h2 class="list-heading">Releases</h2>
48 {user?.isAdmin && (
49 <a
50 href={`/${repo.name}/releases/new`}
51 class="btn btn-primary btn-sm"
52 >
53 New release
54 </a>
55 )}
56 </div>
57 {releases.length === 0 ? (
58 <div class="empty-state">
59 <p>No releases yet.</p>
60 </div>
61 ) : (
62 <ul class="issue-list">
63 {releases.map((release) => (
64 <li class="issue-item">
65 <div class="release-item-header">
66 <div class="release-item-main">
67 <a
68 href={`/${repo.name}/releases/${release.id}`}
69 class="release-item-title"
70 style={`view-transition-name: release-title-${release.id}`}
71 safe
72 >
73 {release.name}
74 </a>
75 <div class="release-item-meta">
76 {release.asset_count > 0 && (
77 <span>
78 {release.asset_count} asset
79 {release.asset_count !== 1
80 ? "s"
81 : ""}
82 </span>
83 )}
84 {release.include_source_code ===
85 1 && (
86 <span>source archives</span>
87 )}
88 </div>
89 </div>
90 <div class="release-item-date">
91 {!!release.tag_name && (
92 <a
93 href={`/${repo.name}/tree/${release.tag_name}`}
94 class="badge"
95 safe
96 >
97 {release.tag_name}
98 </a>
99 )}
100 <time datetime={release.created_at} safe>
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