+7
-5
src/mixins/header.pug
+7
-5
src/mixins/header.pug
···
1
mixin header(user)
2
div.header
3
div.header-item
4
-
a(href=`/`) home
5
div.header-item
6
-
a(href=`/r/all`) all
7
div.header-item
8
-
a(href=`/search`) search
9
div.header-item
10
-
a(href=`/subs`) subs
11
if user
12
div.header-item
13
-
a(href='/dashboard') #{user.username}
14
|
15
a(href='/logout') (logout)
16
else
···
1
mixin header(user)
2
+
- var viewQuery = 'view=' + (query && query.view ? query.view : 'compact')
3
+
- var sortQuery = 'sort=' + (query ? (query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot') : 'hot')
4
div.header
5
div.header-item
6
+
a(href=`/?${sortQuery}&${viewQuery}`) home
7
div.header-item
8
+
a(href=`/r/all?${sortQuery}&${viewQuery}`) all
9
div.header-item
10
+
a(href=`/search?${sortQuery}&${viewQuery}`) search
11
div.header-item
12
+
a(href=`/subs?${sortQuery}&${viewQuery}`) subs
13
if user
14
div.header-item
15
+
a(href=`/dashboard?${sortQuery}&${viewQuery}`) #{user.username}
16
|
17
a(href='/logout') (logout)
18
else
+65
-40
src/mixins/post.pug
+65
-40
src/mixins/post.pug
···
2
include postUtils
3
mixin post(p, currentUrl)
4
- var from = encodeURIComponent(currentUrl)
5
article.post
6
-
div.post-container
7
-
div.post-text
8
-
div.title-container
9
-
a(href=`/comments/${p.id}?from=${from}`)
10
!= p.title
11
span.domain (#{p.domain})
12
div.info-container
···
18
| ·
19
| #{timeDifference(Date.now(), p.created * 1000)}
20
| ·
21
-
a(href=`/r/${p.subreddit}`) r/#{p.subreddit}
22
| ·
23
-
a(href=`/comments/${p.id}?from=${from}`) #{fmtnum (p.num_comments)} ↩
24
-
div.media-preview
25
if isPostGallery(p)
26
- var item = postGalleryItems(p)[0]
27
img(src=item.url onclick=`toggleDetails('${p.id}')`)
28
else if isPostImage(p)
29
-
- var url = postThumbnail(p)
30
img(src=url onclick=`toggleDetails('${p.id}')`)
31
else if isPostVideo(p)
32
-
- var url = p.secure_media.reddit_video.scrubber_media_url
33
-
video(src=url data-dashjs-player width='100px' height='100px' onclick=`toggleDetails('${p.id}')`)
34
else if isPostLink(p)
35
a(href=p.url)
36
| ↗
37
38
-
if isPostGallery(p)
39
-
details(id=`${p.id}`)
40
-
summary.expand-post expand gallery
41
-
div.gallery
42
-
each item in postGalleryItems(p)
43
-
div.gallery-item
44
-
div.gallery-item-idx
45
-
| #{`${item.idx}/${item.total}`}
46
-
a(href=`/media/${item.url}`)
47
-
img(src=item.url loading="lazy")
48
-
button(onclick=`toggleDetails('${p.id}')`) close
49
-
else if isPostImage(p)
50
-
details(id=`${p.id}`)
51
-
summary.expand-post expand image
52
-
a(href=`/media/${p.url}`)
53
-
img(src=p.url loading="lazy").post-media
54
-
button(onclick=`toggleDetails('${p.id}')`) close
55
-
else if isPostVideo(p)
56
-
details(id=`${p.id}`)
57
-
summary.expand-post expand video
58
-
- var url = p.secure_media.reddit_video.dash_url
59
-
video(src=url controls data-dashjs-player loading="lazy").post-media
60
-
button(onclick=`toggleDetails('${p.id}')`) close
61
-
else if isPostLink(p)
62
-
details(id=`${p.id}`)
63
-
summary.expand-post expand link
64
-
a(href=`${p.url}`)
65
-
| #{p.url}
66
-
br
67
-
button(onclick=`toggleDetails('${p.id}')`) close
···
2
include postUtils
3
mixin post(p, currentUrl)
4
- var from = encodeURIComponent(currentUrl)
5
+
- var viewQuery = query && query.view ? query.view : 'compact'
6
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
7
article.post
8
+
div.post-container(class=`${query.view}`)
9
+
div.post-text(class=`${query.view}`)
10
+
div.title-container(class=`${query.view}`)
11
+
a(class=`${query.view}`, href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`)
12
!= p.title
13
span.domain (#{p.domain})
14
div.info-container
···
20
| ·
21
| #{timeDifference(Date.now(), p.created * 1000)}
22
| ·
23
+
a(href=`/r/${p.subreddit}?sort=${sortQuery}&view=${viewQuery}`) r/#{p.subreddit}
24
| ·
25
+
a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩
26
+
if (query.view == "card" && !isPostGallery(p) && !isPostImage(p) && !isPostVideo(p) && p.selftext_html)
27
+
div.self-text-overflow(class='card')
28
+
if query.view == "card" && (p.spoiler || p.over_18)
29
+
div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`)
30
+
h2
31
+
!= p.over_18 ? 'nsfw' : 'spoiler'
32
+
div.self-text(class='card')
33
+
!= convertInlineImageLinks(p.selftext_html)
34
+
div.media-preview(class=`${query.view}`)
35
+
if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p))
36
+
div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`)
37
+
h2
38
+
!= p.over_18 ? 'nsfw' : 'spoiler'
39
if isPostGallery(p)
40
- var item = postGalleryItems(p)[0]
41
img(src=item.url onclick=`toggleDetails('${p.id}')`)
42
else if isPostImage(p)
43
+
- var url = query.view == "card" ? p.url : postThumbnail(p)
44
img(src=url onclick=`toggleDetails('${p.id}')`)
45
else if isPostVideo(p)
46
+
- var decodedVideos = decodePostVideoUrls(p)
47
+
if query.view == "card"
48
+
video(controls="" muted="" data-dashjs-player="" preload="metadata" poster=decodedVideos[4])
49
+
// HLS
50
+
source(src=decodedVideos[0])
51
+
// Dash
52
+
source(src=decodedVideos[1])
53
+
// Fallback
54
+
source(src=decodedVideos[2])
55
+
else
56
+
video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` width="100px" height="100px")
57
+
// Scrubber
58
+
source(src=decodedVideos[3])
59
else if isPostLink(p)
60
a(href=p.url)
61
+
if (query.view == 'card')
62
+
| #{p.domain}
63
| ↗
64
65
+
if (isPostGallery(p) || isPostImage(p) || isPostVideo(p))
66
+
details(id=`${p.id}`,class=`${query.view}`)
67
+
summary.expand-post expand media
68
+
div.image-viewer(class=`${query.view}`)
69
+
if isPostGallery(p)
70
+
div.gallery(class=`${query.view}`)
71
+
each item in postGalleryItems(p)
72
+
div.gallery-item(class=`${query.view}`)
73
+
div.gallery-item-idx(class=`${query.view}`)
74
+
| #{`${item.idx}/${item.total}`}
75
+
a(href=`/media/${item.url}`)
76
+
img(src=item.url loading="lazy")
77
+
else if isPostImage(p)
78
+
a(href=`/media/${p.url}`)
79
+
img(src=p.url loading="lazy").post-media
80
+
else if isPostVideo(p)
81
+
video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy").post-media
82
+
//HLS
83
+
source(src=decodedVideos[0])
84
+
// Dash
85
+
source(src=decodedVideos[1])
86
+
// Fallback
87
+
source(src=decodedVideos[2])
88
+
button(onclick=`toggleDetails('${p.id}')`,class=`${query.view}`)
89
+
if (query.view == 'card')
90
+
| ╳
91
+
else
92
+
| close
+36
-6
src/mixins/postUtils.pug
+36
-6
src/mixins/postUtils.pug
···
10
}
11
-
12
function postThumbnail(p) {
13
-
if (p.thumbnail == "image" || p.thumbnail == "") {
14
-
return p.url;
15
-
} else if (p.over_18) {
16
-
return "/nsfw.svg";
17
} else if (p.thumbnail == "spoiler") {
18
-
return "/spoiler.svg";
19
} else {
20
-
return p.thumbnail;
21
}
22
}
23
-
···
51
return null;
52
}
53
}
···
10
}
11
-
12
function postThumbnail(p) {
13
+
if (p.over_18) {
14
+
return "/nsfw.svg";
15
} else if (p.thumbnail == "spoiler") {
16
+
return "/spoiler.svg";
17
+
} else if (p.thumbnail == "image" || p.thumbnail == "") {
18
+
return p.url;
19
} else {
20
+
return p.thumbnail;
21
}
22
}
23
-
···
51
return null;
52
}
53
}
54
+
-
55
+
function convertInlineImageLinks(html) {
56
+
// Find all anchors that href to https://preview.redd.it
57
+
const expression = /<a href="https:\/\/preview\.redd\.it.*?">(.*?)<\/a>/g;
58
+
const matches = html.matchAll(expression);
59
+
var result = html;
60
+
matches.forEach((match) => {
61
+
// Replace each occurrence with an actual img tag
62
+
result = result.replace(match[0], '<a href="' + match[1] + '"><img src="' + match[1] + '"></a>');
63
+
})
64
+
65
+
return result;
66
+
}
67
+
-
68
+
function decodePostVideoUrls(p) {
69
+
// Video URLs have querystring separators that are HTML-encoded, so replace them.
70
+
const expression = /&/g;
71
+
72
+
var hls_url = p.secure_media && p.secure_media.reddit_video && p.secure_media.reddit_video.hls_url ? p.secure_media.reddit_video.hls_url.replace(expression, '&') : '';
73
+
74
+
var dash_url = p.secure_media && p.secure_media.reddit_video && p.secure_media.reddit_video.dash_url ? p.secure_media.reddit_video.dash_url.replace(expression, '&') : '';
75
+
76
+
var fallback_url = p.secure_media && p.secure_media.reddit_video && p.secure_media.reddit_video.fallback_url ? p.secure_media.reddit_video.fallback_url.replace(expression, '&') : '';
77
+
78
+
var scrubber_url = p.secure_media && p.secure_media.reddit_video && p.secure_media.reddit_video.scrubber_media_url ? p.secure_media.reddit_video.scrubber_media_url.replace(expression, '&') : '';
79
+
80
+
var poster_url = p.preview && p.preview.images ? p.preview.images[0].source.url.replace(expression, '&') : '';
81
+
82
+
return [hls_url, dash_url, fallback_url, scrubber_url, poster_url];
83
+
}
+179
src/public/styles.css
+179
src/public/styles.css
···
43
color: var(--text-color);
44
}
45
46
main {
47
display: flex;
48
flex-direction: column;
···
127
font-size: 0.9rem;
128
}
129
130
.comments-container {
131
font-size: 0.9rem;
132
}
···
171
height: 4rem;
172
}
173
174
.media-preview a {
175
font-size: 2rem;
176
text-decoration: none;
···
225
width: 5rem;
226
height: 5rem;
227
}
228
.post-author {
229
display: inline
230
}
···
252
width: 5rem;
253
height: 5rem;
254
}
255
.media-preview a {
256
font-size: 2rem;
257
padding: 2rem;
258
}
259
.post-author {
260
display: inline
···
276
.post, .comments-container, .hero, .header, .footer {
277
flex: 1 1 40%;
278
width: 40%;
279
}
280
.sort-opts {
281
grid-template-columns: repeat(9, 1fr);
···
343
.title-container > a {
344
color: var(--text-color);
345
text-decoration: none;
346
}
347
348
.title-container > a:hover {
···
43
color: var(--text-color);
44
}
45
46
+
body:has(details.card[open]) {
47
+
overflow: hidden;
48
+
}
49
+
50
main {
51
display: flex;
52
flex-direction: column;
···
131
font-size: 0.9rem;
132
}
133
134
+
.post-container.card {
135
+
border: 1px solid var(--bg-color-muted);
136
+
border-radius: 16px;
137
+
display: block;
138
+
}
139
+
140
+
.post-text.card {
141
+
padding: 0.9rem;
142
+
padding-top: 0.3rem;
143
+
}
144
+
145
+
.self-text-overflow.card {
146
+
/* For spoiler positioning */
147
+
position: relative;
148
+
padding-top: 0.3rem;
149
+
max-height: 10vh;
150
+
overflow: hidden;
151
+
overflow-wrap: break-word;
152
+
display: block;
153
+
}
154
+
155
+
.self-text.card {
156
+
overflow: hidden;
157
+
display: -webkit-box;
158
+
/* Safari on iOS <= 17 */
159
+
-webkit-box-orient: vertical;
160
+
-webkit-line-clamp: 3;
161
+
line-clamp: 3;
162
+
text-overflow: ellipsis;
163
+
}
164
+
165
+
.media-preview.card {
166
+
position: relative;
167
+
padding: 0.3rem;
168
+
padding-bottom: 0.3rem;
169
+
}
170
+
171
+
.media-preview.card > img {
172
+
cursor: pointer;
173
+
}
174
+
175
+
.image-viewer.card {
176
+
/* Safari on iOS <= 17 */
177
+
-webkit-backdrop-filter: blur(2rem);
178
+
backdrop-filter: blur(2rem);
179
+
position: fixed;
180
+
inset: 0;
181
+
box-sizing: border-box;
182
+
display: flex;
183
+
height: 100%;
184
+
width: 100%;
185
+
justify-content: center;
186
+
align-items: center;
187
+
z-index: 100;
188
+
}
189
+
190
+
.image-viewer.card > button {
191
+
position: absolute;
192
+
top: 0;
193
+
right: 0;
194
+
margin: 1rem;
195
+
padding: initial;
196
+
height: 3rem;
197
+
width: 3rem;
198
+
font-size: 2rem;
199
+
border-radius: 100%;
200
+
cursor: pointer;
201
+
}
202
+
203
+
.image-viewer.card > a > img {
204
+
max-width: 100vw;
205
+
max-height: 100vh;
206
+
width: auto;
207
+
height: auto;
208
+
}
209
+
210
+
.gallery.card {
211
+
align-items: center;
212
+
}
213
+
214
+
.gallery-item.card > a > img {
215
+
max-width: 95vw;
216
+
max-height: 95vh;
217
+
width: auto;
218
+
height: auto;
219
+
}
220
+
221
+
.spoiler {
222
+
background-color: rbga(var(--bg-color-muted), 0.2);
223
+
/* Safari on iOS <= 17 */
224
+
-webkit-backdrop-filter: blur(3rem);
225
+
backdrop-filter: blur(3rem);
226
+
border-radius: 4px;
227
+
228
+
position: absolute;
229
+
top: 0;
230
+
left: 0;
231
+
232
+
box-sizing: border-box;
233
+
display: flex;
234
+
height: 100%;
235
+
width: 100%;
236
+
237
+
justify-content: center;
238
+
align-items: center;
239
+
240
+
cursor: pointer;
241
+
}
242
+
243
+
.gallery-item-idx.card,
244
+
.spoiler > h2 {
245
+
text-shadow: 0.1rem 0.1rem 1rem var(--bg-color-muted);
246
+
}
247
+
248
.comments-container {
249
font-size: 0.9rem;
250
}
···
289
height: 4rem;
290
}
291
292
+
.media-preview.card img,
293
+
.media-preview.card video {
294
+
border-radius: 16px;
295
+
296
+
max-height: 40vh;
297
+
max-width: 100%;
298
+
299
+
display: block;
300
+
width: initial;
301
+
height: initial;
302
+
margin-left: auto;
303
+
margin-right: auto;
304
+
margin-bottom: 1rem;
305
+
306
+
object-fit: fill;
307
+
}
308
+
309
+
.media-preview.card a {
310
+
font-size: 1.5rem;
311
+
margin: 1rem;
312
+
padding: initial;
313
+
}
314
+
315
.media-preview a {
316
font-size: 2rem;
317
text-decoration: none;
···
366
width: 5rem;
367
height: 5rem;
368
}
369
+
.media-preview.card img,
370
+
.media-preview.card video
371
+
{
372
+
max-height: 50vh;
373
+
}
374
+
.media-preview.card a {
375
+
font-size: 1rem;
376
+
margin: 0.7rem;
377
+
padding: initial;
378
+
}
379
+
.self-text.card {
380
+
-webkit-line-clamp: 4;
381
+
line-clamp: 4;
382
+
}
383
.post-author {
384
display: inline
385
}
···
407
width: 5rem;
408
height: 5rem;
409
}
410
+
.media-preview.card img,
411
+
.media-preview.card video
412
+
{
413
+
max-height: 30vh;
414
+
}
415
.media-preview a {
416
font-size: 2rem;
417
padding: 2rem;
418
+
}
419
+
.media-preview.card a {
420
+
font-size: 1rem;
421
+
margin: 0.5rem;
422
+
padding: initial;
423
+
}
424
+
.self-text.card {
425
+
-webkit-line-clamp: 6;
426
+
line-clamp: 6;
427
}
428
.post-author {
429
display: inline
···
445
.post, .comments-container, .hero, .header, .footer {
446
flex: 1 1 40%;
447
width: 40%;
448
+
}
449
+
.media-preview.card img,
450
+
.media-preview.card video
451
+
{
452
+
max-height: 20vh;
453
}
454
.sort-opts {
455
grid-template-columns: repeat(9, 1fr);
···
517
.title-container > a {
518
color: var(--text-color);
519
text-decoration: none;
520
+
}
521
+
522
+
.title-container.card > a {
523
+
font-size: 1.125rem;
524
+
font-weight: bold;
525
}
526
527
.title-container > a:hover {
+35
-8
src/routes/index.js
+35
-8
src/routes/index.js
···
16
const subs = db
17
.query("SELECT * FROM subscriptions WHERE user_id = $id")
18
.all({ id: req.user.id });
19
if (subs.length === 0) {
20
-
res.redirect("/r/all");
21
} else {
22
const p = subs.map((s) => s.subreddit).join("+");
23
-
res.redirect(`/r/${p}`);
24
}
25
});
26
···
31
const query = req.query ? req.query : {};
32
if (!query.sort) {
33
query.sort = "hot";
34
}
35
36
let isSubbed = false;
···
47
48
const [posts, about] = await Promise.all([postsReq, aboutReq]);
49
50
res.render("index", {
51
subreddit,
52
posts,
···
71
data: unescape_submission(response),
72
user: req.user,
73
from: req.query.from,
74
});
75
});
76
···
104
)
105
.all({ id: req.user.id });
106
107
-
res.render("subs", { subs, user: req.user });
108
});
109
110
// GET /search
111
router.get("/search", authenticateToken, async (req, res) => {
112
-
res.render("search", { user: req.user });
113
});
114
115
// GET /sub-search
···
133
message,
134
user: req.user,
135
original_query: req.query.q,
136
});
137
}
138
});
···
147
items.length === 0
148
? "no results found"
149
: `showing ${items.length} results`;
150
res.render("post-search", {
151
items,
152
after,
···
154
user: req.user,
155
original_query: req.query.q,
156
currentUrl: req.url,
157
});
158
}
159
});
···
176
usedAt: Date.parse(inv.usedAt),
177
}));
178
}
179
-
res.render("dashboard", { invites, isAdmin, user: req.user });
180
});
181
182
router.get("/create-invite", authenticateAdmin, async (req, res) => {
···
359
const post = response.submission.data;
360
const comments = response.comments;
361
362
if (post.selftext_html) {
363
post.selftext_html = he.decode(post.selftext_html);
364
}
365
-
comments.forEach(unescape_comment);
366
-
367
-
return { post, comments };
368
}
369
370
function unescape_comment(comment) {
···
16
const subs = db
17
.query("SELECT * FROM subscriptions WHERE user_id = $id")
18
.all({ id: req.user.id });
19
+
20
+
const qs = req.query ? ('?' + new URLSearchParams(req.query).toString()) : '';
21
+
22
if (subs.length === 0) {
23
+
res.redirect(`/r/all${qs}`);
24
} else {
25
const p = subs.map((s) => s.subreddit).join("+");
26
+
res.redirect(`/r/${p}${qs}`);
27
}
28
});
29
···
34
const query = req.query ? req.query : {};
35
if (!query.sort) {
36
query.sort = "hot";
37
+
}
38
+
if (!query.view) {
39
+
query.view = "compact";
40
}
41
42
let isSubbed = false;
···
53
54
const [posts, about] = await Promise.all([postsReq, aboutReq]);
55
56
+
if (query.view == 'card' && posts && posts.posts) {
57
+
posts.posts.forEach(unescape_selftext);
58
+
}
59
+
60
res.render("index", {
61
subreddit,
62
posts,
···
81
data: unescape_submission(response),
82
user: req.user,
83
from: req.query.from,
84
+
query: req.query,
85
});
86
});
87
···
115
)
116
.all({ id: req.user.id });
117
118
+
res.render("subs", { subs, user: req.user, query: req.query });
119
});
120
121
// GET /search
122
router.get("/search", authenticateToken, async (req, res) => {
123
+
res.render("search", { user: req.user, query: req.query });
124
});
125
126
// GET /sub-search
···
144
message,
145
user: req.user,
146
original_query: req.query.q,
147
+
query: req.query,
148
});
149
}
150
});
···
159
items.length === 0
160
? "no results found"
161
: `showing ${items.length} results`;
162
+
163
+
if (req.query.view == 'card' && items) {
164
+
items.forEach(unescape_selftext);
165
+
}
166
+
167
res.render("post-search", {
168
items,
169
after,
···
171
user: req.user,
172
original_query: req.query.q,
173
currentUrl: req.url,
174
+
query: req.query,
175
});
176
}
177
});
···
194
usedAt: Date.parse(inv.usedAt),
195
}));
196
}
197
+
res.render("dashboard", { invites, isAdmin, user: req.user, query: req.query });
198
});
199
200
router.get("/create-invite", authenticateAdmin, async (req, res) => {
···
377
const post = response.submission.data;
378
const comments = response.comments;
379
380
+
unescape_selftext(post);
381
+
comments.forEach(unescape_comment);
382
+
383
+
return { post, comments };
384
+
}
385
+
386
+
function unescape_selftext(post) {
387
+
// If called after getSubmissions
388
+
if (post.data && post.data.selftext_html) {
389
+
post.data.selftext_html = he.decode(post.data.selftext_html);
390
+
}
391
+
// If called after getSubmissionComments
392
if (post.selftext_html) {
393
post.selftext_html = he.decode(post.selftext_html);
394
}
395
}
396
397
function unescape_comment(comment) {
+4
-2
src/views/comments.pug
+4
-2
src/views/comments.pug
···
6
7
- var post = data.post
8
- var comments = data.comments
9
doctype html
10
html
11
+head(post.title)
···
27
|
28
| ·
29
|
30
-
a(href=`/r/${post.subreddit}`) r/#{post.subreddit}
31
32
div.info-container
33
- var domain = (new URL(post.url)).hostname
···
65
66
if post.selftext_html
67
div.self-text
68
-
!= post.selftext_html
69
70
hr
71
···
6
7
- var post = data.post
8
- var comments = data.comments
9
+
- var viewQuery = 'view=' + (query && query.view ? query.view : 'compact')
10
+
- var sortQuery = 'sort=' + (query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot')
11
doctype html
12
html
13
+head(post.title)
···
29
|
30
| ·
31
|
32
+
a(href=`/r/${post.subreddit}?${sortQuery}&${viewQuery}`) r/#{post.subreddit}
33
34
div.info-container
35
- var domain = (new URL(post.url)).hostname
···
67
68
if post.selftext_html
69
div.self-text
70
+
!= convertInlineImageLinks(post.selftext_html)
71
72
hr
73
+20
-13
src/views/index.pug
+20
-13
src/views/index.pug
···
2
include ../mixins/header
3
include ../mixins/head
4
include ../utils
5
doctype html
6
html
7
+head("home")
···
14
div.sub-title
15
h1
16
if isMulti
17
-
a(href=`/`) lurker
18
else
19
-
a(href=`/r/${subreddit}`)
20
| r/#{subreddit}
21
if !isMulti
22
div#button-container
···
32
a(href="https://donate.stripe.com/dR62bTaZH1295Da4gg") oppiliappan
33
|, author of lurker
34
hr
35
-
details
36
-
summary.sorting sorting by #{query.sort + (query.t?' '+query.t:'')}
37
div.sort-opts
38
div
39
-
a(href=`/r/${subreddit}?sort=hot`) hot
40
div
41
-
a(href=`/r/${subreddit}?sort=new`) new
42
div
43
-
a(href=`/r/${subreddit}?sort=rising`) rising
44
div
45
-
a(href=`/r/${subreddit}?sort=top`) top
46
div
47
-
a(href=`/r/${subreddit}?sort=top&t=day`) top day
48
div
49
-
a(href=`/r/${subreddit}?sort=top&t=week`) top week
50
div
51
-
a(href=`/r/${subreddit}?sort=top&t=month`) top month
52
div
53
-
a(href=`/r/${subreddit}?sort=top&t=year`) top year
54
div
55
-
a(href=`/r/${subreddit}?sort=top&t=all`) top all
56
57
if posts
58
each child in posts.posts
···
2
include ../mixins/header
3
include ../mixins/head
4
include ../utils
5
+
- var viewQuery = query && query.view ? query.view : 'compact'
6
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
7
doctype html
8
html
9
+head("home")
···
16
div.sub-title
17
h1
18
if isMulti
19
+
a(href=`/?${sortQuery}&${viewQuery}`) lurker
20
else
21
+
a(href=`/r/${subreddit}?${sortQuery}&${viewQuery}`)
22
| r/#{subreddit}
23
if !isMulti
24
div#button-container
···
34
a(href="https://donate.stripe.com/dR62bTaZH1295Da4gg") oppiliappan
35
|, author of lurker
36
hr
37
+
details.sort-details
38
+
summary.sorting sorting by #{query.sort + (query.t?' '+query.t:'')}, #{viewQuery} view
39
div.sort-opts
40
div
41
+
a(href=`/r/${subreddit}?sort=hot&view=${viewQuery}`) hot
42
div
43
+
a(href=`/r/${subreddit}?sort=new&view=${viewQuery}`) new
44
+
div
45
+
a(href=`/r/${subreddit}?sort=rising&view=${viewQuery}`) rising
46
+
div
47
+
a(href=`/r/${subreddit}?sort=top&view=${viewQuery}`) top
48
div
49
+
a(href=`/r/${subreddit}?sort=top&t=day&view=${viewQuery}`) top day
50
div
51
+
a(href=`/r/${subreddit}?sort=top&t=week&view=${viewQuery}`) top week
52
div
53
+
a(href=`/r/${subreddit}?sort=top&t=month&view=${viewQuery}`) top month
54
div
55
+
a(href=`/r/${subreddit}?sort=top&t=year&view=${viewQuery}`) top year
56
div
57
+
a(href=`/r/${subreddit}?sort=top&t=all&view=${viewQuery}`) top all
58
+
div.sort-opts
59
div
60
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=compact`) compact
61
div
62
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=card`) card
63
64
if posts
65
each child in posts.posts
+4
src/views/post-search.pug
+4
src/views/post-search.pug
···
2
include ../mixins/header
3
include ../mixins/head
4
5
doctype html
6
html
7
+head("search posts")
···
14
form(action="/post-search" method="get").search-bar
15
- var prefill = original_query ?? "";
16
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
17
button(type="submit").search-button go
18
if message
19
div.search-message
···
2
include ../mixins/header
3
include ../mixins/head
4
5
+
- var viewQuery = query && query.view ? query.view : 'compact'
6
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
7
doctype html
8
html
9
+head("search posts")
···
16
form(action="/post-search" method="get").search-bar
17
- var prefill = original_query ?? "";
18
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
19
+
input(type="hidden" name="sort" value=sortQuery)
20
+
input(type="hidden" name="view" value=viewQuery)
21
button(type="submit").search-button go
22
if message
23
div.search-message
+6
src/views/search.pug
+6
src/views/search.pug
···
1
include ../mixins/header
2
include ../mixins/head
3
4
doctype html
5
html
6
+head("search subreddits")
···
14
form(action="/sub-search" method="get").search-bar
15
- var prefill = original_query ?? "";
16
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
17
button(type="submit").search-button go
18
19
hr
···
23
form(action="/post-search" method="get").search-bar
24
- var prefill = original_query ?? "";
25
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
26
button(type="submit").search-button go
27
p
28
| you can narrow search results using filters:
···
1
include ../mixins/header
2
include ../mixins/head
3
4
+
- var viewQuery = query && query.view ? query.view : 'compact'
5
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
6
doctype html
7
html
8
+head("search subreddits")
···
16
form(action="/sub-search" method="get").search-bar
17
- var prefill = original_query ?? "";
18
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
19
+
input(type="hidden" name="sort" value=sortQuery)
20
+
input(type="hidden" name="view" value=viewQuery)
21
button(type="submit").search-button go
22
23
hr
···
27
form(action="/post-search" method="get").search-bar
28
- var prefill = original_query ?? "";
29
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
30
+
input(type="hidden" name="sort" value=sortQuery)
31
+
input(type="hidden" name="view" value=viewQuery)
32
button(type="submit").search-button go
33
p
34
| you can narrow search results using filters:
+5
-1
src/views/sub-search.pug
+5
-1
src/views/sub-search.pug
···
1
include ../mixins/header
2
include ../mixins/head
3
4
doctype html
5
html
6
+head("search subreddits")
···
13
form(action="/sub-search" method="get").search-bar
14
- var prefill = original_query ?? "";
15
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
16
button(type="submit").search-button go
17
if message
18
div.search-message
···
25
- var isSubbed = subs.includes(subreddit)
26
div.sub-title
27
h3
28
-
a(href=`/r/${subreddit}`)
29
| r/#{subreddit}
30
div#button-container
31
if isSubbed
···
1
include ../mixins/header
2
include ../mixins/head
3
4
+
- var viewQuery = (query && query.view) ? query.view : 'compact'
5
+
- var sortQuery = (query && query.sort) ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
6
doctype html
7
html
8
+head("search subreddits")
···
15
form(action="/sub-search" method="get").search-bar
16
- var prefill = original_query ?? "";
17
input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input
18
+
input(type="hidden" name="sort" value=sortQuery)
19
+
input(type="hidden" name="view" value=viewQuery)
20
button(type="submit").search-button go
21
if message
22
div.search-message
···
29
- var isSubbed = subs.includes(subreddit)
30
div.sub-title
31
h3
32
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
33
| r/#{subreddit}
34
div#button-container
35
if isSubbed
+3
-1
src/views/subs.pug
+3
-1
src/views/subs.pug
···
1
include ../mixins/header
2
include ../mixins/head
3
4
+
- var viewQuery = query && query.view ? query.view : 'compact'
5
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
6
doctype html
7
html
8
+head("subscriptions")
···
18
- var isSubbed = true
19
div.sub-title
20
h4
21
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
22
| r/#{subreddit}
23
div#button-container
24
if isSubbed