aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mixins/comment.pug3
-rw-r--r--src/mixins/header.pug12
-rw-r--r--src/mixins/post.pug107
-rw-r--r--src/mixins/postUtils.pug43
-rw-r--r--src/public/styles.css179
-rw-r--r--src/routes/index.js43
-rw-r--r--src/views/comments.pug6
-rw-r--r--src/views/index.pug33
-rw-r--r--src/views/post-search.pug4
-rw-r--r--src/views/search.pug6
-rw-r--r--src/views/sub-search.pug6
-rw-r--r--src/views/subs.pug4
12 files changed, 368 insertions, 78 deletions
diff --git a/src/mixins/comment.pug b/src/mixins/comment.pug
index 5b6f097..0609587 100644
--- a/src/mixins/comment.pug
+++ b/src/mixins/comment.pug
@@ -1,4 +1,5 @@
1include ../utils 1include ../utils
2include postUtils
2 3
3mixin infoContainer(data, next_id, prev_id) 4mixin infoContainer(data, next_id, prev_id)
4 - var hats = (data.is_submitter?['op']:[]).concat(data.distinguished=="moderator"?['mod']:[]) 5 - var hats = (data.is_submitter?['op']:[]).concat(data.distinguished=="moderator"?['mod']:[])
@@ -51,7 +52,7 @@ mixin comment(com, isfirst, parent_id, next_id, prev_id)
51 summary.expand-comments 52 summary.expand-comments
52 +infoContainer(data, next_id, prev_id) 53 +infoContainer(data, next_id, prev_id)
53 div.comment-body 54 div.comment-body
54 != data.body_html 55 != convertInlineImageLinks(data.body_html)
55 if hasReplyData 56 if hasReplyData
56 div.replies 57 div.replies
57 - var total = data.replies.data.children.length 58 - var total = data.replies.data.children.length
diff --git a/src/mixins/header.pug b/src/mixins/header.pug
index 9cf1e1a..7d56ff5 100644
--- a/src/mixins/header.pug
+++ b/src/mixins/header.pug
@@ -1,16 +1,18 @@
1mixin header(user) 1mixin 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 div.header 4 div.header
3 div.header-item 5 div.header-item
4 a(href=`/`) home 6 a(href=`/?${sortQuery}&${viewQuery}`) home
5 div.header-item 7 div.header-item
6 a(href=`/r/all`) all 8 a(href=`/r/all?${sortQuery}&${viewQuery}`) all
7 div.header-item 9 div.header-item
8 a(href=`/search`) search 10 a(href=`/search?${sortQuery}&${viewQuery}`) search
9 div.header-item 11 div.header-item
10 a(href=`/subs`) subs 12 a(href=`/subs?${sortQuery}&${viewQuery}`) subs
11 if user 13 if user
12 div.header-item 14 div.header-item
13 a(href='/dashboard') #{user.username} 15 a(href=`/dashboard?${sortQuery}&${viewQuery}`) #{user.username}
14 |  16 | 
15 a(href='/logout') (logout) 17 a(href='/logout') (logout)
16 else 18 else
diff --git a/src/mixins/post.pug b/src/mixins/post.pug
index 722a353..2e77402 100644
--- a/src/mixins/post.pug
+++ b/src/mixins/post.pug
@@ -2,11 +2,13 @@ include ../utils
2include postUtils 2include postUtils
3mixin post(p, currentUrl) 3mixin post(p, currentUrl)
4 - var from = encodeURIComponent(currentUrl) 4 - var from = encodeURIComponent(currentUrl)
5 article(class=`post ${p.stickied?"sticky":""}`) 5 - var viewQuery = query && query.view ? query.view : 'compact'
6 div.post-container 6 - var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
7 div.post-text 7 article(class=`post`)
8 div.title-container 8 div.post-container(class=`${query.view} ${p.stickied?"sticky":""}`)
9 a(href=`/comments/${p.id}?from=${from}`) 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 != p.title 12 != p.title
11 span.domain (#{p.domain}) 13 span.domain (#{p.domain})
12 div.info-container 14 div.info-container
@@ -22,50 +24,73 @@ mixin post(p, currentUrl)
22 |  ·  24 |  · 
23 | #{timeDifference(Date.now(), p.created * 1000)} 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)} ↩ 29 a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩
28 div.media-preview 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 if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p))
40 div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`)
41 h2
42 != p.over_18 ? 'nsfw' : 'spoiler'
29 if isPostGallery(p) 43 if isPostGallery(p)
30 - var item = postGalleryItems(p)[0] 44 - var item = postGalleryItems(p)[0]
31 img(src=item.url onclick=`toggleDetails('${p.id}')`) 45 img(src=item.url onclick=`toggleDetails('${p.id}')`)
32 else if isPostImage(p) 46 else if isPostImage(p)
33 - var url = postThumbnail(p) 47 - var url = query.view == "card" ? p.url : postThumbnail(p)
34 img(src=url onclick=`toggleDetails('${p.id}')`) 48 img(src=url onclick=`toggleDetails('${p.id}')`)
35 else if isPostVideo(p) 49 else if isPostVideo(p)
36 - var url = p.secure_media.reddit_video.scrubber_media_url 50 - var decodedVideos = decodePostVideoUrls(p)
37 video(src=url data-dashjs-player width='100px' height='100px' onclick=`toggleDetails('${p.id}')`) 51 if query.view == "card"
52 video(controls="" muted="" data-dashjs-player="" preload="metadata" poster=decodedVideos[4])
53 // HLS
54 source(src=decodedVideos[0])
55 // Dash
56 source(src=decodedVideos[1])
57 // Fallback
58 source(src=decodedVideos[2])
59 else
60 video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` width="100px" height="100px")
61 // Scrubber
62 source(src=decodedVideos[3])
38 else if isPostLink(p) 63 else if isPostLink(p)
39 a(href=p.url) 64 a(href=p.url)
65 if (query.view == 'card')
66 | #{p.domain}
40 | ↗ 67 | ↗
41 68
42 if isPostGallery(p) 69 if (isPostGallery(p) || isPostImage(p) || isPostVideo(p))
43 details(id=`${p.id}`) 70 details(id=`${p.id}`,class=`${query.view}`)
44 summary.expand-post expand gallery 71 summary.expand-post expand media
45 div.gallery 72 div.image-viewer(class=`${query.view}`)
46 each item in postGalleryItems(p) 73 if isPostGallery(p)
47 div.gallery-item 74 div.gallery(class=`${query.view}`)
48 div.gallery-item-idx 75 each item in postGalleryItems(p)
49 | #{`${item.idx}/${item.total}`} 76 div.gallery-item(class=`${query.view}`)
50 a(href=`/media/${item.url}`) 77 div.gallery-item-idx(class=`${query.view}`)
51 img(src=item.url loading="lazy") 78 | #{`${item.idx}/${item.total}`}
52 button(onclick=`toggleDetails('${p.id}')`) close 79 a(href=`/media/${item.url}`)
53 else if isPostImage(p) 80 img(src=item.url loading="lazy")
54 details(id=`${p.id}`) 81 else if isPostImage(p)
55 summary.expand-post expand image 82 a(href=`/media/${p.url}`)
56 a(href=`/media/${p.url}`) 83 img(src=p.url loading="lazy").post-media
57 img(src=p.url loading="lazy").post-media 84 else if isPostVideo(p)
58 button(onclick=`toggleDetails('${p.id}')`) close 85 video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy").post-media
59 else if isPostVideo(p) 86 //HLS
60 details(id=`${p.id}`) 87 source(src=decodedVideos[0])
61 summary.expand-post expand video 88 // Dash
62 - var url = p.secure_media.reddit_video.dash_url 89 source(src=decodedVideos[1])
63 video(src=url controls data-dashjs-player loading="lazy").post-media 90 // Fallback
64 button(onclick=`toggleDetails('${p.id}')`) close 91 source(src=decodedVideos[2])
65 else if isPostLink(p) 92 button(onclick=`toggleDetails('${p.id}')`,class=`${query.view}`)
66 details(id=`${p.id}`) 93 if (query.view == 'card')
67 summary.expand-post expand link 94 | ╳
68 a(href=`${p.url}`) 95 else
69 | #{p.url} 96 | close
70 br
71 button(onclick=`toggleDetails('${p.id}')`) close
diff --git a/src/mixins/postUtils.pug b/src/mixins/postUtils.pug
index 816adf7..b778cbd 100644
--- a/src/mixins/postUtils.pug
+++ b/src/mixins/postUtils.pug
@@ -10,14 +10,14 @@
10 } 10 }
11- 11-
12 function postThumbnail(p) { 12 function postThumbnail(p) {
13 if (p.thumbnail == "image" || p.thumbnail == "") { 13 if (p.over_18) {
14 return p.url; 14 return "/nsfw.svg";
15 } else if (p.over_18) {
16 return "/nsfw.svg";
17 } else if (p.thumbnail == "spoiler") { 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 } else { 19 } else {
20 return p.thumbnail; 20 return p.thumbnail;
21 } 21 }
22 } 22 }
23- 23-
@@ -51,3 +51,34 @@
51 return null; 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 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 = /&amp;/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 } \ No newline at end of file
diff --git a/src/public/styles.css b/src/public/styles.css
index fcb0752..5996c75 100644
--- a/src/public/styles.css
+++ b/src/public/styles.css
@@ -47,6 +47,10 @@ body {
47 color: var(--text-color); 47 color: var(--text-color);
48} 48}
49 49
50body:has(details.card[open]) {
51 overflow: hidden;
52}
53
50main { 54main {
51 display: flex; 55 display: flex;
52 flex-direction: column; 56 flex-direction: column;
@@ -135,6 +139,120 @@ nav {
135 font-size: 0.9rem; 139 font-size: 0.9rem;
136} 140}
137 141
142.post-container.card {
143 border: 1px solid var(--bg-color-muted);
144 border-radius: 16px;
145 display: block;
146}
147
148.post-text.card {
149 padding: 0.9rem;
150 padding-top: 0.3rem;
151}
152
153.self-text-overflow.card {
154 /* For spoiler positioning */
155 position: relative;
156 padding-top: 0.3rem;
157 max-height: 10vh;
158 overflow: hidden;
159 overflow-wrap: break-word;
160 display: block;
161}
162
163.self-text.card {
164 overflow: hidden;
165 display: -webkit-box;
166 /* Safari on iOS <= 17 */
167 -webkit-box-orient: vertical;
168 -webkit-line-clamp: 3;
169 line-clamp: 3;
170 text-overflow: ellipsis;
171}
172
173.media-preview.card {
174 position: relative;
175 padding: 0.3rem;
176 padding-bottom: 0.3rem;
177}
178
179.media-preview.card > img {
180 cursor: pointer;
181}
182
183.image-viewer.card {
184 /* Safari on iOS <= 17 */
185 -webkit-backdrop-filter: blur(2rem);
186 backdrop-filter: blur(2rem);
187 position: fixed;
188 inset: 0;
189 box-sizing: border-box;
190 display: flex;
191 height: 100%;
192 width: 100%;
193 justify-content: center;
194 align-items: center;
195 z-index: 100;
196}
197
198.image-viewer.card > button {
199 position: absolute;
200 top: 0;
201 right: 0;
202 margin: 1rem;
203 padding: initial;
204 height: 3rem;
205 width: 3rem;
206 font-size: 2rem;
207 border-radius: 100%;
208 cursor: pointer;
209}
210
211.image-viewer.card > a > img {
212 max-width: 100vw;
213 max-height: 100vh;
214 width: auto;
215 height: auto;
216}
217
218.gallery.card {
219 align-items: center;
220}
221
222.gallery-item.card > a > img {
223 max-width: 95vw;
224 max-height: 95vh;
225 width: auto;
226 height: auto;
227}
228
229.spoiler {
230 background-color: rbga(var(--bg-color-muted), 0.2);
231 /* Safari on iOS <= 17 */
232 -webkit-backdrop-filter: blur(3rem);
233 backdrop-filter: blur(3rem);
234 border-radius: 4px;
235
236 position: absolute;
237 top: 0;
238 left: 0;
239
240 box-sizing: border-box;
241 display: flex;
242 height: 100%;
243 width: 100%;
244
245 justify-content: center;
246 align-items: center;
247
248 cursor: pointer;
249}
250
251.gallery-item-idx.card,
252.spoiler > h2 {
253 text-shadow: 0.1rem 0.1rem 1rem var(--bg-color-muted);
254}
255
138.comments-container { 256.comments-container {
139 font-size: 0.9rem; 257 font-size: 0.9rem;
140} 258}
@@ -179,6 +297,29 @@ summary::before {
179 height: 4rem; 297 height: 4rem;
180} 298}
181 299
300.media-preview.card img,
301.media-preview.card video {
302 border-radius: 16px;
303
304 max-height: 40vh;
305 max-width: 100%;
306
307 display: block;
308 width: initial;
309 height: initial;
310 margin-left: auto;
311 margin-right: auto;
312 margin-bottom: 1rem;
313
314 object-fit: fill;
315}
316
317.media-preview.card a {
318 font-size: 1.5rem;
319 margin: 1rem;
320 padding: initial;
321}
322
182.media-preview a { 323.media-preview a {
183 font-size: 2rem; 324 font-size: 2rem;
184 text-decoration: none; 325 text-decoration: none;
@@ -233,6 +374,20 @@ form {
233 width: 5rem; 374 width: 5rem;
234 height: 5rem; 375 height: 5rem;
235 } 376 }
377 .media-preview.card img,
378 .media-preview.card video
379 {
380 max-height: 50vh;
381 }
382 .media-preview.card a {
383 font-size: 1rem;
384 margin: 0.7rem;
385 padding: initial;
386 }
387 .self-text.card {
388 -webkit-line-clamp: 4;
389 line-clamp: 4;
390 }
236 .post-author { 391 .post-author {
237 display: inline 392 display: inline
238 } 393 }
@@ -260,10 +415,24 @@ form {
260 width: 5rem; 415 width: 5rem;
261 height: 5rem; 416 height: 5rem;
262 } 417 }
418 .media-preview.card img,
419 .media-preview.card video
420 {
421 max-height: 30vh;
422 }
263 .media-preview a { 423 .media-preview a {
264 font-size: 2rem; 424 font-size: 2rem;
265 padding: 2rem; 425 padding: 2rem;
266 } 426 }
427 .media-preview.card a {
428 font-size: 1rem;
429 margin: 0.5rem;
430 padding: initial;
431 }
432 .self-text.card {
433 -webkit-line-clamp: 6;
434 line-clamp: 6;
435 }
267 .post-author { 436 .post-author {
268 display: inline 437 display: inline
269 } 438 }
@@ -285,6 +454,11 @@ form {
285 flex: 1 1 40%; 454 flex: 1 1 40%;
286 width: 40%; 455 width: 40%;
287 } 456 }
457 .media-preview.card img,
458 .media-preview.card video
459 {
460 max-height: 20vh;
461 }
288 .sort-opts { 462 .sort-opts {
289 grid-template-columns: repeat(9, 1fr); 463 grid-template-columns: repeat(9, 1fr);
290 grid-template-rows: repeat(1, 1fr); 464 grid-template-rows: repeat(1, 1fr);
@@ -353,6 +527,11 @@ form {
353 text-decoration: none; 527 text-decoration: none;
354} 528}
355 529
530.title-container.card > a {
531 font-size: 1.125rem;
532 font-weight: bold;
533}
534
356.title-container > a:hover { 535.title-container > a:hover {
357 text-decoration: underline; 536 text-decoration: underline;
358} 537}
diff --git a/src/routes/index.js b/src/routes/index.js
index 117b728..9415607 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -16,11 +16,14 @@ router.get("/", authenticateToken, async (req, res) => {
16 const subs = db 16 const subs = db
17 .query("SELECT * FROM subscriptions WHERE user_id = $id") 17 .query("SELECT * FROM subscriptions WHERE user_id = $id")
18 .all({ id: req.user.id }); 18 .all({ id: req.user.id });
19
20 const qs = req.query ? ('?' + new URLSearchParams(req.query).toString()) : '';
21
19 if (subs.length === 0) { 22 if (subs.length === 0) {
20 res.redirect("/r/all"); 23 res.redirect(`/r/all${qs}`);
21 } else { 24 } else {
22 const p = subs.map((s) => s.subreddit).join("+"); 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
@@ -32,6 +35,9 @@ router.get("/r/:subreddit", authenticateToken, async (req, res) => {
32 if (!query.sort) { 35 if (!query.sort) {
33 query.sort = "hot"; 36 query.sort = "hot";
34 } 37 }
38 if (!query.view) {
39 query.view = "compact";
40 }
35 41
36 let isSubbed = false; 42 let isSubbed = false;
37 if (!isMulti) { 43 if (!isMulti) {
@@ -47,6 +53,10 @@ router.get("/r/:subreddit", authenticateToken, async (req, res) => {
47 53
48 const [posts, about] = await Promise.all([postsReq, aboutReq]); 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 res.render("index", { 60 res.render("index", {
51 subreddit, 61 subreddit,
52 posts, 62 posts,
@@ -71,6 +81,7 @@ router.get("/comments/:id", authenticateToken, async (req, res) => {
71 data: unescape_submission(response), 81 data: unescape_submission(response),
72 user: req.user, 82 user: req.user,
73 from: req.query.from, 83 from: req.query.from,
84 query: req.query,
74 }); 85 });
75}); 86});
76 87
@@ -104,12 +115,12 @@ router.get("/subs", authenticateToken, async (req, res) => {
104 ) 115 )
105 .all({ id: req.user.id }); 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// GET /search 121// GET /search
111router.get("/search", authenticateToken, async (req, res) => { 122router.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// GET /sub-search 126// GET /sub-search
@@ -133,6 +144,7 @@ router.get("/sub-search", authenticateToken, async (req, res) => {
133 message, 144 message,
134 user: req.user, 145 user: req.user,
135 original_query: req.query.q, 146 original_query: req.query.q,
147 query: req.query,
136 }); 148 });
137 } 149 }
138}); 150});
@@ -147,6 +159,11 @@ router.get("/post-search", authenticateToken, async (req, res) => {
147 items.length === 0 159 items.length === 0
148 ? "no results found" 160 ? "no results found"
149 : `showing ${items.length} results`; 161 : `showing ${items.length} results`;
162
163 if (req.query.view == 'card' && items) {
164 items.forEach(unescape_selftext);
165 }
166
150 res.render("post-search", { 167 res.render("post-search", {
151 items, 168 items,
152 after, 169 after,
@@ -154,6 +171,7 @@ router.get("/post-search", authenticateToken, async (req, res) => {
154 user: req.user, 171 user: req.user,
155 original_query: req.query.q, 172 original_query: req.query.q,
156 currentUrl: req.url, 173 currentUrl: req.url,
174 query: req.query,
157 }); 175 });
158 } 176 }
159}); 177});
@@ -176,7 +194,7 @@ router.get("/dashboard", authenticateToken, async (req, res) => {
176 usedAt: Date.parse(inv.usedAt), 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
182router.get("/create-invite", authenticateAdmin, async (req, res) => { 200router.get("/create-invite", authenticateAdmin, async (req, res) => {
@@ -359,14 +377,23 @@ function unescape_submission(response) {
359 const post = response.submission.data; 377 const post = response.submission.data;
360 const comments = response.comments; 378 const comments = response.comments;
361 379
362 if (post.selftext_html) { 380 unescape_selftext(post);
363 post.selftext_html = he.decode(post.selftext_html);
364 }
365 comments.forEach(unescape_comment); 381 comments.forEach(unescape_comment);
366 382
367 return { post, comments }; 383 return { post, comments };
368} 384}
369 385
386function 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
370function unescape_comment(comment) { 397function unescape_comment(comment) {
371 if (comment.data.body_html) { 398 if (comment.data.body_html) {
372 comment.data.body_html = he.decode(comment.data.body_html); 399 comment.data.body_html = he.decode(comment.data.body_html);
diff --git a/src/views/comments.pug b/src/views/comments.pug
index 17af45a..1caf65a 100644
--- a/src/views/comments.pug
+++ b/src/views/comments.pug
@@ -6,6 +6,8 @@ include ../utils
6 6
7- var post = data.post 7- var post = data.post
8- var comments = data.comments 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')
9doctype html 11doctype html
10html 12html
11 +head(post.title) 13 +head(post.title)
@@ -27,7 +29,7 @@ html
27 | &nbsp;&nbsp; 29 | &nbsp;&nbsp;
28 | · 30 | ·
29 | &nbsp;&nbsp; 31 | &nbsp;&nbsp;
30 a(href=`/r/${post.subreddit}`) r/#{post.subreddit} 32 a(href=`/r/${post.subreddit}?${sortQuery}&${viewQuery}`) r/#{post.subreddit}
31 33
32 div.info-container 34 div.info-container
33 - var domain = (new URL(post.url)).hostname 35 - var domain = (new URL(post.url)).hostname
@@ -65,7 +67,7 @@ html
65 67
66 if post.selftext_html 68 if post.selftext_html
67 div.self-text 69 div.self-text
68 != post.selftext_html 70 != convertInlineImageLinks(post.selftext_html)
69 71
70 hr 72 hr
71 73
diff --git a/src/views/index.pug b/src/views/index.pug
index 0f00efe..847adbb 100644
--- a/src/views/index.pug
+++ b/src/views/index.pug
@@ -2,6 +2,8 @@ include ../mixins/post
2include ../mixins/header 2include ../mixins/header
3include ../mixins/head 3include ../mixins/head
4include ../utils 4include ../utils
5 - var viewQuery = query && query.view ? query.view : 'compact'
6 - var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot'
5doctype html 7doctype html
6html 8html
7 +head("home") 9 +head("home")
@@ -14,9 +16,9 @@ html
14 div.sub-title 16 div.sub-title
15 h1 17 h1
16 if isMulti 18 if isMulti
17 a(href=`/`) lurker 19 a(href=`/?sort=${sortQuery}&view=${viewQuery}`) lurker
18 else 20 else
19 a(href=`/r/${subreddit}`) 21 a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
20 | r/#{subreddit} 22 | r/#{subreddit}
21 if !isMulti 23 if !isMulti
22 div#button-container 24 div#button-container
@@ -32,27 +34,32 @@ html
32 a(href="https://donate.stripe.com/dR62bTaZH1295Da4gg") oppiliappan 34 a(href="https://donate.stripe.com/dR62bTaZH1295Da4gg") oppiliappan
33 |, author of lurker 35 |, author of lurker
34 hr 36 hr
35 details 37 details.sort-details
36 summary.sorting sorting by #{query.sort + (query.t?' '+query.t:'')} 38 summary.sorting sorting by #{query.sort + (query.t?' '+query.t:'')}, #{viewQuery} view
37 div.sort-opts 39 div.sort-opts
38 div 40 div
39 a(href=`/r/${subreddit}?sort=hot`) hot 41 a(href=`/r/${subreddit}?sort=hot&view=${viewQuery}`) hot
40 div 42 div
41 a(href=`/r/${subreddit}?sort=new`) new 43 a(href=`/r/${subreddit}?sort=new&view=${viewQuery}`) new
42 div 44 div
43 a(href=`/r/${subreddit}?sort=rising`) rising 45 a(href=`/r/${subreddit}?sort=rising&view=${viewQuery}`) rising
44 div 46 div
45 a(href=`/r/${subreddit}?sort=top`) top 47 a(href=`/r/${subreddit}?sort=top&view=${viewQuery}`) top
46 div 48 div
47 a(href=`/r/${subreddit}?sort=top&t=day`) top day 49 a(href=`/r/${subreddit}?sort=top&t=day&view=${viewQuery}`) top day
48 div 50 div
49 a(href=`/r/${subreddit}?sort=top&t=week`) top week 51 a(href=`/r/${subreddit}?sort=top&t=week&view=${viewQuery}`) top week
50 div 52 div
51 a(href=`/r/${subreddit}?sort=top&t=month`) top month 53 a(href=`/r/${subreddit}?sort=top&t=month&view=${viewQuery}`) top month
52 div 54 div
53 a(href=`/r/${subreddit}?sort=top&t=year`) top year 55 a(href=`/r/${subreddit}?sort=top&t=year&view=${viewQuery}`) top year
54 div 56 div
55 a(href=`/r/${subreddit}?sort=top&t=all`) top all 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
56 63
57 if posts 64 if posts
58 each child in posts.posts 65 each child in posts.posts
diff --git a/src/views/post-search.pug b/src/views/post-search.pug
index b80f9f6..8f93798 100644
--- a/src/views/post-search.pug
+++ b/src/views/post-search.pug
@@ -2,6 +2,8 @@ include ../mixins/post
2include ../mixins/header 2include ../mixins/header
3include ../mixins/head 3include ../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'
5doctype html 7doctype html
6html 8html
7 +head("search posts") 9 +head("search posts")
@@ -14,6 +16,8 @@ html
14 form(action="/post-search" method="get").search-bar 16 form(action="/post-search" method="get").search-bar
15 - var prefill = original_query ?? ""; 17 - var prefill = original_query ?? "";
16 input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input 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 button(type="submit").search-button go 21 button(type="submit").search-button go
18 if message 22 if message
19 div.search-message 23 div.search-message
diff --git a/src/views/search.pug b/src/views/search.pug
index ef9b53e..4dc5c0c 100644
--- a/src/views/search.pug
+++ b/src/views/search.pug
@@ -1,6 +1,8 @@
1include ../mixins/header 1include ../mixins/header
2include ../mixins/head 2include ../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'
4doctype html 6doctype html
5html 7html
6 +head("search subreddits") 8 +head("search subreddits")
@@ -14,6 +16,8 @@ html
14 form(action="/sub-search" method="get").search-bar 16 form(action="/sub-search" method="get").search-bar
15 - var prefill = original_query ?? ""; 17 - var prefill = original_query ?? "";
16 input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input 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 button(type="submit").search-button go 21 button(type="submit").search-button go
18 22
19 hr 23 hr
@@ -23,6 +27,8 @@ html
23 form(action="/post-search" method="get").search-bar 27 form(action="/post-search" method="get").search-bar
24 - var prefill = original_query ?? ""; 28 - var prefill = original_query ?? "";
25 input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input 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 button(type="submit").search-button go 32 button(type="submit").search-button go
27 p 33 p
28 | you can narrow search results using filters: 34 | you can narrow search results using filters:
diff --git a/src/views/sub-search.pug b/src/views/sub-search.pug
index f2402f7..cb8eeef 100644
--- a/src/views/sub-search.pug
+++ b/src/views/sub-search.pug
@@ -1,6 +1,8 @@
1include ../mixins/header 1include ../mixins/header
2include ../mixins/head 2include ../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'
4doctype html 6doctype html
5html 7html
6 +head("search subreddits") 8 +head("search subreddits")
@@ -13,6 +15,8 @@ html
13 form(action="/sub-search" method="get").search-bar 15 form(action="/sub-search" method="get").search-bar
14 - var prefill = original_query ?? ""; 16 - var prefill = original_query ?? "";
15 input(type="text" name="q" placeholder="type in a search term..." value=prefill required).search-input 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 button(type="submit").search-button go 20 button(type="submit").search-button go
17 if message 21 if message
18 div.search-message 22 div.search-message
@@ -25,7 +29,7 @@ html
25 - var isSubbed = subs.includes(subreddit) 29 - var isSubbed = subs.includes(subreddit)
26 div.sub-title 30 div.sub-title
27 h3 31 h3
28 a(href=`/r/${subreddit}`) 32 a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
29 | r/#{subreddit} 33 | r/#{subreddit}
30 div#button-container 34 div#button-container
31 if isSubbed 35 if isSubbed
diff --git a/src/views/subs.pug b/src/views/subs.pug
index 8fca12d..aff8258 100644
--- a/src/views/subs.pug
+++ b/src/views/subs.pug
@@ -1,6 +1,8 @@
1include ../mixins/header 1include ../mixins/header
2include ../mixins/head 2include ../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'
4doctype html 6doctype html
5html 7html
6 +head("subscriptions") 8 +head("subscriptions")
@@ -16,7 +18,7 @@ html
16 - var isSubbed = true 18 - var isSubbed = true
17 div.sub-title 19 div.sub-title
18 h4 20 h4
19 a(href=`/r/${subreddit}`) 21 a(href=`/r/${subreddit}?sort=${sortQuery}&view=${viewQuery}`)
20 | r/#{subreddit} 22 | r/#{subreddit}
21 div#button-container 23 div#button-container
22 if isSubbed 24 if isSubbed