Skip to main content

max / makenotwork

9.0 KB · 315 lines History Blame Raw
1 //! Askama template definitions for all HTML pages and fragments.
2 //!
3 //! Split by domain:
4 //! - `public`: landing, auth, content, blog, discover, health
5 //! - `dashboard`: creator dashboards, admin, export, account management
6 //! - `partials`: HTMX fragments, tab content, alerts, form status
7
8 mod dashboard;
9 mod embed;
10 mod partials;
11 mod public;
12
13 pub use dashboard::*;
14 pub use embed::*;
15 pub use partials::*;
16 pub use public::*;
17
18 use askama::Template;
19 use axum::{
20 http::StatusCode,
21 response::{Html, IntoResponse, Response},
22 };
23
24 /// Base context shared by all templates.
25 /// Note: csrf_token is Option to allow templates to work without CSRF
26 /// but all authenticated pages should include it.
27 pub type CsrfTokenOption = Option<String>;
28
29 /// One frame of the composable click-through carousel (`partials/carousel.html`).
30 ///
31 /// A carousel is just an ordered `&[CarouselFrame]`; the same macro renders it
32 /// on any surface (app product pages, landing), only the frame list differs.
33 /// Build a `Vec<CarouselFrame>` on a page template and pass it to the macro.
34 ///
35 /// Prefer [`CarouselFrame::new`] over a struct literal: it makes the alt text a
36 /// required, named argument and nudges (in debug builds) toward alt that
37 /// actually describes the screenshot. A carousel frame is a meaningful image,
38 /// so alt is not optional and should not be a filename or a bare label like
39 /// "screenshot". A screen-reader user should get the same information a
40 /// sighted viewer does.
41 #[derive(Clone)]
42 pub struct CarouselFrame {
43 /// Image URL (typically an optimized screenshot under `/static/images/shots/`).
44 pub image: String,
45 /// Alt text describing the screenshot. Required, every frame is an image.
46 pub alt: String,
47 /// Optional caption shown under the frame.
48 pub caption: Option<String>,
49 }
50
51 impl CarouselFrame {
52 /// Build a frame, nudging toward helpful alt text.
53 ///
54 /// In debug builds this asserts the alt text is non-empty and looks like a
55 /// description rather than a filename or a one-word placeholder. The checks
56 /// are debug-only so they guide authors during development without ever
57 /// affecting a release render.
58 pub fn new(image: impl Into<String>, alt: impl Into<String>) -> Self {
59 let image = image.into();
60 let alt = alt.into();
61 debug_assert!(
62 !alt.trim().is_empty(),
63 "carousel frame `{image}` has empty alt text, describe what the \
64 screenshot shows so screen-reader users get the same information \
65 sighted viewers do"
66 );
67 debug_assert!(
68 !alt.trim_start().starts_with('/') && !alt.contains(".webp") && !alt.contains(".png"),
69 "carousel frame alt text looks like a filename (`{alt}`), write a \
70 human description of what the screenshot shows instead"
71 );
72 Self {
73 image,
74 alt,
75 caption: None,
76 }
77 }
78
79 /// Attach an optional caption shown under the frame.
80 #[must_use]
81 pub fn with_caption(mut self, caption: impl Into<String>) -> Self {
82 self.caption = Some(caption.into());
83 self
84 }
85 }
86
87 /// Helper to convert any Askama template into an Axum response.
88 fn render_template<T: Template>(template: T) -> Response {
89 match template.render() {
90 Ok(html) => Html(html).into_response(),
91 Err(err) => {
92 tracing::error!(error = ?err, "template rendering error");
93 (StatusCode::INTERNAL_SERVER_ERROR, "Template error").into_response()
94 }
95 }
96 }
97
98 /// Implement `IntoResponse` for one or more Askama template structs.
99 macro_rules! impl_into_response {
100 ($($T:ty),+ $(,)?) => {
101 $(
102 impl IntoResponse for $T {
103 fn into_response(self) -> Response {
104 render_template(self)
105 }
106 }
107 )+
108 };
109 }
110
111 impl_into_response!(
112 // Public pages
113 SandboxTemplate,
114 PolicyTemplate,
115 IndexTemplate,
116 LibraryTemplate,
117 CartTemplate,
118 LoginTemplate,
119 TwoFactorTemplate,
120 OAuthAuthorizeTemplate,
121 ForgotPasswordTemplate,
122 ResetPasswordTemplate,
123 UserTemplate,
124 ProjectTemplate,
125 ProjectPaywallTemplate,
126 ItemTemplate,
127 LibraryAudioTemplate,
128 LibraryDownloadsTemplate,
129 LibraryLockedTemplate,
130 LibraryTextTemplate,
131 LibraryVideoTemplate,
132 TextReaderTemplate,
133 AudioPlayerTemplate,
134 VideoPlayerTemplate,
135 DiscoverTemplate,
136 DiscoverResultsTemplate,
137 PurchaseTemplate,
138 ReceiptTemplate,
139 BuyPageTemplate,
140 FeedTemplate,
141 StripeConnectDisclaimerTemplate,
142 // Blog pages
143 ProjectBlogTemplate,
144 BlogPostTemplate,
145 // Documentation pages
146 DocTemplate,
147 DocIndexTemplate,
148 // Pricing calculator
149 PricingTemplate,
150 FeeCalculatorPartial,
151 // Platform economics + runway disclosure
152 EconomicsTemplate,
153 // Use cases
154 UseCasesTemplate,
155 // Team
156 TeamTemplate,
157 // Fan+
158 FanPlusTemplate,
159 // Creator invite system
160 CreatorsTemplate,
161 // Email & account
162 EmailResultTemplate,
163 EmailPreferencesTemplate,
164 ConfirmDeleteTemplate,
165 AccountDeletedTemplate,
166 AcknowledgeTemplate,
167 // Health
168 HealthTemplate,
169 // Dashboard pages
170 DashboardUserTemplate,
171 DashboardProjectTemplate,
172 DashboardItemTemplate,
173 // Admin
174 AdminWaitlistTemplate,
175 AdminUsersTemplate,
176 AdminUploadsTemplate,
177 AdminScanAuditTemplate,
178 AdminAppealsTemplate,
179 AdminReportsTemplate,
180 AdminSignupsTemplate,
181 AdminMetricsTemplate,
182 AdminCompCodesTemplate,
183 // Export, import & account management
184 ExportPortalTemplate,
185 ImportPortalTemplate,
186 DeleteAccountTemplate,
187 BlogEditorTemplate,
188 // HTMX partials
189 AlertTemplate,
190 LibraryStatusTemplate,
191 ExportDownloadTemplate,
192 ExportContentReadyTemplate,
193 TransactionsTableTemplate,
194 UserProfileTabTemplate,
195 UserSettingsTabTemplate,
196 UserAccountTabTemplate,
197 UserSshKeysTabTemplate,
198 UserPaymentsTabTemplate,
199 UserProjectsTabTemplate,
200 UserCreatorTabTemplate,
201 ProjectOverviewTabTemplate,
202 ProjectContentTabTemplate,
203 ProjectAnalyticsTabTemplate,
204 UserAnalyticsTabTemplate,
205 BuyerContactsPartialTemplate,
206 PayoutSummaryPartialTemplate,
207 ProjectSettingsTabTemplate,
208 ProjectCodeTabTemplate,
209 ProjectBlogTabTemplate,
210 ProjectSubscriptionsTabTemplate,
211 ProjectMembersTabTemplate,
212 ProjectMonetizationTabTemplate,
213 ItemEditRowTemplate,
214 // Admin partials
215 AdminWaitlistEntriesTemplate,
216 AdminCompCodesEntriesTemplate,
217 AdminUserEntriesTemplate,
218 AdminUploadEntriesTemplate,
219 AdminQueueSummaryTemplate,
220 AdminAppealEntriesTemplate,
221 AdminReportEntriesTemplate,
222 SuspensionBannerTemplate,
223 // License keys
224 ItemLicenseKeysTemplate,
225 // Promo codes
226 PromoCodesListTemplate,
227 ProjectPromotionsTabTemplate,
228 // Sessions
229 UserSessionsPartialTemplate,
230 // SyncKit
231 UserSyncKitTabTemplate,
232 ProjectSyncKitTabTemplate,
233 // Forums (Multithreaded)
234 UserForumsTabTemplate,
235 // Media library
236 UserMediaTabTemplate,
237 // Support
238 UserSupportTabTemplate,
239 // Collections
240 CollectionTemplate,
241 // Library tabs
242 LibraryPurchasesTabTemplate,
243 LibraryFeedTabTemplate,
244 LibraryCollectionsTabTemplate,
245 LibraryContactsTabTemplate,
246 LibraryCommunitiesTabTemplate,
247 // Follow button
248 FollowButtonTemplate,
249 TagFollowToggleTemplate,
250 // Tag suggestions
251 TagSuggestionsTemplate,
252 // Item analytics
253 ItemAnalyticsPartialTemplate,
254 // Item dashboard tabs
255 ItemOverviewTabTemplate,
256 ItemDetailsTabTemplate,
257 ItemPricingTabTemplate,
258 ItemFilesTabTemplate,
259 ItemSalesTabTemplate,
260 ItemEmbedTabTemplate,
261 // Onboarding checklist
262 OnboardingChecklistPartialTemplate,
263 // Tag tree browser
264 TagTreeTemplate,
265 // TOTP 2FA
266 TotpSetupTemplate,
267 TotpStatusTemplate,
268 // Passkeys
269 PasskeyListTemplate,
270 // Git source browser
271 GitRepoTemplate,
272 GitTreeTemplate,
273 GitFileTemplate,
274 GitCommitsTemplate,
275 GitCommitDetailTemplate,
276 GitNotesTemplate,
277 GitTagsTemplate,
278 GitReplaceTemplate,
279 GitBlameTemplate,
280 GitUserReposTemplate,
281 GitExploreTemplate,
282 GitFileLogTemplate,
283 // Git issues
284 GitIssueListTemplate,
285 GitIssueDetailTemplate,
286 GitRepoSettingsTemplate,
287 // Join wizard
288 WizardJoinTemplate,
289 WizardJoinAccountTemplate,
290 WizardJoinProfileTemplate,
291 WizardJoinCompleteTemplate,
292 // Creation wizards, full pages
293 WizardProjectTemplate,
294 WizardItemTemplate,
295 // Creation wizards, project step partials
296 WizardProjectBasicsTemplate,
297 WizardProjectAppearanceTemplate,
298 WizardProjectMonetizationTemplate,
299 WizardProjectFirstContentTemplate,
300 WizardProjectPreviewTemplate,
301 // Creation wizards, item step partials
302 WizardItemTypeTemplate,
303 WizardItemBasicsTemplate,
304 WizardItemContentTemplate,
305 WizardItemSectionsTemplate,
306 WizardItemPricingTemplate,
307 WizardItemPreviewTemplate,
308 // Embed widgets
309 EmbedItemButtonTemplate,
310 EmbedItemCardTemplate,
311 EmbedItemPlayerTemplate,
312 EmbedTipButtonTemplate,
313 EmbedProjectCardTemplate,
314 );
315