From df6638fc9ee69e4654959f133300460013a6a18d Mon Sep 17 00:00:00 2001 From: Simon Lecoq <22963968+lowlighter@users.noreply.github.com> Date: Tue, 2 Mar 2021 10:17:00 +0100 Subject: [PATCH] Add hashnode support for posts plugin and additional options (#165) --- source/app/mocks/api/axios/post/hashnode.mjs | 23 ++++++++++ source/app/web/statics/app.placeholder.js | 6 ++- source/plugins/posts/index.mjs | 20 ++++++--- source/plugins/posts/metadata.yml | 13 ++++++ source/plugins/posts/queries/hashnode.graphql | 12 ++++++ source/plugins/posts/tests.yml | 8 ++++ source/templates/classic/partials/posts.ejs | 16 +++++-- source/templates/classic/style.css | 43 ++++++++++++++----- 8 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 source/app/mocks/api/axios/post/hashnode.mjs create mode 100644 source/plugins/posts/queries/hashnode.graphql diff --git a/source/app/mocks/api/axios/post/hashnode.mjs b/source/app/mocks/api/axios/post/hashnode.mjs new file mode 100644 index 00000000..9936df43 --- /dev/null +++ b/source/app/mocks/api/axios/post/hashnode.mjs @@ -0,0 +1,23 @@ +/**Mocked data */ + export default function({faker, url, body, login = faker.internet.userName()}) { + if (/^https:..api.hashnode.com.*$/.test(url)) { + console.debug(`metrics/compute/mocks > mocking hashnode result > ${url}`) + return ({ + status:200, + data:{ + data:{ + user:{ + publication:{ + posts:new Array(30).fill(null).map(_ => ({ + title:faker.lorem.sentence(), + brief:faker.lorem.paragraph(), + coverImage:null, + dateAdded:faker.date.recent(), + })), + }, + }, + }, + }, + }) + } + } diff --git a/source/app/web/statics/app.placeholder.js b/source/app/web/statics/app.placeholder.js index 5ee8733b..a1ee7ba5 100644 --- a/source/app/web/statics/app.placeholder.js +++ b/source/app/web/statics/app.placeholder.js @@ -344,9 +344,13 @@ ...(set.plugins.enabled.posts ? ({ posts:{ source:options["posts.source"], + descriptions:options["posts.descriptions"], + covers:options["posts.covers"], list:new Array(Number(options["posts.limit"])).fill(null).map(_ => ({ title:faker.lorem.sentence(), - date:faker.date.recent().toString().substring(4, 10).trim() + description:faker.lorem.paragraph(), + date:faker.date.recent(), + image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==", })) } }) : null), diff --git a/source/plugins/posts/index.mjs b/source/plugins/posts/index.mjs index 4465fd3f..9d652384 100644 --- a/source/plugins/posts/index.mjs +++ b/source/plugins/posts/index.mjs @@ -1,5 +1,5 @@ //Setup - export default async function({login, data, imports, q, account}, {enabled = false} = {}) { + export default async function({login, data, imports, q, queries, account}, {enabled = false} = {}) { //Plugin execution try { //Check if plugin is enabled and requirements are met @@ -7,7 +7,7 @@ return null //Load inputs - let {source, limit, user} = imports.metadata.plugins.posts.inputs({data, account, q}) + let {source, descriptions, covers, limit, user} = imports.metadata.plugins.posts.inputs({data, account, q}) //Retrieve posts console.debug(`metrics/compute/${login}/plugins > posts > processing with source ${source}`) @@ -16,7 +16,12 @@ //Dev.to case "dev.to":{ console.debug(`metrics/compute/${login}/plugins > posts > querying api`) - posts = (await imports.axios.get(`https://dev.to/api/articles?username=${user}&state=fresh`)).data.map(({title, readable_publish_date:date}) => ({title, date})) + posts = (await imports.axios.get(`https://dev.to/api/articles?username=${user}&state=fresh`)).data.map(({title, description, published_at:date, cover_image:image}) => ({title, description, date, image})) + break + } + //Hashnode + case "hashnode":{ + posts = (await imports.axios.post("https://api.hashnode.com", {query:queries.posts.hashnode({user})}, {headers:{"Content-type":"application/json"}})).data.data.user.publication.posts.map(({title, brief:description, dateAdded:date, coverImage:image}) => ({title, description, date, image})) break } //Unsupported @@ -26,13 +31,18 @@ //Format posts if (Array.isArray(posts)) { - //Limit tracklist + //Limit posts if (limit > 0) { console.debug(`metrics/compute/${login}/plugins > posts > keeping only ${limit} posts`) posts.splice(limit) } + //Cover images + if (covers) { + console.debug(`metrics/compute/${login}/plugins > posts > formatting cover images`) + posts = await Promise.all(posts.map(async({image, ...post}) => ({image:await imports.imgb64(image, {width:144, height:-1}), ...post}))) + } //Results - return {source, list:posts} + return {source, descriptions, covers, list:posts} } //Unhandled error diff --git a/source/plugins/posts/metadata.yml b/source/plugins/posts/metadata.yml index a53ef737..fa46a5d7 100644 --- a/source/plugins/posts/metadata.yml +++ b/source/plugins/posts/metadata.yml @@ -19,6 +19,19 @@ inputs: default: "" values: - dev.to # Dev.to + - hashnode + + # Display a few lines about each posts + plugin_posts_descriptions: + description: Display posts descriptions + type: boolean + default: no + + # Display posts cover images + plugin_posts_covers: + description: Display posts cover images + type: boolean + default: no # Number of posts to display plugin_posts_limit: diff --git a/source/plugins/posts/queries/hashnode.graphql b/source/plugins/posts/queries/hashnode.graphql new file mode 100644 index 00000000..bb9d6b29 --- /dev/null +++ b/source/plugins/posts/queries/hashnode.graphql @@ -0,0 +1,12 @@ +query PostsHashnode { + user(username: "$user"){ + publication{ + posts(page: 1) { + title + brief + coverImage + dateAdded + } + } + } +} diff --git a/source/plugins/posts/tests.yml b/source/plugins/posts/tests.yml index a82bb4c8..09f7eb7f 100644 --- a/source/plugins/posts/tests.yml +++ b/source/plugins/posts/tests.yml @@ -5,3 +5,11 @@ plugin_posts: yes plugin_posts_source: dev.to plugin_posts_user: lowlighter + +- name: Posts plugin (hashnode) + uses: lowlighter/metrics@latest + with: + token: NOT_NEEDED + plugin_posts: yes + plugin_posts_source: hashnode + plugin_posts_user: lowlighter diff --git a/source/templates/classic/partials/posts.ejs b/source/templates/classic/partials/posts.ejs index 46774d9b..4394fb82 100644 --- a/source/templates/classic/partials/posts.ejs +++ b/source/templates/classic/partials/posts.ejs @@ -17,12 +17,22 @@ From <%= plugins.posts.source %> <% if (plugins.posts.list.length) { %> - <% for (const {title, date} of plugins.posts.list) { %> + <% for (const {title, description, image, date} of plugins.posts.list) { %>
-
<%= date %>
-
<%= title %>
+
+
<%= f.date(new Date(date), {dateStyle:"short"}) %>
+ <% if (plugins.posts.covers) { %> +
+ <% } %> +
+
+
<%= title %>
+ <% if (plugins.posts.descriptions) { %> +
<%= description %>
+ <% } %> +
<% } %> diff --git a/source/templates/classic/style.css b/source/templates/classic/style.css index 7c7c8d4a..d20c98ff 100644 --- a/source/templates/classic/style.css +++ b/source/templates/classic/style.css @@ -287,20 +287,43 @@ display: flex; margin-bottom: 4px; } - .post .infos .title { - font-size: 14px; - width: 380px; - white-space: normal; - overflow: hidden; - text-overflow: ellipsis; - max-height: 40px;; - } - .post .infos .date { + .post .infos .left { flex-shrink: 0; font-size: 12px; color: #666666; - width: 60px; + width: 72px; padding-top: 1px; + text-align: center; + } + .post .infos .cover { + width: 100%; + height: 56px; + background-position: center; + background-size: cover; + border-radius: 6px; + overflow: hidden; + } + .post .infos .right { + width: 376px; + padding-left: 4px; + } + .post .infos .title, .post .infos .description { + font-size: 14px; + white-space: normal; + overflow: hidden; + text-overflow: ellipsis; + max-height: 38px; + /* May not work in all browsers */ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + } + .post .infos .description { + margin-top: 3px; + font-size: 12px; + max-height: 48px; + color: #666666; + -webkit-line-clamp: 3; } /* Topics */