selfhostable, read-only reddit client
16
fork

Configure Feed

Select the types of activity you want to include in your feed.

finish subscriptions

Akshay 8b5db5e1 55ef1a63

+162 -54
+1 -1
flake.nix
··· 36 36 cp -R ./node_modules/* $out/node_modules 37 37 ls -la $out/node_modules 38 38 ''; 39 - outputHash = "sha256-rDMFY/D7rRWj6PDhZu2vRST12fyNpYUMl1a1LBB6/Jw="; 39 + outputHash = "sha256-k77Ht47QBQUmoGp2zxBwVIjQ9fwnIGCqcqBLK6/d6jM="; 40 40 outputHashAlgo = "sha256"; 41 41 outputHashMode = "recursive"; 42 42 };
+32
src/mixins/sub.pug
··· 1 + mixin subMgmt() 2 + script. 3 + function getSubs() { 4 + var store = localStorage.getItem('subs'); 5 + if (store) { 6 + return store.split(',').map((n)=>n.replace(/\/?r\//,'')); 7 + } else { 8 + return []; 9 + } 10 + } 11 + 12 + function subscribe(newsub) { 13 + var subs = getSubs(); 14 + if (!subs.includes(newsub)) { 15 + localStorage.setItem('subs',[...subs,newsub]); 16 + updateButton(newsub); 17 + } 18 + } 19 + 20 + function unsubscribe(sub) { 21 + var subs = getSubs(); 22 + if (subs.includes(sub)) { 23 + localStorage.setItem('subs',subs.filter((s)=>s!=sub)); 24 + updateButton(sub); 25 + } 26 + } 27 + 28 + function issub(sub) { 29 + return getSubs().includes(sub); 30 + } 31 + 32 +
+58 -9
src/public/styles.css
··· 1 + @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); 2 + 3 + body { 4 + font-family: 'Inter', sans-serif; 5 + } 6 + 1 7 main { 2 8 display: flex; 3 9 flex-direction: column; ··· 5 11 align-items: center; 6 12 } 7 13 8 - .post, .comments-container, .header { 14 + .header { 15 + display: flex; 16 + flex-direction: row; 17 + } 18 + 19 + nav { 20 + display: flex; 21 + align-items: stretch; 22 + } 23 + 24 + .post, .comments-container, .hero, .header { 9 25 padding: 0.3rem; 10 26 flex: 1 1 100%; 11 27 font-size: 1rem; 12 28 width: 100%; 13 29 } 14 30 15 - .info-container { 31 + .info-container, .comment-info-container { 16 32 color: #777; 17 33 font-size: 0.7rem; 18 34 display: flex; ··· 23 39 font-size: 0.7rem; 24 40 } 25 41 26 - .info-item { 42 + .info-item, .header-item { 27 43 margin-right: 14px; 28 44 } 29 45 46 + .header-item { 47 + position: relative; /* Needed for positioning the pseudo-element */ 48 + } 49 + 50 + .header-item:not(:last-child)::after { 51 + content: "·"; /* Middle dot as the separator */ 52 + position: absolute; 53 + right: -10px; /* Adjust position as needed */ 54 + top: 50%; 55 + transform: translateY(-50%); /* Center vertically */ 56 + color: #888; /* Separator color */ 57 + font-size: 20px; /* Adjust size of the separator */ 58 + } 59 + 30 60 @media (min-width: 768px) { 31 - .post, .comments-container, .header { 61 + .post, .comments-container, .hero, .header { 32 62 flex: 1 1 65%; 33 63 width: 65%; 34 64 } 35 - .info-container { 65 + .info-container, .comment-info-container { 36 66 font-size: 0.8rem; 37 67 } 38 68 } 39 69 40 70 @media (min-width: 1080px) { 41 - .post, .comments-container, .header { 71 + .post, .comments-container, .hero, .header { 42 72 flex: 1 1 50%; 43 73 width: 50%; 44 74 } 45 - .info-container { 75 + .info-container, .comment-info-container { 46 76 font-size: 0.8rem; 47 77 } 48 78 } ··· 95 125 display: block; 96 126 margin: 0 auto; 97 127 max-width: 100%; 128 + max-height: 400px; 98 129 } 99 130 100 - .title-container,.info-container { 101 - flex: 1; 131 + .title-container,.info-container, .comment-info-container { 132 + flex: 1; 102 133 margin-top: 10px; 103 134 margin-bottom: 10px; 104 135 } 105 136 137 + .comment-info-container { 138 + margin-bottom: -12px; 139 + } 140 + 106 141 hr { 107 142 border 1px solid #000; 108 143 } ··· 113 148 border-left: 4px solid green; 114 149 color: green; 115 150 } 151 + 152 + pre, code { 153 + background: #eee; 154 + } 155 + 156 + pre { 157 + padding: 10px; 158 + width: 100%; 159 + overflow: auto; 160 + } 161 + 162 + code { 163 + overflow-x: auto; 164 + }
+5
src/routes/index.js
··· 31 31 res.render('comments', unescape_submission(response)); 32 32 }); 33 33 34 + // GET /subs 35 + router.get('/subs', async (req, res) => { 36 + res.render('subs'); 37 + }); 38 + 34 39 module.exports = router; 35 40 36 41 function unescape_submission(response) {
+6 -5
src/views/comment.pug src/mixins/comment.pug
··· 1 - include utils 1 + include ../utils 2 2 mixin comment(com, isfirst) 3 3 - var data = com.data 4 4 - var kind = com.kind 5 5 if kind == "more" 6 - div.more #{data.count} more comments 6 + div(class=`${isfirst?'first':''}`) 7 + div.more #{data.count} more comments 7 8 else 8 9 div(class=`comment ${isfirst?'first':''}`) 10 + div.comment-info-container 11 + div.info-item u/#{data.author} 12 + div.info-item ↑ #{fmtnum(data.ups)} 9 13 div.comment-body 10 14 != data.body_html 11 - div.info-container 12 - div.info-item by u/#{data.author} 13 - div.info-item ↑ #{fmtnum(data.ups)} 14 15 div.replies 15 16 if data.replies 16 17 if data.replies.data
+3 -3
src/views/comments.pug
··· 1 + include ../mixins/comment 2 + 1 3 doctype html 2 4 html 3 5 head ··· 6 8 link(rel='stylesheet', href='/styles.css') 7 9 body 8 10 main#content 9 - div.header 11 + div.hero 10 12 a(href=`/r/${post.subreddit}`) 11 13 h4 ← r/#{post.subreddit} 12 14 h2 #{post.title} ··· 15 17 else if post.post_hint == 'hosted:video' 16 18 video(src=post.url).post-media 17 19 p.self-text !{post.selftext_html} 18 - hr 19 20 20 21 div.comments-container 21 22 each child in comments 22 - include comment 23 23 +comment(child, true) 24 24 25 25 script(src='https://unpkg.com/htmx.org@1.9.10')
+24 -35
src/views/index.pug
··· 1 + include ../mixins/post 2 + include ../mixins/sub 1 3 - var subs = [] 2 4 doctype html 3 5 html ··· 5 7 meta(charset='UTF-8') 6 8 title reddit 7 9 link(rel='stylesheet', href='/styles.css') 10 + 11 + +subMgmt() 8 12 script. 9 - function getSubs() { 10 - var store = localStorage.getItem('subs'); 11 - if (store) { 12 - return store.split(',').map((n)=>n.replace(/\/?r\//,'')); 13 - } else { 14 - return []; 15 - } 16 - } 13 + function updateButton(sub) { 14 + var b = document.getElementById("button-container"); 15 + b.innerHTML = ''; 17 16 18 - function subscribe(newsub) { 19 - var subs = getSubs(); 20 - if (!subs.includes(newsub)) { 21 - localStorage.setItem('subs',[...subs,newsub]); 17 + const button = document.createElement("button"); 18 + 19 + if (issub(sub)) { 20 + button.innerText = "unsubscribe"; 21 + button.onclick = ()=>unsubscribe(sub); 22 + } else { 23 + button.innerText = "subscribe"; 24 + button.onclick = ()=>subscribe(sub); 22 25 } 26 + b.appendChild(button); 23 27 } 24 28 25 - function newSubItem(newsub) { 26 - var p = document.createElement("p"); 27 - var text = document.createTextNode(newsub); 28 - p.appendChild(text); 29 - return p; 30 - } 31 - 32 - function genSubListing() { 33 - const body = document.body; 34 - const subbar = document.createElement('div'); 35 - subbar.id = 'subscriptions'; 36 - body.insertBefore(subbar, body.firstChild); 37 - getSubs().forEach((item) => { 38 - subbar.appendChild(newSubItem(item)); 39 - }); 40 - } 41 - 42 - document.addEventListener('DOMContentLoaded', function() { 43 - genSubListing(); 44 - }); 29 + document.addEventListener('DOMContentLoaded', ()=>updateButton("#{subreddit}")); 45 30 body 46 - 47 31 main#content 48 32 div.header 33 + div.header-item 34 + a(href=`/`) home 35 + div.header-item 36 + a(href=`/subs`) subscriptions 37 + 38 + div.hero 49 39 a(href=`/r/${subreddit}`) 50 40 h1 r/#{subreddit} 51 41 if about 52 42 p #{about.public_description} 53 - button(onclick=`subscribe("${subreddit}")`) subscribe 43 + div#button-container 54 44 55 45 each child in posts.posts 56 - include post 57 46 +post(child.data) 58 47 59 48 script(src='https://unpkg.com/htmx.org@1.9.10')
+1 -1
src/views/post.pug src/mixins/post.pug
··· 1 - include utils 1 + include ../utils 2 2 mixin post(p) 3 3 article.post 4 4 div.post-container
+32
src/views/subs.pug
··· 1 + include ../mixins/sub 2 + 3 + doctype html 4 + html 5 + head 6 + meta(charset='UTF-8') 7 + title reddit 8 + link(rel='stylesheet', href='/styles.css') 9 + 10 + +subMgmt() 11 + script. 12 + function newSubItem(sub) { 13 + const p = document.createElement("p"); 14 + const a = document.createElement("a"); 15 + a.href = `/r/${sub}`; 16 + a.innerText = `r/${sub}`; 17 + p.appendChild(a); 18 + return p; 19 + } 20 + 21 + function buildSubList() { 22 + var subList = document.getElementById('subList'); 23 + getSubs().forEach((sub)=>{ 24 + subList.appendChild(newSubItem(sub)); 25 + }); 26 + } 27 + 28 + document.addEventListener('DOMContentLoaded', buildSubList); 29 + body 30 + main#content 31 + h1 subscriptions 32 + div#subList
src/views/utils.pug src/utils.pug