Skip to main content

max / makenotwork

12.3 KB · 358 lines History Blame Raw
1 //! Templates for the public git source browser, issues, and repo settings.
2
3 use std::collections::HashMap;
4 use std::sync::Arc;
5
6 use askama::Template;
7
8 use crate::auth::SessionUser;
9 use crate::git;
10 use crate::routes::git::notes_view::{AnnotationRow, CommitNote, NamespaceSummary};
11 use crate::types::{Item, Project, Version};
12
13 use super::super::CsrfTokenOption;
14
15 /// An item paired with its versions, for release display on the git repo page.
16 pub struct ReleaseItem {
17 pub item: Item,
18 pub versions: Vec<Version>,
19 }
20
21 /// Repository overview: file tree at HEAD + README.
22 #[derive(Template)]
23 #[template(path = "pages/git/repo.html")]
24 pub struct GitRepoTemplate {
25 pub csrf_token: CsrfTokenOption,
26 pub session_user: Option<SessionUser>,
27 pub owner: String,
28 pub repo_name: String,
29 pub description: Option<String>,
30 pub current_ref: String,
31 pub refs: Vec<git::RefInfo>,
32 pub tree_items: Vec<git::TreeItem>,
33 pub readme_html: Option<String>,
34 pub host_url: Arc<str>,
35 /// Hostname for SSH clone URLs (e.g., "git.makenot.work"). Hidden when `None`.
36 pub git_ssh_host: Option<String>,
37 /// Linked project, if this repo is associated with a public project.
38 pub linked_project: Option<Project>,
39 /// Public items with versions from the linked project (releases).
40 pub release_items: Vec<ReleaseItem>,
41 /// Number of open issues for the nav bar badge.
42 pub open_issue_count: i64,
43 /// Whether this repository has `refs/replace/*` mappings, which links the
44 /// replace page from the overview. Almost always false, and shown here
45 /// rather than in the nav so no repository grows a permanently empty tab.
46 pub has_replacements: bool,
47 /// Whether the current viewer is the repo owner (for settings link).
48 pub is_owner: bool,
49 /// Whether the viewer has muted this repo's issue notifications. `None`
50 /// when logged out, which hides the control: there is nothing to mute for
51 /// somebody we could not mail anyway.
52 pub issues_muted: Option<bool>,
53 /// Repository id, for the mute form's hidden field.
54 pub repo_id: String,
55 pub active_tab: &'static str,
56 }
57
58 /// Subdirectory listing with breadcrumb navigation.
59 #[derive(Template)]
60 #[template(path = "pages/git/tree.html")]
61 pub struct GitTreeTemplate {
62 pub csrf_token: CsrfTokenOption,
63 pub session_user: Option<SessionUser>,
64 pub owner: String,
65 pub repo_name: String,
66 pub current_ref: String,
67 pub refs: Vec<git::RefInfo>,
68 pub path: String,
69 pub parent_path: String,
70 /// Whether we're in a subdirectory (for ".." link and path joining).
71 pub in_subdir: bool,
72 pub breadcrumbs: Vec<git::Breadcrumb>,
73 pub tree_items: Vec<git::TreeItem>,
74 pub open_issue_count: i64,
75 pub is_owner: bool,
76 pub active_tab: &'static str,
77 }
78
79 /// File viewer with syntax highlighting and line numbers.
80 #[derive(Template)]
81 #[template(path = "pages/git/file.html")]
82 pub struct GitFileTemplate {
83 pub csrf_token: CsrfTokenOption,
84 pub session_user: Option<SessionUser>,
85 pub owner: String,
86 pub repo_name: String,
87 pub current_ref: String,
88 pub refs: Vec<git::RefInfo>,
89 pub file_path: String,
90 pub filename: String,
91 pub breadcrumbs: Vec<git::Breadcrumb>,
92 pub file_size: String,
93 pub line_count: usize,
94 pub is_binary: bool,
95 pub highlighted_lines: Vec<String>,
96 /// Notes on the blob this view is showing. A note on a file annotates the
97 /// object, so it follows the content rather than the path.
98 pub notes: Vec<CommitNote>,
99 pub open_issue_count: i64,
100 pub is_owner: bool,
101 pub active_tab: &'static str,
102 }
103
104 /// Commit log with pagination.
105 #[derive(Template)]
106 #[template(path = "pages/git/commits.html")]
107 pub struct GitCommitsTemplate {
108 pub csrf_token: CsrfTokenOption,
109 pub session_user: Option<SessionUser>,
110 pub owner: String,
111 pub repo_name: String,
112 pub current_ref: String,
113 pub refs: Vec<git::RefInfo>,
114 pub commits: Vec<git::CommitInfo>,
115 /// Commits on this page that carry a note, mapped to how many namespaces
116 /// annotate them. One batched lookup per page, never one per row.
117 pub annotated: HashMap<String, usize>,
118 pub page: usize,
119 pub has_more: bool,
120 pub open_issue_count: i64,
121 /// Whether the current viewer is the repo owner (for settings link).
122 pub is_owner: bool,
123 pub active_tab: &'static str,
124 }
125
126 /// Commit detail page with inline diffs.
127 #[derive(Template)]
128 #[template(path = "pages/git/commit.html")]
129 pub struct GitCommitDetailTemplate {
130 pub csrf_token: CsrfTokenOption,
131 pub session_user: Option<SessionUser>,
132 pub owner: String,
133 pub repo_name: String,
134 pub current_ref: String,
135 pub refs: Vec<git::RefInfo>,
136 pub detail: git::CommitDetail,
137 /// Signature state. `Unsigned` for almost every commit, and rendered as
138 /// nothing rather than as a warning: an unsigned commit is normal.
139 pub signature: git::signing::SignatureStatus,
140 /// Notes on this commit, one entry per `refs/notes/*` namespace that has
141 /// one. Empty for the ordinary unannotated commit.
142 pub notes: Vec<CommitNote>,
143 /// Whether this reader may add, edit or remove a note here: the repo owner
144 /// or a collaborator who can push. False for everyone signed out.
145 pub can_write_notes: bool,
146 /// The note just saved was merged with somebody else's concurrent edit, so
147 /// what is on screen is not what was typed. Set from the redirect that
148 /// follows a write, since the page cannot work it out from the repository.
149 pub notes_merged: bool,
150 pub diff_files: Vec<git::DiffFile>,
151 pub total_files: usize,
152 pub total_additions: usize,
153 pub total_deletions: usize,
154 pub open_issue_count: i64,
155 pub is_owner: bool,
156 pub active_tab: &'static str,
157 }
158
159 /// The repo's Notes tab: every `refs/notes/*` namespace with its count, and a
160 /// feed of the annotations in the selected one.
161 #[derive(Template)]
162 #[template(path = "pages/git/notes.html")]
163 pub struct GitNotesTemplate {
164 pub csrf_token: CsrfTokenOption,
165 pub session_user: Option<SessionUser>,
166 pub owner: String,
167 pub repo_name: String,
168 pub current_ref: String,
169 pub refs: Vec<git::RefInfo>,
170 pub namespaces: Vec<NamespaceSummary>,
171 /// Namespace the feed belongs to. `None` for a repository with no notes.
172 pub selected: Option<String>,
173 pub rows: Vec<AnnotationRow>,
174 /// Notes in the selected namespace, which exceeds `rows.len()` when the feed
175 /// was capped.
176 pub total: usize,
177 /// The search these rows answer, echoed back into the box.
178 pub search: String,
179 pub commits_only: bool,
180 /// 1-based page of the feed. Named apart from `page` because Askama would
181 /// otherwise shadow nothing usefully and the template reads better for it.
182 pub page_number: usize,
183 pub has_more: bool,
184 /// Whether the rows came from the Postgres index. False means the
185 /// repository was walked, which cannot search or page, so the controls are
186 /// not offered.
187 pub from_index: bool,
188 pub open_issue_count: i64,
189 pub is_owner: bool,
190 pub active_tab: &'static str,
191 }
192
193 /// The repo's Tags tab: every tag, with the annotation an annotated tag
194 /// carries. The message on an annotated tag is a release note the author
195 /// already wrote and no forge shows.
196 #[derive(Template)]
197 #[template(path = "pages/git/tags.html")]
198 pub struct GitTagsTemplate {
199 pub csrf_token: CsrfTokenOption,
200 pub session_user: Option<SessionUser>,
201 pub owner: String,
202 pub repo_name: String,
203 pub current_ref: String,
204 pub refs: Vec<git::RefInfo>,
205 pub tags: Vec<git::TagInfo>,
206 pub open_issue_count: i64,
207 pub is_owner: bool,
208 pub active_tab: &'static str,
209 }
210
211 /// The repository's `refs/replace/*` mappings.
212 ///
213 /// Reachable by URL on every repository and linked from the overview only when
214 /// there is something to see, because the overwhelming majority of repositories
215 /// have no replacements and a permanently empty tab is noise.
216 #[derive(Template)]
217 #[template(path = "pages/git/replace.html")]
218 pub struct GitReplaceTemplate {
219 pub csrf_token: CsrfTokenOption,
220 pub session_user: Option<SessionUser>,
221 pub owner: String,
222 pub repo_name: String,
223 pub current_ref: String,
224 pub refs: Vec<git::RefInfo>,
225 pub replacements: Vec<git::Replacement>,
226 pub open_issue_count: i64,
227 pub is_owner: bool,
228 pub active_tab: &'static str,
229 }
230
231 /// Blame view for a single file.
232 #[derive(Template)]
233 #[template(path = "pages/git/blame.html")]
234 pub struct GitBlameTemplate {
235 pub csrf_token: CsrfTokenOption,
236 pub session_user: Option<SessionUser>,
237 pub owner: String,
238 pub repo_name: String,
239 pub current_ref: String,
240 pub refs: Vec<git::RefInfo>,
241 pub file_path: String,
242 pub filename: String,
243 pub breadcrumbs: Vec<git::Breadcrumb>,
244 pub blame_lines: Vec<git::BlameLine>,
245 /// Commits on this page that carry a note, mapped to how many namespaces
246 /// annotate them. Asked once per distinct commit, not once per line.
247 pub annotated: HashMap<String, usize>,
248 pub open_issue_count: i64,
249 pub is_owner: bool,
250 pub active_tab: &'static str,
251 }
252
253 /// User's repository listing page.
254 #[derive(Template)]
255 #[template(path = "pages/git/repos.html")]
256 pub struct GitUserReposTemplate {
257 pub csrf_token: CsrfTokenOption,
258 pub session_user: Option<SessionUser>,
259 pub owner: String,
260 pub repos: Vec<crate::db::DbGitRepo>,
261 pub is_owner: bool,
262 }
263
264 /// Public explore page listing all public repos across all users.
265 #[derive(Template)]
266 #[template(path = "pages/git/explore.html")]
267 pub struct GitExploreTemplate {
268 pub csrf_token: CsrfTokenOption,
269 pub session_user: Option<SessionUser>,
270 pub repos: Vec<crate::db::git_repos::PublicRepoWithOwner>,
271 pub page: usize,
272 pub has_more: bool,
273 pub total_count: i64,
274 }
275
276 /// Per-file commit history with breadcrumb context.
277 #[derive(Template)]
278 #[template(path = "pages/git/file_log.html")]
279 pub struct GitFileLogTemplate {
280 pub csrf_token: CsrfTokenOption,
281 pub session_user: Option<SessionUser>,
282 pub owner: String,
283 pub repo_name: String,
284 pub current_ref: String,
285 pub refs: Vec<git::RefInfo>,
286 pub file_path: String,
287 pub filename: String,
288 pub breadcrumbs: Vec<git::Breadcrumb>,
289 pub commits: Vec<git::CommitInfo>,
290 /// Commits on this page that carry a note, mapped to how many namespaces
291 /// annotate them. One batched lookup per page, never one per row.
292 pub annotated: HashMap<String, usize>,
293 pub page: usize,
294 pub has_more: bool,
295 pub open_issue_count: i64,
296 pub is_owner: bool,
297 pub active_tab: &'static str,
298 }
299
300 // Git Issues
301
302 /// Issue list page with status tabs and search (read-only).
303 #[derive(Template)]
304 #[template(path = "pages/git/issues.html")]
305 pub struct GitIssueListTemplate {
306 pub csrf_token: CsrfTokenOption,
307 pub session_user: Option<SessionUser>,
308 pub owner: String,
309 pub repo_name: String,
310 pub current_ref: String,
311 pub issues: Vec<crate::db::DbIssueWithMeta>,
312 pub open_count: i64,
313 pub closed_count: i64,
314 pub current_status: String,
315 pub search_query: String,
316 pub current_page: i64,
317 pub total_pages: i64,
318 /// Whether the current viewer is the repo owner (for settings link).
319 pub is_owner: bool,
320 /// Email address for submitting new issues.
321 pub email_address: String,
322 }
323
324 /// Issue detail page with comments (read-only).
325 #[derive(Template)]
326 #[template(path = "pages/git/issue.html")]
327 pub struct GitIssueDetailTemplate {
328 pub csrf_token: CsrfTokenOption,
329 pub session_user: Option<SessionUser>,
330 pub owner: String,
331 pub repo_name: String,
332 pub current_ref: String,
333 pub issue: crate::db::DbIssue,
334 pub author_username: String,
335 pub comments: Vec<crate::db::DbIssueCommentWithAuthor>,
336 pub is_owner: bool,
337 pub open_issue_count: i64,
338 /// Email address for submitting new issues.
339 pub email_address: String,
340 }
341
342 /// Repository settings page (owner only).
343 #[derive(Template)]
344 #[template(path = "pages/git/settings.html")]
345 pub struct GitRepoSettingsTemplate {
346 pub csrf_token: CsrfTokenOption,
347 pub session_user: Option<SessionUser>,
348 pub owner: String,
349 pub repo_name: String,
350 pub current_ref: String,
351 pub repo: crate::db::DbGitRepo,
352 pub open_issue_count: i64,
353 /// Owner's projects for the link dropdown.
354 pub projects: Vec<crate::db::DbProject>,
355 /// ID of the currently linked project as a string (for dropdown comparison).
356 pub linked_project_id: String,
357 }
358