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">
31 {renderMarkdown(notes) as "safe"}
32 </div>
33 </details>
34 );
35}
36
37export function ReleaseList({
38 user,
39 repo,
40 releases,
41 pagination,
42}: ReleaseListProps) {
43 return (
44 <Layout user={user} title={`Releases — ${repo.name}`}>
45 <div class="container">
46 <RepoHeader repo={repo} />
47 <RepoNav repo={repo} active="releases" user={user} />
48 <div class="list-header">
49 <h2 class="list-heading">Releases</h2>
50 {user?.isAdmin && (
51 <a
52 href={`/${repo.name}/releases/new`}
53 class="btn btn-primary btn-sm"
54 >
55 New release
56 </a>
57 )}
58 </div>
59 {releases.length === 0 ? (
60 <div class="empty-state">
61 <p>No releases yet.</p>
62 </div>
63 ) : (
64 <ul class="issue-list">
65 {releases.map((release) => (
66 <li class="issue-item">
67 <div class="release-item-header">
68 <div class="release-item-main">
69 <a
70 href={`/${repo.name}/releases/${release.id}`}
71 class="release-item-title"
72 style={`view-transition-name: release-title-${release.id}`}
73 safe
74 >
75 {release.name}
76 </a>
77 <div class="release-item-meta">
78 {release.asset_count > 0 && (
79 <span>
80 {release.asset_count} asset
81 {release.asset_count !== 1
82 ? "s"
83 : ""}
84 </span>
85 )}
86 {release.include_source_code ===
87 1 && (
88 <span>source archives</span>
89 )}
90 </div>
91 </div>
92 <div class="release-item-date">
93 {!!release.tag_name && (
94 <a
95 href={`/${repo.name}/tree/${release.tag_name}`}
96 class="badge"
97 safe
98 >
99 {release.tag_name}
100 </a>
101 )}
102 <time
103 datetime={release.created_at}
104 safe
105 >
106 {formatDate(release.created_at)}
107 </time>
108 </div>
109 </div>
110 {!!release.notes && (
111 <div class="release-notes-section">
112 <p class="release-notes-label">
113 Release Notes
114 </p>
115 <NotesPreview notes={release.notes} />
116 </div>
117 )}
118 </li>
119 ))}
120 </ul>
121 )}
122 <Pagination {...pagination} />
123 </div>
124 </Layout>
125 );
126}
127