Skip to content

Commit

Permalink
fix: hashnode embeds (#885)
Browse files Browse the repository at this point in the history
  • Loading branch information
scissorsneedfoodtoo authored May 13, 2024
1 parent 82275ec commit 8578321
Show file tree
Hide file tree
Showing 9 changed files with 497 additions and 21 deletions.
37 changes: 37 additions & 0 deletions cypress/fixtures/mock-hashnode-posts.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,43 @@
"id": "65dc2b7cbb4eb0cd565b4463",
"posts": {
"edges": [
{
"node": {
"id": "66334bb9c18427d98514c9c1",
"slug": "hashnode-embed-test",
"title": "Hashnode Embed Test",
"author": {
"id": "5e135008490269cb3022acbf",
"username": "scissorsneedfoodtoo",
"name": "Kristofer Koishigawa",
"bio": {
"text": ""
},
"profilePicture": "https://cdn.hashnode.com/res/hashnode/image/upload/v1710857138258/qSuNPwWGp.jpeg",
"socialMediaLinks": {
"website": "https://kriskoishigawa.com",
"twitter": "https://twitter.com/kriskoishigawa",
"facebook": ""
},
"location": ""
},
"tags": [
{
"id": "56744723958ef13879b9549b",
"name": "Testing",
"slug": "testing"
}
],
"coverImage": null,
"brief": "YouTube\nhttps://www.youtube.com/watch?v=KZe0C0Qq4p0\n \nVimeo\nhttps://vimeo.com/137080554\n \nCodePen\nhttps://codepen.io/freeCodeCamp/pen/VPaoNP\n \nCodeSandbox\nhttps://codesandbox.io/s/react-new?fontsize=14&hidenavigation=1&theme=dark\n \nSeems to work with...",
"readTimeInMinutes": 1,
"content": {
"html": "<h2 id=\"heading-youtube\">YouTube</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://www.youtube.com/watch?v=KZe0C0Qq4p0\">https://www.youtube.com/watch?v=KZe0C0Qq4p0</a></div>\n<p> </p>\n<h2 id=\"heading-vimeo\">Vimeo</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://vimeo.com/137080554\">https://vimeo.com/137080554</a></div>\n<p> </p>\n<h2 id=\"heading-codepen\">CodePen</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://codepen.io/freeCodeCamp/pen/VPaoNP\">https://codepen.io/freeCodeCamp/pen/VPaoNP</a></div>\n<p> </p>\n<h2 id=\"heading-codesandbox\">CodeSandbox</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://codesandbox.io/s/react-new?fontsize=14&amp;hidenavigation=1&amp;theme=dark\">https://codesandbox.io/s/react-new?fontsize=14&amp;hidenavigation=1&amp;theme=dark</a></div>\n<p> </p>\n<p>Seems to work with older versions of Sandbox links (<a target=\"_blank\" href=\"https://codesandbox.io/s/react-new?fontsize=14&amp;hidenavigation=1&amp;theme=dark\">https://codesandbox.io/s/</a>*) and not newer versions (<a target=\"_blank\" href=\"https://codesandbox.io/p/sandbox/react-new?file=%2Fsrc%2FApp.js&amp;fontsize=14&amp;hidenavigation=1&amp;theme=dark\">https://codesandbox.io/p/sandbox/</a>*).</p>\n<p>However, adding an <code>iframe</code> directly to an HTML block works.</p>\n<h2 id=\"heading-twitter\">Twitter</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://twitter.com/freeCodeCamp/status/1780642881054609864\">https://twitter.com/freeCodeCamp/status/1780642881054609864</a></div>\n<p> </p>\n<h2 id=\"heading-gist\">Gist</h2>\n<div class=\"gist-block embed-wrapper\" data-gist-show-loading=\"false\" data-id=\"539dbbd01ebfd36fd8a671124d290f5a\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a href=\"https://gist.github.com/scissorsneedfoodtoo/539dbbd01ebfd36fd8a671124d290f5a\" class=\"embed-card\">https://gist.github.com/scissorsneedfoodtoo/539dbbd01ebfd36fd8a671124d290f5a</a></div><p> </p>\n<h2 id=\"heading-glitch\">Glitch</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://glitch.com/embed/#!/glitter-magenta-machine?path=server.js%3A1%3A0\">https://glitch.com/embed/#!/glitter-magenta-machine?path=server.js%3A1%3A0</a></div>\n<p> </p>\n<h2 id=\"heading-soundcloud\">SoundCloud</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://soundcloud.com/l2shareost38/say-sue-me-so-tender-nevertheless-ost-part-8\">https://soundcloud.com/l2shareost38/say-sue-me-so-tender-nevertheless-ost-part-8</a></div>\n<p> </p>\n<h2 id=\"heading-anchor\">Anchor</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://anchor.fm/bravenotperfect/episodes/The-Bravery-To-Ask-For-Help-ft--Soledad-OBrien-eiq25m\">https://anchor.fm/bravenotperfect/episodes/The-Bravery-To-Ask-For-Help-ft--Soledad-OBrien-eiq25m</a></div>\n<p> </p>\n<h2 id=\"heading-spotify\">Spotify</h2>\n<p>Track:</p>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://open.spotify.com/track/6Va86Ypwk0VV657Wk9sRFO\">https://open.spotify.com/track/6Va86Ypwk0VV657Wk9sRFO</a></div>\n<p> </p>\n<p>Playlist:</p>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://open.spotify.com/playlist/37i9dQZF1DWWQRwui0ExPn\">https://open.spotify.com/playlist/37i9dQZF1DWWQRwui0ExPn</a></div>\n<p> </p>\n<p>Podcast:</p>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://open.spotify.com/show/7CpuEnbCLIXwI6LEcbBOYP\">https://open.spotify.com/show/7CpuEnbCLIXwI6LEcbBOYP</a></div>\n<p> </p>\n<h2 id=\"heading-giphy\">Giphy</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://giphy.com/gifs/computer-cat-wearing-glasses-VbnUQpnihPSIgIXuZv\">https://giphy.com/gifs/computer-cat-wearing-glasses-VbnUQpnihPSIgIXuZv</a></div>\n<p> </p>\n<h2 id=\"heading-runkit\">Runkit</h2>\n<div class=\"embed-wrapper\"><div class=\"embed-loading\"><div class=\"loadingRow\"></div><div class=\"loadingRow\"></div></div><a class=\"embed-card\" href=\"https://runkit.com/bolajiayodeji/5f6fa7407d6a3d001a9f605e\">https://runkit.com/bolajiayodeji/5f6fa7407d6a3d001a9f605e</a></div>\n<p> </p>\n<h2 id=\"heading-iframe-in-html-block\">iframe in HTML Block</h2>\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/N1pYdEAU9mk?si=BapivyRfMfD99MTc\"></iframe>"
},
"publishedAt": "2024-05-02T08:15:53.976Z",
"updatedAt": null
}
},
{
"node": {
"id": "662bc86c57b91227386cc393",
Expand Down
45 changes: 35 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"build": "cross-env ELEVENTY_ENV=prod NODE_OPTIONS=--max-old-space-size=8192 eleventy",
"build:ci": "cross-env ELEVENTY_ENV=ci eleventy",
"postbuild": "node ./config/i18n/generate-serve-config.js",
"test": "jest",
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"lint": "npm-run-all lint:*",
"lint:code": "eslint . --ext .js --cache",
"lint:i18n-schema": "node ./config/i18n/schema-validation.js",
Expand Down Expand Up @@ -80,6 +80,7 @@
"eslint-plugin-cypress": "2.15.2",
"eslint-plugin-ghost": "3.4.0",
"eslint-utils": "3.0.0",
"get-video-id": "4.1.5",
"graceful-fs": "4.2.11",
"graphql-request": "^6.1.0",
"husky": "9.0.11",
Expand Down
17 changes: 17 additions & 0 deletions src/_includes/assets/css/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ on every page through _includes/layouts/default.njk
14. under-header-content donation
15. Ads
16. RTL layout
17. Hashnode
*/

Expand Down Expand Up @@ -2434,3 +2435,19 @@ a.banner:hover span {
direction: ltr;
unicode-bidi: isolate;
}

/* 17. Hashnode
/* ---------------------------------------------------------- */

.post-full-content .embed-wrapper {
/* margin: 1.5em 0 3em; */
margin-top: 1.25em;
margin-bottom: 1.25em;
}

.embed-wrapper {
display: flex;
justify-content: center;
width: 100%;
overflow-x: hidden;
}
8 changes: 5 additions & 3 deletions utils/ghost/process-batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const processBatch = async ({ batch, type, currBatchNo, totalBatches }) => {
obj.primary_author = removeUnusedKeys(obj.primary_author);
obj.tags.map(tag => removeUnusedKeys(tag));

// Set the source of the publication for tracking and later processing
obj.source = 'Ghost';

// Set a default feature image for posts if one doesn't exist
if (type === 'posts' && !obj.feature_image)
obj.feature_image =
Expand Down Expand Up @@ -150,11 +153,10 @@ const processBatch = async ({ batch, type, currBatchNo, totalBatches }) => {
// Also, append Google ads to post body and generate bottom banner ad if ads are enabled.
obj.html = await modifyHTMLContent({
postContent: obj.html,
postTitle: obj.title
postTitle: obj.title,
source: obj.source
});

obj.source = 'Ghost';

return obj;
})
);
Expand Down
8 changes: 5 additions & 3 deletions utils/hashnode/process-batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const processBatch = async ({ batch, currBatchNo, totalBatches }) => {
newPost.title = oldPost.title;
newPost.reading_time = oldPost.readTimeInMinutes;

// Set the source of the publication for tracking and later processing
newPost.source = 'Hashnode';

// Set a default feature image for posts if one doesn't exist
// Note: We're not handling pages from Hashnode, so there's no
// need to handle cases where we may not want to have a cover image
Expand Down Expand Up @@ -78,7 +81,8 @@ const processBatch = async ({ batch, currBatchNo, totalBatches }) => {
newPost.path = `/${oldPost.slug}/`;
newPost.html = await modifyHTMLContent({
postContent: oldPost.content.html,
postTitle: newPost.title
postTitle: newPost.title,
source: newPost.source
});

// Note: Longer posts include an ellipsis. We can decide how to
Expand All @@ -93,8 +97,6 @@ const processBatch = async ({ batch, currBatchNo, totalBatches }) => {
.join(' ');
}

newPost.source = 'Hashnode';

newBatch.push(newPost);
}

Expand Down
42 changes: 38 additions & 4 deletions utils/modify-html-content.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const translate = require('./translate');
const { setDefaultAlt } = require('./modify-html-helpers');
const {
generateHashnodeEmbedMarkup,
setDefaultAlt
} = require('./modify-html-helpers');
const getImageDimensions = require('./get-image-dimensions');
const fitVids = require('./fitvids');

const modifyHTMLContent = async ({ postContent, postTitle }) => {
const modifyHTMLContent = async ({ postContent, postTitle, source }) => {
const dom = new JSDOM(postContent);
const window = dom.window;
const document = window.document;
const hashnodeEmbedAnchorEls = [
...document.querySelectorAll('div.embed-wrapper a.embed-card')
];

await Promise.all(
hashnodeEmbedAnchorEls.map(async anchorEl => {
const embedWrapper = anchorEl.parentElement;
const embedURL = anchorEl.href;
const embedMarkup = await generateHashnodeEmbedMarkup(embedURL);

if (embedMarkup) {
embedWrapper.innerHTML = embedMarkup;
}
})
);

const embeds = [...document.getElementsByTagName('embed')];
const images = [...document.getElementsByTagName('img')];
const iframes = [...document.getElementsByTagName('iframe')];

if (embeds.length || iframes.length) fitVids(window);
if (source === 'Ghost' && (embeds.length || iframes.length)) fitVids(window);

await Promise.all(
images.map(async image => {
Expand All @@ -30,7 +49,22 @@ const modifyHTMLContent = async ({ postContent, postTitle }) => {
}),

iframes.map(async iframe => {
iframe.setAttribute('title', `${translate('embed-title')}`);
if (!iframe.title) iframe.setAttribute('title', translate('embed-title'));
// For iframes on Hashnode that were copy and pasted into an HTML block,
// wrap them in a div similar to how Hashnode does for links in embed blocks
if (
source === 'Hashnode' &&
!['embed-wrapper', 'giphy-wrapper'].some(className =>
iframe?.parentElement?.classList.contains(className)
)
) {
const embedWrapper = document.createElement('div');

embedWrapper.classList.add('embed-wrapper');
iframe.parentElement.replaceChild(embedWrapper, iframe);
embedWrapper.appendChild(iframe);
}

iframe.setAttribute('loading', 'lazy');
})
);
Expand Down
Loading

0 comments on commit 8578321

Please sign in to comment.