From 5bfccb0faf32c1e8058263a7c6b520e460dae5c8 Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Sat, 28 Oct 2023 18:45:44 +0530 Subject: [PATCH 1/7] render preview image url in card, when imageUrl is provided --- src/cards/repo-card.js | 2 ++ src/common/Card.js | 23 +++++++++++++++++++ src/fetchers/repo-fetcher.js | 1 + src/fetchers/types.d.ts | 1 + .../renderWakatimeCard.test.js.snap | 2 ++ 5 files changed, 29 insertions(+) diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 09b5841880a97..0d0dbcceef6da 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -61,6 +61,7 @@ const renderRepoCard = (repo, options = {}) => { isTemplate, starCount, forkCount, + openGraphImageUrl, } = repo; const { hide_border = false, @@ -141,6 +142,7 @@ const renderRepoCard = (repo, options = {}) => { height, border_radius, colors, + imageUrl: openGraphImageUrl, }); card.disableAnimations(); diff --git a/src/common/Card.js b/src/common/Card.js index d32da56255f89..93564ffda94dc 100644 --- a/src/common/Card.js +++ b/src/common/Card.js @@ -11,6 +11,8 @@ class Card { * @param {string?=} args.customTitle Card custom title. * @param {string?=} args.defaultTitle Card default title. * @param {string?=} args.titlePrefixIcon Card title prefix icon. + * @param {string?=} args.imageUrl Card preview image. + * @param {number?=} args.imageHeight Card preview image. * @param {object?=} args.colors Card colors arguments. * @param {string} args.colors.titleColor Card title color. * @param {string} args.colors.textColor Card text color. @@ -27,6 +29,8 @@ class Card { customTitle, defaultTitle = "", titlePrefixIcon, + imageUrl = "", + imageHeight = 200, }) { this.width = width; this.height = height; @@ -36,6 +40,8 @@ class Card { this.border_radius = border_radius; + this.imageHeight = imageHeight; + this.imageUrl = imageUrl; // returns theme based colors with proper overrides and defaults this.colors = colors; this.title = @@ -51,6 +57,10 @@ class Card { this.animations = true; this.a11yTitle = ""; this.a11yDesc = ""; + if (this.imageUrl) { + this.height += this.imageHeight; + this.paddingY += this.imageHeight; + } } /** @@ -199,6 +209,18 @@ class Card { `; }; + renderImage = () => { + if (!this.imageUrl) { + return ""; + } + return ` + + + + `; + }; + /** * @param {string} body The inner body of the card. * @returns {string} The rendered card. @@ -264,6 +286,7 @@ class Card { > ${body} + ${this.renderImage()} `; } diff --git a/src/fetchers/repo-fetcher.js b/src/fetchers/repo-fetcher.js index 6438f8895cfb6..b361a987d7a78 100644 --- a/src/fetchers/repo-fetcher.js +++ b/src/fetchers/repo-fetcher.js @@ -34,6 +34,7 @@ const fetcher = (variables, token) => { name } forkCount + openGraphImageUrl } query getRepo($login: String!, $repo: String!) { user(login: $login) { diff --git a/src/fetchers/types.d.ts b/src/fetchers/types.d.ts index affb407b816b0..e7775eea610cb 100644 --- a/src/fetchers/types.d.ts +++ b/src/fetchers/types.d.ts @@ -22,6 +22,7 @@ export type RepositoryData = { }; forkCount: number; starCount: number; + openGraphImageUrl: string; }; export type StatsData = { diff --git a/tests/__snapshots__/renderWakatimeCard.test.js.snap b/tests/__snapshots__/renderWakatimeCard.test.js.snap index f38ac26ef07f7..6592058915ac3 100644 --- a/tests/__snapshots__/renderWakatimeCard.test.js.snap +++ b/tests/__snapshots__/renderWakatimeCard.test.js.snap @@ -150,6 +150,7 @@ exports[`Test Render WakaTime Card should render correctly with compact layout 1 + " `; @@ -302,6 +303,7 @@ exports[`Test Render WakaTime Card should render correctly with compact layout w + " `; From d4b16d2935814cb5c962dfbad55ccd78e8847021 Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Thu, 2 Nov 2023 10:51:15 +0530 Subject: [PATCH 2/7] make image_preview optional --- api/pin.js | 2 ++ src/cards/repo-card.js | 3 ++- src/cards/types.d.ts | 1 + src/common/Card.js | 3 +++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/api/pin.js b/api/pin.js index 21ecf966b3ff4..c33848afcd744 100644 --- a/api/pin.js +++ b/api/pin.js @@ -24,6 +24,7 @@ export default async (req, res) => { locale, border_radius, border_color, + show_image, } = req.query; res.setHeader("Content-Type", "image/svg+xml"); @@ -80,6 +81,7 @@ export default async (req, res) => { border_color, show_owner: parseBoolean(show_owner), locale: locale ? locale.toLowerCase() : null, + show_image: parseBoolean(show_image), }), ); } catch (err) { diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 0d0dbcceef6da..72a565badad4b 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -74,6 +74,7 @@ const renderRepoCard = (repo, options = {}) => { border_radius, border_color, locale, + show_image = false, } = options; const lineHeight = 10; @@ -142,7 +143,7 @@ const renderRepoCard = (repo, options = {}) => { height, border_radius, colors, - imageUrl: openGraphImageUrl, + imageUrl: show_image ? openGraphImageUrl : "", }); card.disableAnimations(); diff --git a/src/cards/types.d.ts b/src/cards/types.d.ts index dce964d21af7e..df5fc54cc0d3c 100644 --- a/src/cards/types.d.ts +++ b/src/cards/types.d.ts @@ -32,6 +32,7 @@ export type StatCardOptions = CommonOptions & { export type RepoCardOptions = CommonOptions & { show_owner: boolean; + show_image: boolean; }; export type TopLangOptions = CommonOptions & { diff --git a/src/common/Card.js b/src/common/Card.js index 93564ffda94dc..e7cae5273aed6 100644 --- a/src/common/Card.js +++ b/src/common/Card.js @@ -209,6 +209,9 @@ class Card { `; }; + /** + * @returns {string} Renders social preview image + */ renderImage = () => { if (!this.imageUrl) { return ""; From 075add2e595b22f570d556ee4e438075bd64b935 Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Thu, 2 Nov 2023 11:05:45 +0530 Subject: [PATCH 3/7] add line spacing --- src/common/Card.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/Card.js b/src/common/Card.js index e7cae5273aed6..ad1ad61995c47 100644 --- a/src/common/Card.js +++ b/src/common/Card.js @@ -42,6 +42,7 @@ class Card { this.imageHeight = imageHeight; this.imageUrl = imageUrl; + // returns theme based colors with proper overrides and defaults this.colors = colors; this.title = From 03d7f85d770e682dbcaecf5a275347bb7bf4c7c1 Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Thu, 2 Nov 2023 12:44:08 +0530 Subject: [PATCH 4/7] add image-preview tests --- src/common/Card.js | 4 ++-- tests/renderRepoCard.test.js | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/common/Card.js b/src/common/Card.js index ad1ad61995c47..f1823b2e36e27 100644 --- a/src/common/Card.js +++ b/src/common/Card.js @@ -218,8 +218,8 @@ class Card { return ""; } return ` - - + `; diff --git a/tests/renderRepoCard.test.js b/tests/renderRepoCard.test.js index 050e7109490bb..0c8e3ea42d15e 100644 --- a/tests/renderRepoCard.test.js +++ b/tests/renderRepoCard.test.js @@ -18,6 +18,8 @@ const data_repo = { }, starCount: 38000, forkCount: 100, + openGraphImageUrl: + "https://repository-images.githubusercontent.com/266996769/1e4f2180-b194-11ea-9806-2395601f119b", }, }; @@ -339,4 +341,21 @@ describe("Test renderRepoCard", () => { "No description provided", ); }); + + it("should render repo's social preview image, when show_image is true", () => { + document.body.innerHTML = renderRepoCard(data_repo.repository, { + show_image: true, + }); + + expect(queryByTestId(document.body, "card-image")).toBeInTheDocument(); + expect( + queryByTestId(document.body, "card-image").children[0], + ).toHaveAttribute("href", data_repo.repository.openGraphImageUrl); + }); + + it("should not render repo's social preview image by default", () => { + document.body.innerHTML = renderRepoCard(data_repo.repository); + + expect(queryByTestId(document.body, "card-image")).not.toBeInTheDocument(); + }); }); From 9575f2ea95a2be6ab45187833c16d28d8636f637 Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Thu, 2 Nov 2023 12:44:33 +0530 Subject: [PATCH 5/7] update RepoCard docs --- readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/readme.md b/readme.md index a010eca3b6d5f..3d1814852110e 100644 --- a/readme.md +++ b/readme.md @@ -386,6 +386,7 @@ If we don't support your language, please consider contributing! You can find mo #### Repo Card Exclusive Options * `show_owner` - Shows the repo's owner name *(boolean)*. Default: `false`. +* `show_image` - Shows the repo's social preview image *(boolean)*. Default: `false`. #### Gist Card Exclusive Options @@ -447,6 +448,11 @@ Use [show\_owner](#repo-card-exclusive-options) query option to include the repo ![Readme Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra\&repo=github-readme-stats\&show_owner=true) + +Use [show\_image](#repo-card-exclusive-options) query option to include the repo's social preview image header + +![Readme Card](https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra\&repo=github-readme-stats\&show_image=true) + # GitHub Gist Pins GitHub gist pins allow you to pin gists in your GitHub profile using a GitHub readme profile. From 99571df33ff7a0cea5d853c19a86e0d2a6a6a737 Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Fri, 3 Nov 2023 17:29:10 +0530 Subject: [PATCH 6/7] convert image to base64 before rendering --- api/pin.js | 5 +++++ src/common/utils.js | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/api/pin.js b/api/pin.js index c33848afcd744..8ba9feb8a28e6 100644 --- a/api/pin.js +++ b/api/pin.js @@ -3,6 +3,7 @@ import { blacklist } from "../src/common/blacklist.js"; import { clampValue, CONSTANTS, + getBase64URIFromImage, parseBoolean, renderError, } from "../src/common/utils.js"; @@ -69,6 +70,10 @@ export default async (req, res) => { }, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, ); + repoData.openGraphImageUrl = await getBase64URIFromImage( + repoData.openGraphImageUrl, + ); + return res.send( renderRepoCard(repoData, { hide_border: parseBoolean(hide_border), diff --git a/src/common/utils.js b/src/common/utils.js index 6792df881ac7a..81edc0a8a572a 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -569,6 +569,27 @@ const dateDiff = (d1, d2) => { return Math.round(diff / (1000 * 60)); }; +const getBase64URIFromImage = async (imageURL) => { + try { + const response = await axios.get(imageURL, { + responseType: "arraybuffer", + }); + + if (response.status === 200) { + const base64Image = Buffer.from(response.data, "binary").toString( + "base64", + ); + const mimeType = response.headers["content-type"]; + return `data:${mimeType};base64,${base64Image}`; + } else { + throw new Error("Failed to fetch the image."); + } + } catch (error) { + console.error("Error:", error); + return null; + } +}; + export { ERROR_CARD_LENGTH, renderError, @@ -595,4 +616,5 @@ export { chunkArray, parseEmojis, dateDiff, + getBase64URIFromImage, }; From 3705f73b9716b3aa802aa88b101066ff330e300c Mon Sep 17 00:00:00 2001 From: Bikrant Jajware Date: Fri, 3 Nov 2023 18:30:01 +0530 Subject: [PATCH 7/7] convert image to base64 before rendering --- api/pin.js | 2 +- src/cards/repo-card.js | 4 ++-- src/common/Card.js | 12 ++++++------ src/fetchers/types.d.ts | 2 +- tests/renderRepoCard.test.js | 5 ++--- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/api/pin.js b/api/pin.js index 8ba9feb8a28e6..fe724bbea89a7 100644 --- a/api/pin.js +++ b/api/pin.js @@ -70,7 +70,7 @@ export default async (req, res) => { }, s-maxage=${cacheSeconds}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`, ); - repoData.openGraphImageUrl = await getBase64URIFromImage( + repoData.stringifiedRepoImage = await getBase64URIFromImage( repoData.openGraphImageUrl, ); diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 72a565badad4b..fc6ba8a5eb528 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -61,7 +61,7 @@ const renderRepoCard = (repo, options = {}) => { isTemplate, starCount, forkCount, - openGraphImageUrl, + stringifiedRepoImage, } = repo; const { hide_border = false, @@ -143,7 +143,7 @@ const renderRepoCard = (repo, options = {}) => { height, border_radius, colors, - imageUrl: show_image ? openGraphImageUrl : "", + stringifiedRepoImage: show_image ? stringifiedRepoImage : "", }); card.disableAnimations(); diff --git a/src/common/Card.js b/src/common/Card.js index f1823b2e36e27..6850c8a7b2dfe 100644 --- a/src/common/Card.js +++ b/src/common/Card.js @@ -11,7 +11,7 @@ class Card { * @param {string?=} args.customTitle Card custom title. * @param {string?=} args.defaultTitle Card default title. * @param {string?=} args.titlePrefixIcon Card title prefix icon. - * @param {string?=} args.imageUrl Card preview image. + * @param {string?=} args.stringifiedRepoImage Card preview image. * @param {number?=} args.imageHeight Card preview image. * @param {object?=} args.colors Card colors arguments. * @param {string} args.colors.titleColor Card title color. @@ -29,7 +29,7 @@ class Card { customTitle, defaultTitle = "", titlePrefixIcon, - imageUrl = "", + stringifiedRepoImage = "", imageHeight = 200, }) { this.width = width; @@ -41,7 +41,7 @@ class Card { this.border_radius = border_radius; this.imageHeight = imageHeight; - this.imageUrl = imageUrl; + this.stringifiedRepoImage = stringifiedRepoImage; // returns theme based colors with proper overrides and defaults this.colors = colors; @@ -58,7 +58,7 @@ class Card { this.animations = true; this.a11yTitle = ""; this.a11yDesc = ""; - if (this.imageUrl) { + if (this.stringifiedRepoImage) { this.height += this.imageHeight; this.paddingY += this.imageHeight; } @@ -214,13 +214,13 @@ class Card { * @returns {string} Renders social preview image */ renderImage = () => { - if (!this.imageUrl) { + if (!this.stringifiedRepoImage) { return ""; } return ` + href="${this.stringifiedRepoImage}"> `; }; diff --git a/src/fetchers/types.d.ts b/src/fetchers/types.d.ts index e7775eea610cb..7d0c377b8d4ad 100644 --- a/src/fetchers/types.d.ts +++ b/src/fetchers/types.d.ts @@ -22,7 +22,7 @@ export type RepositoryData = { }; forkCount: number; starCount: number; - openGraphImageUrl: string; + stringifiedRepoImage: string; }; export type StatsData = { diff --git a/tests/renderRepoCard.test.js b/tests/renderRepoCard.test.js index 0c8e3ea42d15e..f1e045bf23f2f 100644 --- a/tests/renderRepoCard.test.js +++ b/tests/renderRepoCard.test.js @@ -18,8 +18,7 @@ const data_repo = { }, starCount: 38000, forkCount: 100, - openGraphImageUrl: - "https://repository-images.githubusercontent.com/266996769/1e4f2180-b194-11ea-9806-2395601f119b", + stringifiedRepoImage: "data:image/png;base64,base64/image/string", }, }; @@ -350,7 +349,7 @@ describe("Test renderRepoCard", () => { expect(queryByTestId(document.body, "card-image")).toBeInTheDocument(); expect( queryByTestId(document.body, "card-image").children[0], - ).toHaveAttribute("href", data_repo.repository.openGraphImageUrl); + ).toHaveAttribute("href", data_repo.repository.stringifiedRepoImage); }); it("should not render repo's social preview image by default", () => {