source: Klonkt/src/views/pages/user.ejs@ 16ce669

main
Last change on this file since 16ce669 was 8aa85d0, checked in by roboburr <roboburr@…>, 3 months ago

Prutter = premium Hub feature: Hub-only + behind premium gate

Prutter (DMs) is now hard-scoped to Hub mode (route 404s outside hub) and
locked behind the premium gate via the new premiumUnlocked() (= premium off
→ freely available, demos keep working; premium on → Patreon required).
UI entry points (topnav/bottom-tab/Send DM) follow the same condition.
Added to the premium description in Admin → Settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@…>

  • Property mode set to 100644
File size: 5.2 KB
Line 
1<div class="container user-page">
2 <header class="user-header">
3 <div class="user-avatar-large">
4 <% if (author.avatar_url) { %>
5 <img src="<%= author.avatar_url %>" alt="">
6 <% } else { %>
7 <span><%= author.username.charAt(0).toUpperCase() %></span>
8 <% } %>
9 </div>
10 <div class="user-meta">
11 <h1><%= author.username %></h1>
12 <% if (author.role === 'god') { %>
13 <span class="user-role-pill user-role-god">god</span>
14 <% } else if (author.role === 'admin') { %>
15 <span class="user-role-pill user-role-admin">admin</span>
16 <% } %>
17 <% if (author.bio) { %>
18 <p class="user-bio"><%= author.bio %></p>
19 <% } %>
20 <p class="user-stats">
21 <%= posts.length %> post<%= posts.length === 1 ? '' : 's' %> on this site
22 <% if (totalPosts > posts.length) { %>
23 &middot; <%= totalPosts %> total
24 <% } %>
25 <% if (author.created_at) { %>
26 &middot; joined <%= formatDate(author.created_at) %>
27 <% } %>
28 </p>
29 <% if (user && user.id !== author.id && site && site.enable_prutter && (typeof tenancy !== 'undefined' && tenancy === 'hub') && (typeof premiumUnlocked === 'undefined' || premiumUnlocked)) { %>
30 <p class="user-actions">
31 <a href="<%= siteUrlBase %>/prutter/new?to=<%= encodeURIComponent(author.username) %>" class="btn btn-primary">💬 Send DM</a>
32 </p>
33 <% } %>
34 </div>
35 </header>
36
37 <% if (!posts.length) { %>
38 <p class="user-empty">No posts on this site yet.</p>
39 <% } else { %>
40 <h2 class="user-posts-heading">Posts</h2>
41 <ol class="user-posts">
42 <% posts.forEach(function(p) { %>
43 <li class="user-post">
44 <% if (p.cover_image_url) { %>
45 <a href="/<%= p.slug %>" class="user-post-cover"
46 hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
47 hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading">
48 <img src="<%= p.cover_image_url %>" alt="">
49 </a>
50 <% } %>
51 <div class="user-post-body">
52 <h3 class="user-post-title">
53 <a href="/<%= p.slug %>"
54 hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
55 hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading">
56 <%= p.title || '(untitled)' %>
57 </a>
58 </h3>
59 <p class="user-post-meta">
60 <% if (p.published_at) { %><%= formatDate(p.published_at) %><% } %>
61 </p>
62 <% if (p.excerpt) { %>
63 <p class="user-post-excerpt"><%= p.excerpt %></p>
64 <% } %>
65 </div>
66 </li>
67 <% }); %>
68 </ol>
69 <% } %>
70</div>
71
72<style>
73.user-page { max-width: 720px; margin: 3rem auto; padding: 0 1rem; }
74
75.user-header {
76 display: flex;
77 gap: 1.25rem;
78 align-items: center;
79 margin-bottom: 2rem;
80 padding-bottom: 1.5rem;
81 border-bottom: 1px solid var(--rule);
82}
83.user-avatar-large {
84 width: 96px;
85 height: 96px;
86 border-radius: 50%;
87 background: var(--paper-2);
88 border: 2px solid var(--rule);
89 overflow: hidden;
90 flex-shrink: 0;
91 display: inline-flex;
92 align-items: center;
93 justify-content: center;
94}
95.user-avatar-large img { width: 100%; height: 100%; object-fit: cover; }
96.user-avatar-large span {
97 font-family: var(--font-display, serif);
98 font-size: 2.5rem;
99 font-weight: 700;
100 color: var(--accent);
101}
102.user-meta { flex: 1; min-width: 0; }
103.user-meta h1 {
104 font-family: var(--font-display, serif);
105 margin: 0;
106 font-size: 2rem;
107}
108.user-role-pill {
109 display: inline-block;
110 padding: 0.1rem 0.5rem;
111 border-radius: 10px;
112 font-size: 0.7rem;
113 text-transform: uppercase;
114 letter-spacing: 0.05em;
115 vertical-align: middle;
116 margin-left: 0.4rem;
117}
118.user-role-pill.user-role-god { background: var(--accent); color: white; }
119.user-role-pill.user-role-admin { background: var(--paper); color: var(--accent); border: 1px solid var(--accent); }
120.user-bio { margin: 0.4rem 0; color: var(--ink-soft); }
121.user-stats { margin: 0; color: var(--ink-muted, var(--ink-soft)); font-size: 0.85rem; }
122
123.user-posts-heading {
124 font-family: var(--font-display, serif);
125 font-size: 1.4rem;
126 margin: 0 0 1rem;
127}
128.user-empty { color: var(--ink-muted, var(--ink-soft)); text-align: center; margin: 3rem 0; }
129
130.user-posts { list-style: none; padding: 0; margin: 0; }
131.user-post { display: flex; gap: 1rem; padding: 1rem 0; border-bottom: 1px solid var(--rule); }
132.user-post:last-child { border-bottom: 0; }
133.user-post-cover { flex-shrink: 0; width: 96px; height: 96px; border-radius: 6px; overflow: hidden; background: var(--paper-2); }
134.user-post-cover img { width: 100%; height: 100%; object-fit: cover; }
135.user-post-body { flex: 1; min-width: 0; }
136.user-post-title { font-family: var(--font-display, serif); font-size: 1.15rem; margin: 0 0 0.2rem; }
137.user-post-title a { color: var(--ink); text-decoration: none; }
138.user-post-title a:hover { color: var(--accent); }
139.user-post-meta { color: var(--ink-muted, var(--ink-soft)); font-size: 0.85rem; margin: 0 0 0.4rem; }
140.user-post-excerpt { color: var(--ink-soft); margin: 0; line-height: 1.5; }
141
142@media (max-width: 500px) {
143 .user-header { flex-direction: column; text-align: center; }
144 .user-post-cover { width: 64px; height: 64px; }
145}
146</style>
Note: See TracBrowser for help on using the repository browser.