From 43141a802e443fb16467ee37ff1b1a99c460c545 Mon Sep 17 00:00:00 2001 From: 3nprob <3nprob@3nprob> Date: Fri, 8 Oct 2021 15:45:56 +0900 Subject: [PATCH] Add tag page --- .eslintrc | 1 + src/fetchers.ts | 15 +++++++-- src/handlers.ts | 21 +++++++++--- src/index.ts | 3 +- src/types/index.d.ts | 55 ++++++++++++++++++++++++++++++- static/css/custom.css | 9 ++++++ static/css/styles.css | 70 +++++++++++++++++++++++++++++++++++++++- templates/user-posts.pug | 12 +++++-- 8 files changed, 174 insertions(+), 12 deletions(-) diff --git a/.eslintrc b/.eslintrc index cf18051..0c908e8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -81,6 +81,7 @@ "keyword-spacing": [1, {"before": true, "after": true}], "space-before-blocks": [1, "always"], "@typescript-eslint/ban-ts-ignore": 0, + "@typescript-eslint/ban-types": 0, "@typescript-eslint/explicit-function-return-type": 0, "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/no-shadow": ["error"], diff --git a/src/fetchers.ts b/src/fetchers.ts index abed59d..8e14d30 100644 --- a/src/fetchers.ts +++ b/src/fetchers.ts @@ -62,11 +62,22 @@ export const fetchComments = async (galleryID: string): Promise => { /* eslint-enable max-len */ } -export const fetchUserPosts = async (userID: string, sort: Sorting = 'newest'): Promise => { +export const fetchUserPosts = async (userID: string, sort: Sorting = 'newest'): Promise => { /* eslint-disable max-len */ // https://api.imgur.com/3/account/mombotnumber5/submissions/0/newest?album_previews=1&client_id=${CLIENT_ID} const response = await got( - `https://api.imgur.com/3/account/${userID.toLowerCase()}/submissions/0/newest?album_previews=1&client_id=${CONFIG.imgur_client_id}`, + `https://api.imgur.com/3/gallery/${userID.toLowerCase()}/submissions/0/${sort}?album_previews=1&client_id=${CONFIG.imgur_client_id}`, + { agent } + ); + return JSON.parse(response.body).data; + /* eslint-enable max-len */ +} + +export const fetchTagPosts = async (tagID: string, sort: Sorting = 'viral'): Promise => { + /* eslint-disable max-len */ + // https://api.imgur.com/3/account/mombotnumber5/submissions/0/newest?album_previews=1&client_id=${CLIENT_ID} + const response = await got( + `https://api.imgur.com/3/gallery/t/${tagID.toLowerCase()}/${sort}/week/0?client_id=${CONFIG.imgur_client_id}`, { agent } ); return JSON.parse(response.body).data; diff --git a/src/handlers.ts b/src/handlers.ts index 39fe571..03b3fb4 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -1,6 +1,7 @@ import Hapi = require('@hapi/hapi'); import '@hapi/vision'; -import { fetchAlbum, fetchAlbumURL, fetchComments, fetchGallery, fetchMedia, fetchUserPosts } from './fetchers'; +import { fetchAlbum, fetchAlbumURL, fetchComments, fetchGallery, fetchMedia, fetchTagPosts, fetchUserPosts } + from './fetchers'; import * as util from './util'; import CONFIG from './config'; @@ -41,17 +42,27 @@ export const handleUser = async (request: Hapi.Request, h: Hapi.ResponseToolkit) return 'User page disabled. Rimgu administrator needs to enable API for this to work.'; } const userID = request.params.userID; - const userPosts = await fetchUserPosts(userID); + const posts = await fetchUserPosts(userID); return h.view('user-posts', { - userPosts, + posts, pageTitle: CONFIG.page_title, util, }); }; -export const handleTag = (request: Hapi.Request, h: Hapi.ResponseToolkit) => { +export const handleTag = async (request: Hapi.Request, h: Hapi.ResponseToolkit) => { // https://imgur.com/t/funny - throw new Error('not implemented'); + if (!CONFIG.use_api) { + return 'Tag page disabled. Rimgu administrator needs to enable API for this to work.'; + } + const tagID = request.params.tagID; + const result = await fetchTagPosts(tagID); + return h.view('user-posts', { + posts: result.items, + pageTitle: CONFIG.page_title, + tag: result, + util, + }); }; export const handleGallery = async (request: Hapi.Request, h: Hapi.ResponseToolkit) => { diff --git a/src/index.ts b/src/index.ts index b85e9d4..3dedc53 100644 --- a/src/index.ts +++ b/src/index.ts @@ -82,7 +82,8 @@ process.on('unhandledRejection', (err) => { if (!CONFIG.use_api) { console.log('Running without imgur client ID; certain views and functionality missing.'); -} else if (!CONFIG.imgur_client_id) { +} +else if (!CONFIG.imgur_client_id) { console.error('imgur_client_id missing. Configure it via RIMGU_IMGUR_CLIENT_ID or set RIMGU_USE_API=false'); process.exit(1); } diff --git a/src/types/index.d.ts b/src/types/index.d.ts index fafd587..379fead 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -8,7 +8,7 @@ interface Account { type MediaMimeType = 'image/jpeg' | 'image/png' | 'image/gif'; type MediaType = 'image'; type MediaExt = 'jpeg' | 'png' | 'gif'; -type Sorting = 'newest' | 'oldest' | 'best'; +type Sorting = 'newest' | 'oldest' | 'best' | 'viral'; interface Tag { tag: string; @@ -75,3 +75,56 @@ interface Gallery { tags: Tag[]; cover: Media; } + +interface PostTag { + name: string; + display_name: string; + followers: number; + total_items: number; + following: boolean; + is_whitelisted: boolean; + background_hash: string; + thumbnail_hash: string; + accent: string; + background_is_animated: boolean; + thumbnail_is_animated: boolean; + is_promoted: boolean; + description: string; + logo_hash: string; + logo_destination_url: string; + description_annotations: {} +} + +interface Post { + id: string; + account_id: number; + type: MediaMimeType; + animated: boolean; + width: number; + height: number; + size: number; + views: number; + bandwidth: number; + vote: null; + favorite: boolean; + nsfw: boolean; + section: string; + account_url: string; + is_ad: boolean; + in_most_viral: boolean; + has_sound: boolean; + tags: PostTag[]; + link: string; + comment_count: number; + ups: number; + downs: number; + score: number; + points: number; + is_album: boolean; +} + +interface TagResult extends PostTag { + items: Post[]; + success: boolean; + status: number; +} diff --git a/static/css/custom.css b/static/css/custom.css index 5274801..a428c46 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -2,3 +2,12 @@ img.album-img { max-width: 100%; max-height: 100%; } + +.TagsCover { + top: 0px; + min-height: 351px; +} + +.ProfilePosts-posts { + margin-top: 311px; +} diff --git a/static/css/styles.css b/static/css/styles.css index 9b88451..9824949 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -388,4 +388,72 @@ Post-item-title { .Post-item-media .PostVideo { max-height: 500px; -} \ No newline at end of file +} + + +/* TAG PAGE */ + +.Cover-item-count { + text-transform: uppercase; +} + +.Cover-stats { + font-size: 16px; + margin: 13px 0 20px; +} + +.Cover-description { + margin: 0 0 10px; + font-size: 22px; + max-width: 390px; +} + +Cover-description, .Cover-name, .Cover-stats { + text-align: center; +} + +.Cover-name { + font-size: 52px; + color: #fff; + margin: 0; + font-family: Proxima Nova ExtraBold,Helvetica Neue,Helvetica,Arial,sans-serif; +} + +.Cover-metadata { + position: relative; + margin: 0 auto; + text-align: center; + height: 100%; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: center; + align-content: stretch; + align-items: center; + z-index: 0; +} + +.NewCover-change-sort-wrapper .Dropdown, .NewCover .Cover-metadata { + margin: auto; +} + +.NewCover { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + background-size: cover; + background-position: 50%; + background-color: #474a51; + padding-bottom: 76px; + opacity: 1; + transition: opacity .5s,box-shadow .4s; +} + +.App-cover { + width: 100%; + position: absolute; + top: 0; + left: 0; + right: 0; +} diff --git a/templates/user-posts.pug b/templates/user-posts.pug index 953a96e..d353e51 100644 --- a/templates/user-posts.pug +++ b/templates/user-posts.pug @@ -3,10 +3,18 @@ html include includes/head.pug body .Profile - .App-cover.NewCover.ProfileCover + if tag + .App-cover.NewCover.TagsCover(style='background-image: linear-gradient(rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0.3) 20%), url("/' + tag.background_hash + '.jpg");') + .Cover-metadata + h1.Cover-name= tag.display_name + p.description= tag.description + .Cover-stats + .Cover-item-count #{tag.total_items} posts + else if user + .App-cover.NewCover.ProfileCover .ProfilePosts-posts .ProfilePosts-top - each post in userPosts + each post in posts div.ProfilePost a.Post-item.novote(href="/gallery/"+post.id) .Post-item-container