From 3e019372474df007d1f5d8544c73cdf024630c6d Mon Sep 17 00:00:00 2001 From: teddit Date: Sun, 17 Jan 2021 17:29:49 +0100 Subject: [PATCH] add '/subreddits' feature for searching and exploring subreddits --- config.js.template | 6 +- inc/processSubredditsExplore.js | 52 ++++++++++++ routes.js | 144 +++++++++++++++++++++++++++++++- static/css/styles.css | 52 +++++++++--- views/subreddits_explore.pug | 96 +++++++++++++++++++++ 5 files changed, 334 insertions(+), 16 deletions(-) create mode 100644 inc/processSubredditsExplore.js create mode 100644 views/subreddits_explore.pug diff --git a/config.js.template b/config.js.template index 59a007d..304c740 100644 --- a/config.js.template +++ b/config.js.template @@ -38,7 +38,11 @@ const config = { searches: 600, sidebar: 60 * 60 * 24 * 7, // 7 days shorts: 60 * 60 * 24 * 31, - wikis: 60* 60 * 24 * 7 + wikis: 60* 60 * 24 * 7, + subreddits_explore: { + front: 60 * 60 * 24 * 1, + new_page: 60 + }, }, convert_urls: { /** diff --git a/inc/processSubredditsExplore.js b/inc/processSubredditsExplore.js new file mode 100644 index 0000000..894f196 --- /dev/null +++ b/inc/processSubredditsExplore.js @@ -0,0 +1,52 @@ +module.exports = function() { + const config = require('../config'); + this.processJsonSubredditsExplore = (json, from, subreddit_front, user_preferences) => { + return new Promise(resolve => { + (async () => { + if(from === 'redis') { + json = JSON.parse(json) + } + if(json.error) { + resolve({ error: true, error_data: json }) + } else { + let before = json.data.before + let after = json.data.after + + let ret = { + info: { + before: before, + after: after + }, + links: [] + } + + let children_len = json.data.children.length + + for(var i = 0; i < children_len; i++) { + let data = json.data.children[i].data + + if(data.over_18) + if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false') + continue + + let obj = { + created: data.created_utc, + id: data.id, + over_18: data.over_18, + display_name: data.display_name, + display_name_prefixed: data.display_name_prefixed, + public_description: data.public_description, + url: data.url, + subscribers: data.subscribers, + over_18: data.over18, + title: data.title, + subreddit_front: subreddit_front, + } + ret.links.push(obj) + } + resolve(ret) + } + })() + }) + } +} diff --git a/routes.js b/routes.js index 5191dfb..37b5a5d 100644 --- a/routes.js +++ b/routes.js @@ -10,6 +10,7 @@ module.exports = (app, redis, fetch, RedditAPI) => { let processAbout = require('./inc/processSubredditAbout.js')(); let tedditApiSubreddit = require('./inc/teddit_api/handleSubreddit.js')(); let tedditApiUser = require('./inc/teddit_api/handleUser.js')(); + let processSubredditsExplore = require('./inc/processSubredditsExplore.js')(); app.get('/about', (req, res, next) => { return res.render('about', { user_preferences: req.cookies }) @@ -31,6 +32,141 @@ module.exports = (app, redis, fetch, RedditAPI) => { app.get('/privacy', (req, res, next) => { return res.render('privacypolicy', { user_preferences: req.cookies }) }) + + app.get('/subreddits/:sort?', (req, res, next) => { + let q = req.query.q + let nsfw = req.query.nsfw + let after = req.query.after + let before = req.query.before + let sortby = req.params.sort + let searching = false + + if(!after) { + after = '' + } + if(!before) { + before = '' + } + + let d = `&after=${after}` + if(before) { + d = `&before=${before}` + } + + if(nsfw !== 'on') { + nsfw = 'off' + } + + if(!sortby) { + sortby = '' + } + + let key = `subreddits:sort:${sortby}${d}` + + if(sortby === 'search') { + if(typeof(q) == 'undefined' || q == '') + return res.redirect('/subreddits') + + key = `subreddits:search:q:${q}:nsfw:${nsfw}${d}` + searching = true + } + + redis.get(key, (error, json) => { + if(error) { + console.error(`Error getting the subreddits key from redis.`, error) + return res.render('index', { json: null, user_preferences: req.cookies }) + } + if(json) { + console.log(`Got subreddits key from redis.`); + (async () => { + let processed_json = await processJsonSubredditsExplore(json, 'redis', null, req.cookies) + if(!processed_json.error) { + return res.render('subreddits_explore', { + json: processed_json, + sortby: sortby, + after: after, + before: before, + q: q, + nsfw: nsfw, + searching: searching, + subreddits_front: (!before && !after) ? true : false, + user_preferences: req.cookies, + instance_nsfw_enabled: config.nsfw_enabled + }) + } else { + return res.render('subreddits_explore', { + json: null, + error: true, + data: processed_json, + user_preferences: req.cookies + }) + } + })() + } else { + let url = '' + if(config.use_reddit_oauth) { + if(!searching) + url = `https://oauth.reddit.com/subreddits/${sortby}?api_type=json&count=25&g=GLOBAL&t=${d}` + else + url = `https://oauth.reddit.com/subreddits/search?api_type=json&q=${q}&include_over_18=${nsfw}${d}` + } else { + if(!searching) + url = `https://reddit.com/subreddits/${sortby}.json?api_type=json&count=25&g=GLOBAL&t=${d}` + else + url = `https://reddit.com/subreddits/search.json?api_type=json&q=${q}&include_over_18=${nsfw}${d}` + } + + fetch(encodeURI(url), redditApiGETHeaders()) + .then(result => { + if(result.status === 200) { + result.json() + .then(json => { + let ex = config.setexs.subreddits_explore.front + if(sortby === 'new') + ex = config.setexs.subreddits_explore.new_page + redis.setex(key, ex, JSON.stringify(json), (error) => { + if(error) { + console.error(`Error setting the subreddits key to redis.`, error) + return res.render('subreddits_explore', { json: null, user_preferences: req.cookies }) + } else { + console.log(`Fetched the JSON from reddit.com/subreddits.`); + (async () => { + let processed_json = await processJsonSubredditsExplore(json, 'from_online', null, req.cookies) + return res.render('subreddits_explore', { + json: processed_json, + sortby: sortby, + after: after, + before: before, + q: q, + nsfw: nsfw, + searching: searching, + subreddits_front: (!before && !after) ? true : false, + user_preferences: req.cookies, + instance_nsfw_enabled: config.nsfw_enabled + }) + })() + } + }) + }) + } else { + if(result.status === 404) { + console.log('404 – Subreddits not found') + } else { + console.error(`Something went wrong while fetching data from Reddit. ${result.status} – ${result.statusText}`) + console.error(config.reddit_api_error_text) + } + return res.render('index', { + json: null, + http_status_code: result.status, + user_preferences: req.cookies + }) + } + }).catch(error => { + console.error(`Error fetching the JSON file from reddit.com/subreddits.`, error) + }) + } + }) + }) app.get('/subscribe/:subreddit', (req, res, next) => { let subreddit = req.params.subreddit @@ -47,11 +183,11 @@ module.exports = (app, redis, fetch, RedditAPI) => { subbed.push(subreddit) res.cookie('subbed_subreddits', subbed, { maxAge: 365 * 24 * 60 * 60 * 1000, httpOnly: true }) - + if(!back) return res.redirect('/r/' + subreddit) else { - back = back.replace(/,/g, '+') + back = back.replace(/,/g, '+').replace(/§1/g, '&') return res.redirect(back) } }) @@ -105,7 +241,7 @@ module.exports = (app, redis, fetch, RedditAPI) => { if(!back) return res.redirect('/r/' + subreddit) else { - back = back.replace(/,/g, '+') + back = back.replace(/,/g, '+').replace(/§1/g, '&') return res.redirect(back) } }) @@ -577,7 +713,7 @@ module.exports = (app, redis, fetch, RedditAPI) => { }) } }).catch(error => { - console.error(`Error fetching the JSON file from reddit.com/r/${subreddit}.`, error) + console.error(`Error fetching the JSON file from reddit.com/r/random.`, error) }) }) diff --git a/static/css/styles.css b/static/css/styles.css index 8eeee73..ae6ac49 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -555,6 +555,32 @@ footer a { border-left: 1px solid #dcdcdc; margin-top: 10px; } +.infobar { + background-color: #f6e69f; + margin: 5px 305px 5px 11px; + padding: 5px 10px; + float: left; + width: calc(100% - 50px); +} +.infobar.blue { + background: #eff8ff; + border: 1px solid #93abc2; +} +.infobar.explore { + margin-bottom: 15px; +} +.explore#links .link { + padding-left: 10%; +} +.explore#links .link .sub-button { + float: left; + margin: 7px 0px; + width: 90px; +} +.explore#links .link .content { + float: left; + width: calc(100% - 120px); +} /* POST */ #post { @@ -564,17 +590,6 @@ footer a { float: left; width: 100%; } -#post .infobar { - background-color: #f6e69f; - margin: 5px 305px 5px 11px; - padding: 5px 10px; - float: left; - width: calc(100% - 50px); -} -#post .infobar.blue { - background: #eff8ff; - border: 1px solid #93abc2; -} #post header { padding-top: 0px; } @@ -1020,6 +1035,11 @@ a.sub-to-subreddit.gray { margin-left: 20px; margin-top: 40px; } +#search.explore { + width: calc(100% - 60px); + margin-left: 20px; + margin-bottom: 15px; +} #search form { max-width: 600px; } @@ -1431,5 +1451,15 @@ code { } a.sub-to-subreddit { padding: 8px 10px 8px 10px; + } + .explore#links .link { + padding-left: 5px; + } + .explore#links .link .sub-button { + margin: 7px 0px; + width: 90px; + } + .explore#links .link .entry { + width: calc(100% - 20px); } } diff --git a/views/subreddits_explore.pug b/views/subreddits_explore.pug new file mode 100644 index 0000000..fe23008 --- /dev/null +++ b/views/subreddits_explore.pug @@ -0,0 +1,96 @@ +doctype html +html + head + title subreddits - explore + include includes/head.pug + body(class=""+ user_preferences.theme +"") + include includes/topbar.pug + if json === null + h1 Error occured + p Error: #{JSON.stringify(json.error_data)} + else + header + a(href="/", class="main") + h1 teddit + .bottom + a(href="/subreddits", class="subreddit") + h2 subreddits - explore + ul.tabmenu + li(class=!sortby || sortby == 'hot' ? 'active' : '') + a(href="/subreddits") popular + li(class=sortby === 'new' ? 'active' : '') + a(href="/subreddits/new") new + #search.explore + form(action="/subreddits/search", method="GET") + div + label(for="q") search subreddits + input(type="text", name="q", id="q", value="" + (q ? q : '') + "", placeholder="search") + div + label(for="nsfw") include NSFW results + if nsfw === 'on' + input(type="checkbox", name="nsfw", id="nsfw", checked="checked") + else + input(type="checkbox", name="nsfw", id="nsfw") + input(type="submit", value="search") + #links.sr.explore + if json.links.length === 0 + p nothing here + else + .infobar.explore + p click the subscribe or unsubscribe buttons to choose which subreddits appear on the home feed. + each link in json.links + .link + .entry + - + let subbed_to_this_subreddit = false + let subbed = [] + if(user_preferences.subbed_subreddits && Array.isArray(user_preferences.subbed_subreddits)) + subbed = user_preferences.subbed_subreddits + for(let i = 0; i < subbed.length; i++) { + if(subbed[i].toLowerCase() === link.display_name.toLowerCase()) + subbed_to_this_subreddit = true + } + .sub-button + if subbed_to_this_subreddit + if !searching + a(href="/unsubscribe/" + link.display_name + "?b=/subreddits/" + sortby + "?after=" + after + "§1before=" + before + "", class="sub-to-subreddit gray", title="subscriptions are saved in your browser's cookies") unsubscribe + else + a(href="/unsubscribe/" + link.display_name + "?b=/subreddits/search?q=" + q + "§1nsfw=" + nsfw + "§1after=" + after + "§1before=" + before + "", class="sub-to-subreddit gray", title="subscriptions are saved in your browser's cookies") unsubscribe + else + if !searching + a(href="/subscribe/" + link.display_name + "?b=/subreddits/" + sortby + "?after=" + after + "§1before=" + before + "", class="sub-to-subreddit", title="subscriptions are saved in your browser's cookies") subscribe + else + a(href="/subscribe/" + link.display_name + "?b=/subreddits/search?q=" + q + "§1nsfw=" + nsfw + "§1after=" + after + "§1before=" + before + "", class="sub-to-subreddit", title="subscriptions are saved in your browser's cookies") subscribe + .content + .title + a(href="" + link.url + "", rel="noopener noreferrer") + h2 #{link.display_name_prefixed}: #{cleanTitle(link.title)} + .description + p #{cleanTitle(link.public_description)} + .meta + p.subscribers #{kFormatter(link.subscribers)} subscribers, + p.submitted   created + span(title="" + toUTCString(link.created) + "") #{timeDifference(link.created)} + .links + if link.over_18 + span.tag.nsfw NSFW + if json.info.before || json.info.after + .view-more-links + if json.info.before && !subreddits_front + a(href="/subreddits/" + sortby + "?before=" + json.info.before + "&nsfw=" + nsfw + "&q=" + (q ? q : '') + "") ‹ prev + if json.info.after + a(href="/subreddits/" + sortby + "?after=" + json.info.after + "&nsfw=" + nsfw + "&q=" + (q ? q : '') + "") next › + #sidebar + .content + if user_preferences.subbed_subreddits && Array.isArray(user_preferences.subbed_subreddits) + p your subscribed subreddits + ul.subreddit-listing + each subreddit in user_preferences.subbed_subreddits + li + if !searching + a(href="/unsubscribe/" + subreddit + "?b=/subreddits/" + sortby + "?after=" + after + "§1before=" + before + "", class="sub-to-subreddit gray", title="subscriptions are saved in your browser's cookies") unsubscribe + a(href="/r/" + subreddit) #{subreddit} + else + a(href="/unsubscribe/" + subreddit + "?b=/subreddits/search?q=" + q + "§1nsfw=" + nsfw + "§1after=" + after + "§1before=" + before + "", class="sub-to-subreddit gray", title="subscriptions are saved in your browser's cookies") unsubscribe + a(href="/r/" + subreddit) #{subreddit} + include includes/footer.pug