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}
71 </a>
72 <div class="release-item-meta">
73 {release.asset_count > 0 && (
74 <span>
75 {release.asset_count} asset
76 {release.asset_count !== 1
77 ? "s"
78 : ""}
79 </span>
80 )}
81 {release.include_source_code ===
82 1 && (
83 <span>source archives</span>
84 )}
85 </div>
86 </div>
87 <div class="release-item-date">
88 {release.tag_name && (
89 <a
90 href={`/${repo.name}/tree/${release.tag_name}`}
91 class="badge"
92 >
93 {release.tag_name}
94 </a>
95 )}
96 <time datetime={release.created_at}>
97 {formatDate(release.created_at)}
98 </time>
99 </div>
100 </div>
101 {release.notes && (
102 <div class="release-notes-section">
103 <p class="release-notes-label">
104 Release Notes
105 </p>
106 <NotesPreview notes={release.notes} />
107 </div>
108 )}
109 </li>
110 ))}
111 </ul>
112 )}
113 <Pagination {...pagination} />
114 </div>
115 </Layout>
116 );
117}
118