aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore179
-rw-r--r--build.js9
-rw-r--r--flake.lock27
-rw-r--r--flake.nix99
-rw-r--r--package.json17
-rw-r--r--public/styles.css92
-rw-r--r--readme.txt1
-rw-r--r--src/geddit.js392
-rw-r--r--src/index.js17
-rw-r--r--src/routes/index.js35
-rw-r--r--src/views/comment.pug18
-rw-r--r--src/views/comments.pug25
-rw-r--r--src/views/index.pug19
-rw-r--r--src/views/post.pug21
-rw-r--r--src/views/utils.pug3
15 files changed, 954 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..da80e40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,179 @@
1# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2
3# Logs
4
5logs
6_.log
7npm-debug.log_
8yarn-debug.log*
9yarn-error.log*
10lerna-debug.log*
11.pnpm-debug.log*
12
13# Caches
14
15.cache
16
17# Diagnostic reports (https://nodejs.org/api/report.html)
18
19report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
20
21# Runtime data
22
23pids
24_.pid
25_.seed
26*.pid.lock
27
28# Directory for instrumented libs generated by jscoverage/JSCover
29
30lib-cov
31
32# Coverage directory used by tools like istanbul
33
34coverage
35*.lcov
36
37# nyc test coverage
38
39.nyc_output
40
41# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
42
43.grunt
44
45# Bower dependency directory (https://bower.io/)
46
47bower_components
48
49# node-waf configuration
50
51.lock-wscript
52
53# Compiled binary addons (https://nodejs.org/api/addons.html)
54
55build/Release
56
57# Dependency directories
58
59node_modules/
60jspm_packages/
61
62# Snowpack dependency directory (https://snowpack.dev/)
63
64web_modules/
65
66# TypeScript cache
67
68*.tsbuildinfo
69
70# Optional npm cache directory
71
72.npm
73
74# Optional eslint cache
75
76.eslintcache
77
78# Optional stylelint cache
79
80.stylelintcache
81
82# Microbundle cache
83
84.rpt2_cache/
85.rts2_cache_cjs/
86.rts2_cache_es/
87.rts2_cache_umd/
88
89# Optional REPL history
90
91.node_repl_history
92
93# Output of 'npm pack'
94
95*.tgz
96
97# Yarn Integrity file
98
99.yarn-integrity
100
101# dotenv environment variable files
102
103.env
104.env.development.local
105.env.test.local
106.env.production.local
107.env.local
108
109# parcel-bundler cache (https://parceljs.org/)
110
111.parcel-cache
112
113# Next.js build output
114
115.next
116out
117
118# Nuxt.js build / generate output
119
120.nuxt
121dist
122
123# Gatsby files
124
125# Comment in the public line in if your project uses Gatsby and not Next.js
126
127# https://nextjs.org/blog/next-9-1#public-directory-support
128
129# public
130
131# vuepress build output
132
133.vuepress/dist
134
135# vuepress v2.x temp and cache directory
136
137.temp
138
139# Docusaurus cache and generated files
140
141.docusaurus
142
143# Serverless directories
144
145.serverless/
146
147# FuseBox cache
148
149.fusebox/
150
151# DynamoDB Local files
152
153.dynamodb/
154
155# TernJS port file
156
157.tern-port
158
159# Stores VSCode versions used for testing VSCode extensions
160
161.vscode-test
162
163# yarn v2
164
165.yarn/cache
166.yarn/unplugged
167.yarn/build-state.yml
168.yarn/install-state.gz
169.pnp.*
170
171# IntelliJ based IDEs
172.idea
173
174# Finder (MacOS) folder config
175.DS_Store
176
177reference
178bun.lockb
179result
diff --git a/build.js b/build.js
new file mode 100644
index 0000000..a16c54b
--- /dev/null
+++ b/build.js
@@ -0,0 +1,9 @@
1import { execSync } from "bun";
2
3try {
4 // Precompile Pug templates in the `src` directory to the `dist` directory
5 execSync("pug src -o dist");
6 console.log("Pug templates compiled successfully.");
7} catch (error) {
8 console.error("Failed to compile Pug templates:", error);
9}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..fa59981
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,27 @@
1{
2 "nodes": {
3 "nixpkgs": {
4 "locked": {
5 "lastModified": 1724748588,
6 "narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=",
7 "owner": "nixos",
8 "repo": "nixpkgs",
9 "rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99",
10 "type": "github"
11 },
12 "original": {
13 "owner": "nixos",
14 "ref": "nixpkgs-unstable",
15 "repo": "nixpkgs",
16 "type": "github"
17 }
18 },
19 "root": {
20 "inputs": {
21 "nixpkgs": "nixpkgs"
22 }
23 }
24 },
25 "root": "root",
26 "version": 7
27}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..917e2e0
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,99 @@
1{
2 inputs = {
3
4 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
5
6 };
7
8 outputs =
9 { self
10 , nixpkgs
11 }:
12 let
13 supportedSystems = [ "x86_64-linux" ];
14 forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
15 nixpkgsFor = forAllSystems (system:
16 import nixpkgs {
17 inherit system;
18 overlays = [ self.overlays.default ];
19 });
20
21 in
22 {
23 overlays.default = final: prev: {
24 node_modules = with final; stdenv.mkDerivation {
25 pname = "readit-node-modules";
26 version = "0.0.1";
27 impureEnvVars = lib.fetchers.proxyImpureEnvVars
28 ++ [ "GIT_PROXY_COMMAND" "SOCKS_SERVER" ];
29 src = ./.;
30 nativeBuildInputs = [ bun ];
31 buildInputs = [ nodejs-slim_latest ];
32 dontConfigure = true;
33 dontFixup = true;
34 buildPhase = ''
35 bun install --no-progress --frozen-lockfile
36 '';
37 installPhase = ''
38 mkdir -p $out/node_modules
39 cp -R ./node_modules/* $out/node_modules
40 ls -la $out/node_modules
41 '';
42 outputHash = "sha256-qFYgRIarDChHQu0ZrUKd/Y61gxaagMWpf2h9xizwGv4=";
43 outputHashAlgo = "sha256";
44 outputHashMode = "recursive";
45 };
46 readit = with final; stdenv.mkDerivation {
47 pname = "readit";
48 version = "0.0.1";
49 src = ./.;
50 nativeBuildInputs = [ makeBinaryWrapper ];
51 buildInputs = [ bun ];
52
53 buildPhase = ''
54 runHook preBuild
55
56
57 runHook postBuild
58 '';
59
60 dontFixup = true;
61
62 installPhase = ''
63 runHook preInstall
64
65 mkdir -p $out/bin
66
67 # cp app.js $out/app.js
68 cp -R ./* $out
69
70 # bun is referenced naked in the package.json generated script
71 # makeBinaryWrapper ${bun}/bin/bun $out/bin/$pname \
72 # --add-flags "run --prefer-offline --no-install $out/app.js"
73
74 makeBinaryWrapper ${bun}/bin/bun $out/bin/$pname \
75 --prefix PATH : ${lib.makeBinPath [ bun ]} \
76 --add-flags "run --prefer-offline --no-install $out/src/index.js"
77
78 '';
79 };
80 };
81
82 devShell = forAllSystems (system:
83 let
84 pkgs = nixpkgsFor."${system}";
85 in
86 pkgs.mkShell {
87 nativeBuildInputs = [
88 pkgs.bun
89 ];
90 RUST_BACKTRACE = 1;
91 });
92
93 packages = forAllSystems(system: {
94 inherit (nixpkgsFor."${system}") readit node_modules;
95 });
96 };
97}
98
99
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3e68c4c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,17 @@
1{
2 "name": "scarlet",
3 "module": "index.ts",
4 "type": "module",
5 "devDependencies": {
6 "@types/bun": "latest",
7 "pug-cli": "^1.0.0-alpha6"
8 },
9 "peerDependencies": {
10 "typescript": "^5.0.0"
11 },
12 "dependencies": {
13 "express": "^4.19.2",
14 "pug": "^3.0.3",
15 "timeago.js": "^4.0.2"
16 }
17} \ No newline at end of file
diff --git a/public/styles.css b/public/styles.css
new file mode 100644
index 0000000..1a541de
--- /dev/null
+++ b/public/styles.css
@@ -0,0 +1,92 @@
1main {
2 display: flex;
3 flex-direction: column;
4 gap: 1rem;
5 align-items: center;
6}
7
8.post, .comments-container, .header {
9 padding: 0.3rem;
10 flex: 1 1 95%;
11 font-size: 1rem;
12 width: 95%;
13}
14
15@media (min-width: 768px) {
16 .post, .comments-container, .header {
17 flex: 1 1 65%;
18 width: 65%;
19 }
20}
21
22@media (min-width: 1080px) {
23 .post, .comments-container, .header {
24 flex: 1 1 50%;
25 width: 50%;
26 }
27}
28
29.comments-container, .self-text {
30 text-align: justify;
31}
32
33.comment, .more {
34 width: 100%;
35 border-left: 1px dashed;
36 padding: 10px 0px 10px 24px;
37 box-sizing: border-box;
38}
39
40.more {
41 margin-bottom: 0px;
42 font-size: 0.7rem;
43 color: #777;
44}
45
46.first {
47 border-left: none;
48 padding-left: 0;
49 margin: 0;
50 margin-top: 12px;
51}
52
53.post-container {
54 align-self: stretch;
55 display: flex;
56}
57
58.post-text {
59 flex-direction: column;
60 align-items: stretch;
61 justify-content: space-between;
62}
63
64.media-preview {
65 padding-left: 10px;
66 margin-left: auto;
67}
68
69.post-media {
70 max-width: 100%;
71}
72
73.title-container,.info-container {
74 flex: 1;
75 margin-top: 10px;
76 margin-bottom: 10px;
77}
78
79.info-container {
80 color: #777;
81 font-size: 0.8rem;
82 display: flex;
83 align-items: center;
84}
85
86.info-item {
87 margin-right: 10px;
88}
89
90hr {
91 border 1px solid #000;
92}
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..f041728
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1 @@
nix build .#readit
diff --git a/src/geddit.js b/src/geddit.js
new file mode 100644
index 0000000..825c3a9
--- /dev/null
+++ b/src/geddit.js
@@ -0,0 +1,392 @@
1class Geddit {
2 constructor() {
3 this.host = "https://www.reddit.com";
4 this.parameters = {
5 limit: 25,
6 include_over_18: true,
7 }
8 this.search_params = {
9 limit: 25,
10 include_over_18: true,
11 type: "sr,link,user",
12 }
13 }
14
15 async getSubmissions(sort = null, subreddit = null, options = {}) {
16 let params = {
17 limit: 25,
18 include_over_18: true,
19 }
20
21 sort = sort ? sort : "hot";
22 subreddit = subreddit ? "/r/" + subreddit : "";
23
24 return await fetch(this.host + subreddit + `/${sort}.json?` + new URLSearchParams(Object.assign(params, options)))
25 .then(res => res.json())
26 .then(json => json.data)
27 .then(data => ({
28 after: data.after,
29 posts: data.children
30 }))
31 .catch(err => null);
32
33 }
34
35 async getDomainHot(domain, options = this.parameters) {
36 return await fetch(this.host + "/domain/" + domain + "/hot.json?" + new URLSearchParams(options))
37 .then(res => res.json())
38 .then(json => json.data)
39 .then(data => ({
40 after: data.after,
41 posts: data.children
42 }))
43 .catch(err => null);
44 }
45
46 async getDomainBest(domain, options = this.parameters) {
47 return await fetch(this.host + "/domain/" + domain + "/best.json?" + new URLSearchParams(options))
48 .then(res => res.json())
49 .then(json => json.data)
50 .then(data => ({
51 after: data.after,
52 posts: data.children
53 }))
54 .catch(err => null);
55 }
56
57 async getDomainTop(domain, options = this.parameters) {
58 return await fetch(this.host + "/domain/" + domain + "/top.json?" + new URLSearchParams(options))
59 .then(res => res.json())
60 .then(json => json.data)
61 .then(data => ({
62 after: data.after,
63 posts: data.children
64 }))
65 .catch(err => null);
66 }
67
68 async getDomainNew(domain, options = this.parameters) {
69 return await fetch(this.host + "/domain/" + domain + "/new.json?" + new URLSearchParams(options))
70 .then(res => res.json())
71 .then(json => json.data)
72 .then(data => ({
73 after: data.after,
74 posts: data.children
75 }))
76 .catch(err => null);
77 }
78
79 async getDomainRising(domain, options = this.parameters) {
80 return await fetch(this.host + "/domain/" + domain + "/rising.json?" + new URLSearchParams(options))
81 .then(res => res.json())
82 .then(json => json.data)
83 .then(data => ({
84 after: data.after,
85 posts: data.children
86 }))
87 .catch(err => null);
88 }
89
90 async getDomainControversial(domain, options = this.parameters) {
91 return await fetch(this.host + "/domain/" + domain + "/controversial.json?" + new URLSearchParams(options))
92 .then(res => res.json())
93 .then(json => json.data)
94 .then(data => ({
95 after: data.after,
96 posts: data.children
97 }))
98 .catch(err => null);
99 }
100
101 async getSubreddit(subreddit) {
102 return await fetch(this.host + "/r/" + subreddit + "/about.json")
103 .then(res => res.json())
104 .then(json => json.data)
105 .catch(err => null);
106 }
107
108 async getSubredditRules(subreddit) {
109 return await fetch(this.host + "/r/" + subreddit + "/about/rules.json")
110 .then(res => res.json())
111 .then(json => json.data)
112 .catch(err => null);
113 }
114
115 async getSubredditModerators(subreddit) {
116 return await fetch(this.host + "/r/" + subreddit + "/about/moderators.json")
117 .then(res => res.json())
118 .then(json => json.data)
119 .then(data = ({
120 users: data.children
121 }))
122 .catch(err => null);
123 }
124
125 async getSubredditWikiPages(subreddit) {
126 return await fetch(this.host + "/r/" + subreddit + "/wiki/pages.json")
127 .then(res => res.json())
128 .then(json => json.data)
129 .catch(err => null);
130 }
131
132 async getSubredditWikiPage(subreddit, page) {
133 return await fetch(this.host + "/r/" + subreddit + "/wiki/" + page + ".json")
134 .then(res => res.json())
135 .then(json => json.data)
136 .catch(err => null);
137 }
138
139 async getSubredditWikiPageRevisions(subreddit, page) {
140 return await fetch(this.host + "/r/" + subreddit + "/wiki/revisions" + page + ".json")
141 .then(res => res.json())
142 .then(json => json.data.children)
143 .catch(err => null);
144 }
145
146 async getPopularSubreddits(options = this.parameters) {
147 return await fetch(this.host + "/subreddits/popular.json?" + new URLSearchParams(options))
148 .then(res => res.json())
149 .then(json => json.data)
150 .then(data => ({
151 after: data.after,
152 subreddits: data.children
153 }))
154 .catch(err => null);
155 }
156
157 async getNewSubreddits(options = this.parameters) {
158 return await fetch(this.host + "/subreddits/new.json?" + new URLSearchParams(options))
159 .then(res => res.json())
160 .then(json => json.data)
161 .then(data => ({
162 after: data.after,
163 subreddits: data.children
164 }))
165 .catch(err => null);
166 }
167
168 async getPremiumSubreddits(options = this.parameters) {
169 return await fetch(this.host + "/subreddits/premium.json?" + new URLSearchParams(options))
170 .then(res => res.json())
171 .then(json => json.data)
172 .then(data => ({
173 after: data.after,
174 subreddits: data.children
175 }))
176 .catch(err => null);
177 }
178
179 async getDefaultSubreddits(options = this.parameters) {
180 return await fetch(this.host + "/subreddits/default.json?" + new URLSearchParams(options))
181 .then(res => res.json())
182 .then(json => json.data)
183 .then(data => ({
184 after: data.after,
185 subreddits: data.children
186 }))
187 .catch(err => null);
188 }
189
190 async getPopularUsers(options = this.parameters) {
191 return await fetch(this.host + "/users/popular.json?" + new URLSearchParams(options))
192 .then(res => res.json())
193 .then(json => json.data)
194 .then(data => ({
195 after: data.after,
196 users: data.children
197 }))
198 .catch(err => null);
199 }
200
201 async getNewUsers(options = this.parameters) {
202 return await fetch(this.host + "/users/new.json?" + new URLSearchParams(options))
203 .then(res => res.json())
204 .then(json => json.data)
205 .then(data => ({
206 after: data.after,
207 users: data.children
208 }))
209 .catch(err => null);
210 }
211
212 async searchSubmissions(query, options = {}) {
213 options.q = query;
214 options.type = "link";
215
216 let params = {
217 limit: 25,
218 include_over_18: true
219 }
220
221 console.log(this.host + "/search.json?" + new URLSearchParams(Object.assign(params, options)));
222
223 return await fetch(this.host + "/search.json?" + new URLSearchParams(Object.assign(params, options)))
224 .then(res => res.json())
225 .then(json => json.data)
226 .then(data => ({
227 after: data.after,
228 items: data.children
229 }))
230 .catch(err => null);
231 }
232
233 async searchSubreddits(query, options = {}) {
234 options.q = query;
235
236 let params = {
237 limit: 25,
238 include_over_18: true
239 }
240
241 return await fetch(this.host + "/subreddits/search.json?" + new URLSearchParams(Object.assign(params, options)))
242 .then(res => res.json())
243 .then(json => json.data)
244 .then(data => ({
245 after: data.after,
246 items: data.children
247 }))
248 .catch(err => null);
249 }
250
251 async searchUsers(query, options = {}) {
252 options.q = query;
253
254 let params = {
255 limit: 25,
256 include_over_18: true
257 }
258
259 return await fetch(this.host + "/users/search.json?" + new URLSearchParams(Object.assign(params, options)))
260 .then(res => res.json())
261 .then(json => json.data)
262 .then(data => ({
263 after: data.after,
264 items: data.children
265 }))
266 .catch(err => null);
267 }
268
269 async searchAll(query, subreddit = null, options = {}) {
270 options.q = query;
271 subreddit = subreddit ? "/r/" + subreddit : "";
272
273 let params = {
274 limit: 25,
275 include_over_18: true,
276 type: "sr,link,user",
277 }
278
279 return await fetch(this.host + subreddit + "/search.json?" + new URLSearchParams(Object.assign(params, options)))
280 .then(res => res.json())
281 .then(json => Array.isArray(json) ? ({
282 after: json[1].data.after,
283 items: json[0].data.children.concat(json[1].data.children)
284 }) : ({
285 after: json.data.after,
286 items: json.data.children
287 }))
288 .catch(err => null);
289 }
290
291 async getSubmission(id) {
292 return await fetch(this.host + "/by_id/" + id + ".json")
293 .then(res => res.json())
294 .then(json => json.data.children[0].data)
295 .catch(err => null);
296 }
297
298 async getSubmissionComments(id, options = this.parameters) {
299 return await fetch(this.host + "/comments/" + id + ".json?" + new URLSearchParams(options))
300 .then(res => res.json())
301 .then(json => ({
302 submission: json[0].data.children[0],
303 comments: json[1].data.children
304 }))
305 .catch(err => null);
306 }
307
308 async getSubredditComments(subreddit, options = this.parameters) {
309 return await fetch(this.host + "/r/" + subreddit + "/comments.json?" + new URLSearchParams(options))
310 .then(res => res.json())
311 .then(json => json.data.children)
312 .catch(err => null);
313 }
314
315 async getUser(username) {
316 return await fetch(this.host + "/user/" + username + "/about.json")
317 .then(res => res.json())
318 .then(json => json.data)
319 .catch(err => null);
320 }
321
322 async getUserOverview(username, options = this.parameters) {
323 return await fetch(this.host + "/user/" + username + "/overview.json?" + new URLSearchParams(options))
324 .then(res => res.json())
325 .then(json => json.data)
326 .then(data => ({
327 after: data.after,
328 items: data.children
329 }))
330 .catch(err => null);
331 }
332
333 async getUserComments(username, options = this.parameters) {
334 return await fetch(this.host + "/user/" + username + "/comments.json?" + new URLSearchParams(options))
335 .then(res => res.json())
336 .then(json => json.data)
337 .then(data => ({
338 after: data.after,
339 items: data.children
340 }))
341 .catch(err => null);
342 }
343
344 async getUserSubmissions(username, options = this.parameters) {
345 return await fetch(this.host + "/user/" + username + "/submitted.json?" + new URLSearchParams(options))
346 .then(res => res.json())
347 .then(json => json.data)
348 .then(data => ({
349 after: data.after,
350 items: data.children
351 }))
352 .catch(err => null);
353 }
354
355 async getLiveThread(id) {
356 return await fetch(this.host + "/live/" + id + "/about.json")
357 .then(res => res.json())
358 .then(json => json.data)
359 .catch(err => null);
360 }
361
362 async getLiveThreadUpdates(id, options = this.parameters) {
363 return await fetch(this.host + "/live/" + id + ".json?" + new URLSearchParams(options))
364 .then(res => res.json())
365 .then(json => json.data.children)
366 .catch(err => null);
367 }
368
369
370 async getLiveThreadContributors(id, options = this.parameters) {
371 return await fetch(this.host + "/live/" + id + "/contributors.json?" + new URLSearchParams(options))
372 .then(res => res.json())
373 .then(json => json.data.children)
374 .catch(err => null);
375 }
376
377 async getLiveThreadDiscussions(id, options = this.parameters) {
378 return await fetch(this.host + "/live/" + id + "/discussions.json?" + new URLSearchParams(options))
379 .then(res => res.json())
380 .then(json => json.data.children)
381 .catch(err => null);
382 }
383
384 async getLiveThreadsNow(options = this.parameters) {
385 return await fetch(this.host + "/live/happening_now.json?" + new URLSearchParams(options))
386 .then(res => res.json())
387 .then(json => json.data.children)
388 .catch(err => null);
389 }
390}
391
392export { Geddit }
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..bfe31f4
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,17 @@
1const express = require('express');
2const path = require('path');
3const routes = require('./routes/index');
4const geddit = require('./geddit.js');
5
6const app = express();
7
8app.set('views', path.join(__dirname, 'views'));
9app.set('view engine', 'pug');
10
11app.use(express.static('public'));
12app.use('/', routes);
13
14const server = app.listen(3000, () => {
15 console.log(`started on ${server.address().port}`);
16});
17
diff --git a/src/routes/index.js b/src/routes/index.js
new file mode 100644
index 0000000..6cf5403
--- /dev/null
+++ b/src/routes/index.js
@@ -0,0 +1,35 @@
1const express = require('express');
2const router = express.Router();
3const geddit = require('../geddit.js');
4const G = new geddit.Geddit();
5const fs = require('fs/promises');
6
7
8// GET /
9router.get('/', async (req, res) => {
10 res.redirect("/r/all")
11});
12
13// GET /r/:id
14router.get('/r/:subreddit', async (req, res) => {
15 var subreddit = req.params.subreddit;
16
17 var postsReq = G.getSubmissions(`r/${subreddit}`);
18 var aboutReq = G.getSubreddit(`${subreddit}`);
19
20 var [posts, about] = await Promise.all([postsReq, aboutReq]);
21 res.render('index', { subreddit, posts, about });
22});
23
24// GET /comments/:id
25router.get('/comments/:id', async (req, res) => {
26 var id = req.params.id;
27
28 response = await G.getSubmissionComments(id);
29 var post = response.submission.data;
30 var comments = response.comments;
31
32 res.render('comments', { post, comments });
33});
34
35module.exports = router;
diff --git a/src/views/comment.pug b/src/views/comment.pug
new file mode 100644
index 0000000..24e1a9b
--- /dev/null
+++ b/src/views/comment.pug
@@ -0,0 +1,18 @@
1include utils
2mixin comment(com, isfirst)
3 - var data = com.data
4 - var kind = com.kind
5 if kind == "more"
6 div.more #{data.count} more comments
7 else
8 div(class=`comment ${isfirst?'first':''}`)
9 div.comment-body !{data.body}
10 div.info-container
11 div.info-item by u/#{data.author}
12 div.info-item ↑ #{fmtnum(data.ups)}
13 div.replies
14 if data.replies
15 if data.replies.data
16 if data.replies.data.children
17 each reply in data.replies.data.children
18 +comment(reply,false)
diff --git a/src/views/comments.pug b/src/views/comments.pug
new file mode 100644
index 0000000..f7964a3
--- /dev/null
+++ b/src/views/comments.pug
@@ -0,0 +1,25 @@
1doctype html
2html
3 head
4 meta(charset='UTF-8')
5 title reddit
6 link(rel='stylesheet', href='/styles.css')
7 body
8 main#content
9 div.header
10 a(href=`/r/${post.subreddit}`)
11 h4 ← r/#{post.subreddit}
12 h2 #{post.title}
13 if post.post_hint == 'image'
14 img(src=post.url).post-media
15 else if post.post_hint == 'hosted:video'
16 video(src=post.url).post-media
17 p.self-text !{post.selftext}
18 hr
19
20 div.comments-container
21 each child in comments
22 include comment
23 +comment(child, true)
24
25 script(src='https://unpkg.com/[email protected]')
diff --git a/src/views/index.pug b/src/views/index.pug
new file mode 100644
index 0000000..d017570
--- /dev/null
+++ b/src/views/index.pug
@@ -0,0 +1,19 @@
1doctype html
2html
3 head
4 meta(charset='UTF-8')
5 title reddit
6 link(rel='stylesheet', href='/styles.css')
7 body
8 main#content
9 div.header
10 a(href=`/r/#{subreddit}`)
11 h1 r/#{subreddit}
12 if about
13 p #{about.public_description}
14
15 each child in posts.posts
16 include post
17 +post(child.data)
18
19 script(src='https://unpkg.com/[email protected]')
diff --git a/src/views/post.pug b/src/views/post.pug
new file mode 100644
index 0000000..77ef3f5
--- /dev/null
+++ b/src/views/post.pug
@@ -0,0 +1,21 @@
1include utils
2mixin post(p)
3 article.post
4 div.post-container
5 div.post-text
6 div.title-container !{p.title}
7 div.info-container
8 div.info-item by u/#{p.author}
9 div.info-item ↑ #{fmtnum(p.ups)}
10 div.info-item #{p.domain}
11 div.info-item
12 a(href=`/r/${p.subreddit}`) r/#{p.subreddit}
13 div.info-item
14 a(href=`/comments/${p.id}`) #{fmtnum (p.num_comments)} #{fmttxt(p.num_comments, 'comment')}
15 div.media-preview
16 if p.post_hint == "image" || p.post_hint == "link"
17 if p.thumbnail && p.thumbnail != "self" || p.thumbnail != "default"
18 a(href=p.url)
19 img(src=p.thumbnail width='100px')
20 else if p.post_hint == "hosted:video"
21 video(src=p.secure_media.reddit_video.scrubber_media_url width='100px')
diff --git a/src/views/utils.pug b/src/views/utils.pug
new file mode 100644
index 0000000..f3f61bb
--- /dev/null
+++ b/src/views/utils.pug
@@ -0,0 +1,3 @@
1- var fmtnum = (n)=>n>=1000?(n/1000).toFixed(1)+'k':n;
2- var fmttxt = (n,t)=>`${t}${n==1?'':'s'}`
3