+2
-1
src/mixins/comment.pug
+2
-1
src/mixins/comment.pug
···
1
1
include ../utils
2
+
include postUtils
2
3
3
4
mixin infoContainer(data, next_id, prev_id)
4
5
- var hats = (data.is_submitter?['op']:[]).concat(data.distinguished=="moderator"?['mod']:[])
···
51
52
summary.expand-comments
52
53
+infoContainer(data, next_id, prev_id)
53
54
div.comment-body
54
-
!= data.body_html
55
+
!= convertInlineImageLinks(data.body_html)
55
56
if hasReplyData
56
57
div.replies
57
58
- var total = data.replies.data.children.length
+7
-5
src/mixins/header.pug
+7
-5
src/mixins/header.pug
···
1
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')
2
4
div.header
3
5
div.header-item
4
-
a(href=`/`) home
6
+
a(href=`/?${sortQuery}&${viewQuery}`) home
5
7
div.header-item
6
-
a(href=`/r/all`) all
8
+
a(href=`/r/all?${sortQuery}&${viewQuery}`) all
7
9
div.header-item
8
-
a(href=`/search`) search
10
+
a(href=`/search?${sortQuery}&${viewQuery}`) search
9
11
div.header-item
10
-
a(href=`/subs`) subs
12
+
a(href=`/subs?${sortQuery}&${viewQuery}`) subs
11
13
if user
12
14
div.header-item
13
-
a(href='/dashboard') #{user.username}
15
+
a(href=`/dashboard?${sortQuery}&${viewQuery}`) #{user.username}
14
16
|
15
17
a(href='/logout') (logout)
16
18
else
+75
-42
src/mixins/post.pug
+75
-42
src/mixins/post.pug
···
2
2
include postUtils
3
3
mixin post(p, currentUrl)
4
4
- var from = encodeURIComponent(currentUrl)
5
-
article(class=`post ${p.stickied?"sticky":""}`)
6
-
div.post-container
7
-
div.post-text
8
-
div.title-container
9
-
a(href=`/comments/${p.id}?from=${from}`)
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(class=`post`)
8
+
div.post-container(class=`${query.view} ${p.stickied?"sticky":""}`)
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}`)
10
12
!= p.title
11
13
span.domain (#{p.domain})
12
14
div.info-container
···
22
24
| ·
23
25
| #{timeDifference(Date.now(), p.created * 1000)}
24
26
| ·
25
-
a(href=`/r/${p.subreddit}`) r/#{p.subreddit}
27
+
a(href=`/r/${p.subreddit}?sort=${sortQuery}&view=${viewQuery}`) r/#{p.subreddit}
26
28
| ·
27
-
a(href=`/comments/${p.id}?from=${from}`) #{fmtnum (p.num_comments)} ↩
28
-
div.media-preview
29
+
a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩
30
+
if (query.view == "card" && !isPostGallery(p) && !isPostImage(p) && !isPostVideo(p) && p.selftext_html)
31
+
div.self-text-overflow(class='card')
32
+
if query.view == "card" && (p.spoiler || p.over_18)
33
+
div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`)
34
+
h2
35
+
!= p.over_18 ? 'nsfw' : 'spoiler'
36
+
div.self-text(class='card')
37
+
!= convertInlineImageLinks(p.selftext_html)
38
+
div.media-preview(class=`${query.view}`)
39
+
- var onclick = query.view != "card" ? `toggleDetails('${p.id}')` : ``
40
+
if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p))
41
+
div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`)
42
+
h2
43
+
!= p.over_18 ? 'nsfw' : 'spoiler'
29
44
if isPostGallery(p)
30
45
- var item = postGalleryItems(p)[0]
31
-
img(src=item.url onclick=`toggleDetails('${p.id}')`)
46
+
if query.view == "card"
47
+
div.gallery(class=`${query.view}`)
48
+
each item in postGalleryItems(p)
49
+
div.gallery-item(class=`${query.view}`)
50
+
a(href=`/media/${item.url}`)
51
+
img(src=item.url loading="lazy")
52
+
div.gallery-item-idx(class=`${query.view}`)
53
+
| #{`${item.idx}/${item.total}`}
54
+
else
55
+
img(src=item.url onclick=onclick)
32
56
else if isPostImage(p)
33
-
- var url = postThumbnail(p)
34
-
img(src=url onclick=`toggleDetails('${p.id}')`)
57
+
- var url = query.view == "card" ? p.url : postThumbnail(p)
58
+
#{query.view == "card" ? "a href=/media/" + url : span}
59
+
img(src=url onclick=onclick)
35
60
else if isPostVideo(p)
36
-
- var url = p.secure_media.reddit_video.scrubber_media_url
37
-
video(src=url data-dashjs-player width='100px' height='100px' onclick=`toggleDetails('${p.id}')`)
61
+
- var decodedVideos = decodePostVideoUrls(p)
62
+
if query.view == "card"
63
+
video(controls="" muted="" data-dashjs-player="" preload="metadata" poster=decodedVideos[4])
64
+
// HLS
65
+
source(src=decodedVideos[0])
66
+
// Dash
67
+
source(src=decodedVideos[1])
68
+
// Fallback
69
+
source(src=decodedVideos[2])
70
+
else
71
+
video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` width="100px" height="100px")
72
+
// Scrubber
73
+
source(src=decodedVideos[3])
38
74
else if isPostLink(p)
39
75
a(href=p.url)
76
+
if (query.view == 'card')
77
+
| #{p.domain}
40
78
| ↗
41
79
42
-
if isPostGallery(p)
43
-
details(id=`${p.id}`)
44
-
summary.expand-post expand gallery
45
-
div.gallery
46
-
each item in postGalleryItems(p)
47
-
div.gallery-item
48
-
div.gallery-item-idx
49
-
| #{`${item.idx}/${item.total}`}
50
-
a(href=`/media/${item.url}`)
51
-
img(src=item.url loading="lazy")
52
-
button(onclick=`toggleDetails('${p.id}')`) close
53
-
else if isPostImage(p)
54
-
details(id=`${p.id}`)
55
-
summary.expand-post expand image
56
-
a(href=`/media/${p.url}`)
57
-
img(src=p.url loading="lazy").post-media
58
-
button(onclick=`toggleDetails('${p.id}')`) close
59
-
else if isPostVideo(p)
60
-
details(id=`${p.id}`)
61
-
summary.expand-post expand video
62
-
- var url = p.secure_media.reddit_video.dash_url
63
-
video(src=url controls data-dashjs-player loading="lazy").post-media
64
-
button(onclick=`toggleDetails('${p.id}')`) close
65
-
else if isPostLink(p)
80
+
if query.view == "compact" && (isPostGallery(p) || isPostImage(p) || isPostVideo(p))
66
81
details(id=`${p.id}`)
67
-
summary.expand-post expand link
68
-
a(href=`${p.url}`)
69
-
| #{p.url}
70
-
br
71
-
button(onclick=`toggleDetails('${p.id}')`) close
82
+
summary.expand-post expand media
83
+
div.image-viewer
84
+
if isPostGallery(p)
85
+
div.gallery
86
+
each item in postGalleryItems(p)
87
+
div.gallery-item
88
+
div.gallery-item-idx
89
+
| #{`${item.idx}/${item.total}`}
90
+
a(href=`/media/${item.url}`)
91
+
img(src=item.url loading="lazy")
92
+
else if isPostImage(p)
93
+
a(href=`/media/${p.url}`)
94
+
img(src=p.url loading="lazy").post-media
95
+
else if isPostVideo(p)
96
+
video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy").post-media
97
+
//HLS
98
+
source(src=decodedVideos[0])
99
+
// Dash
100
+
source(src=decodedVideos[1])
101
+
// Fallback
102
+
source(src=decodedVideos[2])
103
+
button(onclick=`toggleDetails('${p.id}')`)
104
+
| close
+37
-6
src/mixins/postUtils.pug
+37
-6
src/mixins/postUtils.pug
···
10
10
}
11
11
-
12
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";
13
+
if (p.over_18) {
14
+
return "/nsfw.svg";
17
15
} else if (p.thumbnail == "spoiler") {
18
-
return "/spoiler.svg";
16
+
return "/spoiler.svg";
17
+
} else if (p.thumbnail == "image" || p.thumbnail == "") {
18
+
return p.url;
19
19
} else {
20
-
return p.thumbnail;
20
+
return p.thumbnail;
21
21
}
22
22
}
23
23
-
···
51
51
return null;
52
52
}
53
53
}
54
+
-
55
+
function convertInlineImageLinks(html) {
56
+
// Find all anchors that href to preview.redd.it, i.redd.it, i.imgur.com
57
+
// and contain just a link to the same href
58
+
const expression = /<a href="(http[s]?:\/\/(?:preview\.redd\.it|i\.redd\.it|i\.imgur\.com).*?)">\1?<\/a>/g;
59
+
const matches = html.matchAll(expression);
60
+
var result = html;
61
+
matches.forEach((match) => {
62
+
// Replace each occurrence with an actual img tag
63
+
result = result.replace(match[0], '<a href="' + match[1] + '"><img class="inline" src="' + match[1] + '"></a>');
64
+
})
65
+
66
+
return result;
67
+
}
68
+
-
69
+
function decodePostVideoUrls(p) {
70
+
// Video URLs have querystring separators that are HTML-encoded, so replace them.
71
+
const expression = /&/g;
72
+
73
+
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, '&') : '';
74
+
75
+
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, '&') : '';
76
+
77
+
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, '&') : '';
78
+
79
+
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, '&') : '';
80
+
81
+
var poster_url = p.preview && p.preview.images ? p.preview.images[0].source.url.replace(expression, '&') : '';
82
+
83
+
return [hls_url, dash_url, fallback_url, scrubber_url, poster_url];
84
+
}
+210
-15
src/public/styles.css
+210
-15
src/public/styles.css
···
47
47
color: var(--text-color);
48
48
}
49
49
50
+
body:has(details.card[open]) {
51
+
overflow: hidden;
52
+
}
53
+
54
+
body.media-maximized {
55
+
/* Fix for Safari User Agent stylesheet */
56
+
margin: 0;
57
+
}
58
+
59
+
body.media-maximized.zoom,
60
+
div.media-maximized.container.zoom {
61
+
overflow: auto;
62
+
}
63
+
64
+
img.media-maximized {
65
+
cursor: zoom-in;
66
+
}
67
+
68
+
img.media-maximized.zoom {
69
+
max-width: unset;
70
+
max-height: unset;
71
+
cursor: zoom-out;
72
+
}
73
+
50
74
main {
51
75
display: flex;
52
76
flex-direction: column;
···
58
82
.info-container a,
59
83
.comment-info-container a,
60
84
.sort-opts a,
85
+
.view-opts a,
61
86
.more a
62
87
{
63
88
text-decoration: none;
···
92
117
align-items: center;
93
118
}
94
119
95
-
.sorting {
120
+
.sorting,
121
+
.viewing {
96
122
margin-top: 20px;
97
123
}
98
124
99
-
.sort-opts {
125
+
.sort-opts,
126
+
.view-opts {
100
127
display: grid;
101
128
margin: 10px;
102
129
}
103
130
104
-
.sort-opts {
131
+
.sort-opts,
132
+
.view-opts {
105
133
grid-template-columns: repeat(2, 1fr);
106
134
grid-template-rows: repeat(5, 1fr);
107
135
grid-auto-flow: column;
136
+
}
137
+
138
+
.view-opts {
139
+
grid-template-rows: repeat(2, 1fr);
108
140
}
109
141
110
142
.footer {
···
135
167
font-size: 0.9rem;
136
168
}
137
169
170
+
.post-container.card {
171
+
border: 1px solid var(--bg-color-muted);
172
+
border-radius: 8px;
173
+
display: block;
174
+
}
175
+
176
+
.post-text.card {
177
+
padding: 0.9rem;
178
+
padding-top: 0.3rem;
179
+
}
180
+
181
+
.self-text-overflow.card {
182
+
/* For spoiler positioning */
183
+
position: relative;
184
+
padding-top: 0.3rem;
185
+
max-height: 10vh;
186
+
overflow: hidden;
187
+
overflow-wrap: break-word;
188
+
display: block;
189
+
}
190
+
191
+
.self-text.card {
192
+
overflow: hidden;
193
+
display: -webkit-box;
194
+
/* Safari on iOS <= 17 */
195
+
-webkit-box-orient: vertical;
196
+
-webkit-line-clamp: 3;
197
+
line-clamp: 3;
198
+
text-overflow: ellipsis;
199
+
}
200
+
201
+
.media-preview.card {
202
+
position: relative;
203
+
padding: 0.3rem;
204
+
padding-bottom: 0.3rem;
205
+
}
206
+
207
+
.media-preview.card > img {
208
+
cursor: pointer;
209
+
}
210
+
211
+
.gallery.card {
212
+
align-items: center;
213
+
scroll-snap-type: both mandatory;
214
+
}
215
+
216
+
.gallery-item.card {
217
+
max-width: 100%;
218
+
width: 100%;
219
+
scroll-snap-align: center;
220
+
}
221
+
222
+
.gallery-item-idx.card {
223
+
text-align: center;
224
+
}
225
+
226
+
.spoiler {
227
+
background-color: rbga(var(--bg-color-muted), 0.2);
228
+
/* Safari on iOS <= 17 */
229
+
-webkit-backdrop-filter: blur(3rem);
230
+
backdrop-filter: blur(3rem);
231
+
border-radius: 4px;
232
+
233
+
position: absolute;
234
+
top: 0;
235
+
left: 0;
236
+
237
+
box-sizing: border-box;
238
+
display: flex;
239
+
height: 100%;
240
+
width: 100%;
241
+
242
+
justify-content: center;
243
+
align-items: center;
244
+
245
+
cursor: pointer;
246
+
247
+
z-index: 10;
248
+
}
249
+
250
+
.gallery-item-idx.card,
251
+
.spoiler > h2 {
252
+
text-shadow: 0.1rem 0.1rem 1rem var(--bg-color-muted);
253
+
}
254
+
138
255
.comments-container {
139
256
font-size: 0.9rem;
140
257
}
···
179
296
height: 4rem;
180
297
}
181
298
299
+
.media-preview.card {
300
+
padding: unset;
301
+
}
302
+
303
+
.media-preview.card img,
304
+
.media-preview.card video {
305
+
border-radius: 6px;
306
+
307
+
max-height: 40vh;
308
+
max-width: 95%;
309
+
310
+
display: block;
311
+
width: unset;
312
+
height: unset;
313
+
margin-left: auto;
314
+
margin-right: auto;
315
+
margin-bottom: 0.5rem;
316
+
317
+
object-fit: fill;
318
+
}
319
+
320
+
.media-preview.card a {
321
+
font-size: 1.5rem;
322
+
padding: unset;
323
+
padding-left: 1rem;
324
+
}
325
+
326
+
.media-preview.card a:has(img) {
327
+
font-size: 0rem;
328
+
padding: unset;
329
+
}
330
+
182
331
.media-preview a {
183
332
font-size: 2rem;
184
333
text-decoration: none;
185
334
padding: 1rem;
186
335
}
187
336
188
-
.media-maximized-container {
189
-
display: flex;
190
-
justify-content: center;
191
-
align-items: center;
192
-
width: 100vw;
193
-
height: 100vh;
194
-
overflow: hidden;
195
-
}
196
-
197
337
.media-maximized {
198
338
max-width: 100vw;
199
339
max-height: 100vh;
···
204
344
object-fit: contain;
205
345
}
206
346
347
+
.media-maximized.container {
348
+
display: flex;
349
+
justify-content: center;
350
+
align-items: center;
351
+
width: 100vw;
352
+
height: 100vh;
353
+
overflow: hidden;
354
+
}
355
+
207
356
.post-author {
208
357
display: none
209
358
}
···
233
382
width: 5rem;
234
383
height: 5rem;
235
384
}
385
+
.media-preview.card img,
386
+
.media-preview.card video
387
+
{
388
+
max-height: 50vh;
389
+
}
390
+
.media-preview.card a {
391
+
font-size: 1rem;
392
+
margin: 0.7rem;
393
+
padding: initial;
394
+
}
395
+
.self-text.card {
396
+
-webkit-line-clamp: 4;
397
+
line-clamp: 4;
398
+
}
236
399
.post-author {
237
400
display: inline
238
401
}
···
242
405
form {
243
406
width: 40%;
244
407
}
245
-
.sort-opts {
408
+
.sort-opts,
409
+
.view-opts {
246
410
grid-template-columns: repeat(9, 1fr);
247
411
grid-template-rows: repeat(1, 1fr);
248
412
grid-auto-flow: row;
···
259
423
{
260
424
width: 5rem;
261
425
height: 5rem;
426
+
}
427
+
.media-preview.card img,
428
+
.media-preview.card video
429
+
{
430
+
max-height: 30vh;
262
431
}
263
432
.media-preview a {
264
433
font-size: 2rem;
265
434
padding: 2rem;
266
435
}
436
+
.media-preview.card a {
437
+
font-size: 1rem;
438
+
margin: 0.5rem;
439
+
padding: initial;
440
+
}
441
+
.self-text.card {
442
+
-webkit-line-clamp: 6;
443
+
line-clamp: 6;
444
+
}
267
445
.post-author {
268
446
display: inline
269
447
}
···
273
451
form {
274
452
width: 20%;
275
453
}
276
-
.sort-opts {
454
+
.sort-opts,
455
+
.view-opts {
277
456
grid-template-columns: repeat(9, 1fr);
278
457
grid-template-rows: repeat(1, 1fr);
279
458
grid-auto-flow: row;
···
285
464
flex: 1 1 40%;
286
465
width: 40%;
287
466
}
288
-
.sort-opts {
467
+
.media-preview.card img,
468
+
.media-preview.card video
469
+
{
470
+
max-height: 20vh;
471
+
}
472
+
.sort-opts,
473
+
.view-opts {
289
474
grid-template-columns: repeat(9, 1fr);
290
475
grid-template-rows: repeat(1, 1fr);
291
476
grid-auto-flow: row;
···
353
538
text-decoration: none;
354
539
}
355
540
541
+
.title-container.card > a {
542
+
font-size: 1.125rem;
543
+
font-weight: bold;
544
+
}
545
+
356
546
.title-container > a:hover {
357
547
text-decoration: underline;
358
548
}
···
363
553
364
554
.header a,
365
555
.sort-opts a,
556
+
.view-opts a,
366
557
.sub-title a {
367
558
color: var(--text-color);
368
559
}
···
630
821
border-radius: 2px;
631
822
border: 4px solid var(--sticky-color);
632
823
}
824
+
825
+
.inline {
826
+
max-width: 100%;
827
+
}
+35
-8
src/routes/index.js
+35
-8
src/routes/index.js
···
16
16
const subs = db
17
17
.query("SELECT * FROM subscriptions WHERE user_id = $id")
18
18
.all({ id: req.user.id });
19
+
20
+
const qs = req.query ? ('?' + new URLSearchParams(req.query).toString()) : '';
21
+
19
22
if (subs.length === 0) {
20
-
res.redirect("/r/all");
23
+
res.redirect(`/r/all${qs}`);
21
24
} else {
22
25
const p = subs.map((s) => s.subreddit).join("+");
23
-
res.redirect(`/r/${p}`);
26
+
res.redirect(`/r/${p}${qs}`);
24
27
}
25
28
});
26
29
···
31
34
const query = req.query ? req.query : {};
32
35
if (!query.sort) {
33
36
query.sort = "hot";
37
+
}
38
+
if (!query.view) {
39
+
query.view = "compact";
34
40
}
35
41
36
42
let isSubbed = false;
···
47
53
48
54
const [posts, about] = await Promise.all([postsReq, aboutReq]);
49
55
56
+
if (query.view == 'card' && posts && posts.posts) {
57
+
posts.posts.forEach(unescape_selftext);
58
+
}
59
+
50
60
res.render("index", {
51
61
subreddit,
52
62
posts,
···
71
81
data: unescape_submission(response),
72
82
user: req.user,
73
83
from: req.query.from,
84
+
query: req.query,
74
85
});
75
86
});
76
87
···
104
115
)
105
116
.all({ id: req.user.id });
106
117
107
-
res.render("subs", { subs, user: req.user });
118
+
res.render("subs", { subs, user: req.user, query: req.query });
108
119
});
109
120
110
121
// GET /search
111
122
router.get("/search", authenticateToken, async (req, res) => {
112
-
res.render("search", { user: req.user });
123
+
res.render("search", { user: req.user, query: req.query });
113
124
});
114
125
115
126
// GET /sub-search
···
133
144
message,
134
145
user: req.user,
135
146
original_query: req.query.q,
147
+
query: req.query,
136
148
});
137
149
}
138
150
});
···
147
159
items.length === 0
148
160
? "no results found"
149
161
: `showing ${items.length} results`;
162
+
163
+
if (req.query.view == 'card' && items) {
164
+
items.forEach(unescape_selftext);
165
+
}
166
+
150
167
res.render("post-search", {
151
168
items,
152
169
after,
···
154
171
user: req.user,
155
172
original_query: req.query.q,
156
173
currentUrl: req.url,
174
+
query: req.query,
157
175
});
158
176
}
159
177
});
···
176
194
usedAt: Date.parse(inv.usedAt),
177
195
}));
178
196
}
179
-
res.render("dashboard", { invites, isAdmin, user: req.user });
197
+
res.render("dashboard", { invites, isAdmin, user: req.user, query: req.query });
180
198
});
181
199
182
200
router.get("/create-invite", authenticateAdmin, async (req, res) => {
···
359
377
const post = response.submission.data;
360
378
const comments = response.comments;
361
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
362
392
if (post.selftext_html) {
363
393
post.selftext_html = he.decode(post.selftext_html);
364
394
}
365
-
comments.forEach(unescape_comment);
366
-
367
-
return { post, comments };
368
395
}
369
396
370
397
function unescape_comment(comment) {
+4
-2
src/views/comments.pug
+4
-2
src/views/comments.pug
···
6
6
7
7
- var post = data.post
8
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')
9
11
doctype html
10
12
html
11
13
+head(post.title)
···
27
29
|
28
30
| ·
29
31
|
30
-
a(href=`/r/${post.subreddit}`) r/#{post.subreddit}
32
+
a(href=`/r/${post.subreddit}?${sortQuery}&${viewQuery}`) r/#{post.subreddit}
31
33
32
34
div.info-container
33
35
- var domain = (new URL(post.url)).hostname
···
65
67
66
68
if post.selftext_html
67
69
div.self-text
68
-
!= post.selftext_html
70
+
!= convertInlineImageLinks(post.selftext_html)
69
71
70
72
hr
71
73
+21
-12
src/views/index.pug
+21
-12
src/views/index.pug
···
2
2
include ../mixins/header
3
3
include ../mixins/head
4
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'
5
7
doctype html
6
8
html
7
9
+head("home")
···
14
16
div.sub-title
15
17
h1
16
18
if isMulti
17
-
a(href=`/`) lurker
19
+
a(href=`/?sort=${sortQuery}&view=${viewQuery}`) lurker
18
20
else
19
-
a(href=`/r/${subreddit}`)
21
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
20
22
| r/#{subreddit}
21
23
if !isMulti
22
24
div#button-container
···
32
34
a(href="https://donate.stripe.com/dR62bTaZH1295Da4gg") oppiliappan
33
35
|, author of lurker
34
36
hr
35
-
details
37
+
details.sort-details
36
38
summary.sorting sorting by #{query.sort + (query.t?' '+query.t:'')}
37
39
div.sort-opts
38
40
div
39
-
a(href=`/r/${subreddit}?sort=hot`) hot
41
+
a(href=`/r/${subreddit}?sort=hot&view=${viewQuery}`) hot
40
42
div
41
-
a(href=`/r/${subreddit}?sort=new`) new
43
+
a(href=`/r/${subreddit}?sort=new&view=${viewQuery}`) new
42
44
div
43
-
a(href=`/r/${subreddit}?sort=rising`) rising
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
44
50
div
45
-
a(href=`/r/${subreddit}?sort=top`) top
51
+
a(href=`/r/${subreddit}?sort=top&t=week&view=${viewQuery}`) top week
46
52
div
47
-
a(href=`/r/${subreddit}?sort=top&t=day`) top day
53
+
a(href=`/r/${subreddit}?sort=top&t=month&view=${viewQuery}`) top month
48
54
div
49
-
a(href=`/r/${subreddit}?sort=top&t=week`) top week
55
+
a(href=`/r/${subreddit}?sort=top&t=year&view=${viewQuery}`) top year
50
56
div
51
-
a(href=`/r/${subreddit}?sort=top&t=month`) top month
57
+
a(href=`/r/${subreddit}?sort=top&t=all&view=${viewQuery}`) top all
58
+
details.view-details
59
+
summary.viewing viewing as #{viewQuery}
60
+
div.view-opts
52
61
div
53
-
a(href=`/r/${subreddit}?sort=top&t=year`) top year
62
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=compact`) compact
54
63
div
55
-
a(href=`/r/${subreddit}?sort=top&t=all`) top all
64
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=card`) card
56
65
57
66
if posts
58
67
each child in posts.posts
+8
-3
src/views/media.pug
+8
-3
src/views/media.pug
···
2
2
doctype html
3
3
html
4
4
+head("home")
5
-
body
6
-
div.media-maximized-container
5
+
script(type='text/javascript').
6
+
function toggleZoom() {
7
+
Array.from(document.getElementsByClassName('media-maximized')).forEach(element => element.classList.toggle('zoom'));
8
+
}
9
+
10
+
body.media-maximized
11
+
div.media-maximized.container
7
12
if kind == 'img'
8
-
img(src=url).media-maximized
13
+
img(src=url onclick=`toggleZoom()`).media-maximized
9
14
else
10
15
video(src=url controls).media-maximized
+4
src/views/post-search.pug
+4
src/views/post-search.pug
···
2
2
include ../mixins/header
3
3
include ../mixins/head
4
4
5
+
- var viewQuery = query && query.view ? query.view : 'compact'
6
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
5
7
doctype html
6
8
html
7
9
+head("search posts")
···
14
16
form(action="/post-search" method="get").search-bar
15
17
- var prefill = original_query ?? "";
16
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)
17
21
button(type="submit").search-button go
18
22
if message
19
23
div.search-message
+6
src/views/search.pug
+6
src/views/search.pug
···
1
1
include ../mixins/header
2
2
include ../mixins/head
3
3
4
+
- var viewQuery = query && query.view ? query.view : 'compact'
5
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
4
6
doctype html
5
7
html
6
8
+head("search subreddits")
···
14
16
form(action="/sub-search" method="get").search-bar
15
17
- var prefill = original_query ?? "";
16
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)
17
21
button(type="submit").search-button go
18
22
19
23
hr
···
23
27
form(action="/post-search" method="get").search-bar
24
28
- var prefill = original_query ?? "";
25
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)
26
32
button(type="submit").search-button go
27
33
p
28
34
| you can narrow search results using filters:
+5
-1
src/views/sub-search.pug
+5
-1
src/views/sub-search.pug
···
1
1
include ../mixins/header
2
2
include ../mixins/head
3
3
4
+
- var viewQuery = (query && query.view) ? query.view : 'compact'
5
+
- var sortQuery = (query && query.sort) ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
4
6
doctype html
5
7
html
6
8
+head("search subreddits")
···
13
15
form(action="/sub-search" method="get").search-bar
14
16
- var prefill = original_query ?? "";
15
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)
16
20
button(type="submit").search-button go
17
21
if message
18
22
div.search-message
···
25
29
- var isSubbed = subs.includes(subreddit)
26
30
div.sub-title
27
31
h3
28
-
a(href=`/r/${subreddit}`)
32
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
29
33
| r/#{subreddit}
30
34
div#button-container
31
35
if isSubbed
+3
-1
src/views/subs.pug
+3
-1
src/views/subs.pug
···
1
1
include ../mixins/header
2
2
include ../mixins/head
3
3
4
+
- var viewQuery = query && query.view ? query.view : 'compact'
5
+
- var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
4
6
doctype html
5
7
html
6
8
+head("subscriptions")
···
16
18
- var isSubbed = true
17
19
div.sub-title
18
20
h4
19
-
a(href=`/r/${subreddit}`)
21
+
a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
20
22
| r/#{subreddit}
21
23
div#button-container
22
24
if isSubbed