mirror of
https://github.com/gnoxnahte/obsidian-auto-embed.git
synced 2026-07-22 09:50:24 +00:00
Cache the size for embeds that have dynamic sizes (Imgur, Reddit, TikTok, Twitter/X)
Hopefully it should improve [Cumulative Layout Shift](https://web.dev/articles/cls) for embeds that were loaded previously. Also fixes https://github.com/GnoxNahte/obsidian-auto-embed/issues/4#issuecomment-2226989583 which was caused by TikTok not sending the resize message sometimes, especially if the time between each request is very short.
This commit is contained in:
parent
7c29dd1398
commit
ca12faa75d
6 changed files with 76 additions and 29 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import AutoEmbedPlugin from "src/main";
|
||||
import { Dictionary, Size } from "src/utility";
|
||||
|
||||
export class BaseEmbedData {
|
||||
embedSource: EmbedBase;
|
||||
|
|
@ -26,9 +27,14 @@ export abstract class EmbedBase {
|
|||
// If the website doesn't need / doesn't send the message, it'll be undefined.
|
||||
readonly embedOrigin?: string;
|
||||
readonly plugin: AutoEmbedPlugin;
|
||||
// Cache the height of the embed.
|
||||
// Correctly sizes the embeds that it has seen before as soon as the embed is loaded. Reducing Cumulative Layout Shift.
|
||||
// Helps also when some websites don't send the resize message for some reason.
|
||||
sizeCache: Dictionary<Size>;
|
||||
|
||||
constructor(plugin: AutoEmbedPlugin) {
|
||||
this.plugin = plugin;
|
||||
this.sizeCache = {};
|
||||
}
|
||||
|
||||
abstract createEmbed(link: string, embedData?: BaseEmbedData): HTMLElement;
|
||||
|
|
@ -88,7 +94,7 @@ export abstract class EmbedBase {
|
|||
|
||||
// To have a embed source respond to the resize event:
|
||||
// - Set EmbedBase.embedOrigin (e.g. embedOrigin = "https://platform.twitter.com")
|
||||
// - Set body of resize method here (onResizeMessage)
|
||||
// - Implement body of resize method here (onResizeMessage)
|
||||
onResizeMessage?(e: MessageEvent):void;
|
||||
|
||||
onErrorCreatingEmbed(msg?: string): HTMLElement {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,15 @@ export class ImgurEmbed extends EmbedBase {
|
|||
|
||||
iframe.src = `https://imgur.com/a/${imgurId}/embed?pub=true`;
|
||||
|
||||
iframe.classList.add(this.autoEmbedCssClass, "imgur-embed", "imgur-" + imgurId);
|
||||
iframe.classList.add(this.autoEmbedCssClass, "imgur-embed");
|
||||
iframe.dataset.imgurId = imgurId;
|
||||
if (this.sizeCache[imgurId] && this.sizeCache[imgurId].height) {
|
||||
iframe.style.height = this.sizeCache[imgurId].height + "px";
|
||||
|
||||
// Optional
|
||||
if (this.sizeCache[imgurId].width)
|
||||
iframe.style.width = this.sizeCache[imgurId].width + "px";
|
||||
}
|
||||
|
||||
iframe.setAttribute("scrolling", "no");
|
||||
|
||||
|
|
@ -34,10 +42,10 @@ export class ImgurEmbed extends EmbedBase {
|
|||
return;
|
||||
|
||||
const imgurId = regexMatch[1];
|
||||
// Why use class instead of id for getting the reference:
|
||||
// Why use querySelectorAll instead of querySelector for getting the reference:
|
||||
// There might be multiple iframes, some in Reading mode and Live preview.
|
||||
// Some might even duplicates if they user has duplicates
|
||||
const iframes = document.getElementsByClassName("imgur-" + imgurId);
|
||||
const iframes = document.querySelectorAll(`.imgur-embed[data-imgur-id="${imgurId}"`);
|
||||
|
||||
if (iframes.length === 0)
|
||||
return;
|
||||
|
|
@ -48,6 +56,8 @@ export class ImgurEmbed extends EmbedBase {
|
|||
if (data.message === "resize_imgur") {
|
||||
iframe.height = data.height + "px";
|
||||
iframe.width = data.width + "px";
|
||||
|
||||
this.sizeCache[imgurId] = { width: data.width, height: data.height };
|
||||
}
|
||||
// ===== NOTE =====
|
||||
// Imgur has different embed urls for "albums" and "posts"
|
||||
|
|
|
|||
|
|
@ -11,19 +11,19 @@ export class RedditEmbed extends EmbedBase {
|
|||
if (regexMatch === null)
|
||||
return this.onErrorCreatingEmbed();
|
||||
|
||||
const postId = url.match(/(?:\/comments\/)(\w+)/) as RegExpMatchArray;
|
||||
if (!postId)
|
||||
const postIdRegexResult = url.match(/(?:\/comments\/)(\w+)/) as RegExpMatchArray;
|
||||
if (!postIdRegexResult)
|
||||
{
|
||||
return this.onErrorCreatingEmbed();
|
||||
}
|
||||
const postId = postIdRegexResult[1];
|
||||
|
||||
// Creating the iframe
|
||||
const iframe = createEl("iframe");
|
||||
|
||||
iframe.classList.add(this.autoEmbedCssClass, "reddit-embed", "reddit-" + postId[1]);
|
||||
|
||||
url = url.replace("www.reddit.com", "reddit.com"); // Remove "www"
|
||||
url = url.replace("reddit.com", "embed.reddit.com"); // Add embed subdomain
|
||||
url = url.replace("www.reddit.com", "embed.reddit.com"); // Remove "www"
|
||||
|
||||
if (this.plugin.settings.darkMode)
|
||||
{
|
||||
|
|
@ -32,20 +32,14 @@ export class RedditEmbed extends EmbedBase {
|
|||
}
|
||||
|
||||
iframe.src = url;
|
||||
iframe.setAttribute("scrolling", "no");
|
||||
iframe.setAttribute("allowfullscreen", "");
|
||||
|
||||
// TODO: Dynamically set iframe height:
|
||||
// Methods:
|
||||
// - Listen to reddit's postmessage, reddit sends out a postmessage containing:
|
||||
// {
|
||||
// type: "resize.embed"
|
||||
// data: [height value]
|
||||
// }
|
||||
// But it doesn't send the post id, so I'm not sure which iframe to set it to.
|
||||
// - Get height from reddit api? Not sure how to do
|
||||
// - Get content type from reddit api? Main heights are:
|
||||
// - short text: 240px
|
||||
// - long text (has a show more dropdown): 316px
|
||||
// - picture / video: 739px
|
||||
// console.log("Cache: " + JSON.stringify(this.sizeCache))
|
||||
iframe.dataset.redditPostId = postId;
|
||||
if (this.sizeCache[postId] && this.sizeCache[postId].height) {
|
||||
iframe.style.height = this.sizeCache[postId].height + "px";
|
||||
}
|
||||
|
||||
// iframe.style.height="unset";
|
||||
|
||||
|
|
@ -72,7 +66,13 @@ export class RedditEmbed extends EmbedBase {
|
|||
// Check where the message came from
|
||||
if (iframe.contentWindow == e.source)
|
||||
{
|
||||
iframe.style.height = data.data + "px";
|
||||
const height = data.data;
|
||||
iframe.style.height = height + "px";
|
||||
|
||||
const postId = iframe.dataset.redditPostId;
|
||||
if (postId)
|
||||
this.sizeCache[postId] = { width: 0, height: height};
|
||||
|
||||
break;
|
||||
}
|
||||
// console.log("Height: " + iframe.style.height);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { EmbedBase } from "./embedBase";
|
|||
export class TikTokEmbed extends EmbedBase {
|
||||
name = "TikTok";
|
||||
embedOrigin = "https://www.tiktok.com"
|
||||
regex = new RegExp(/https:\/\/www\.tiktok\.com\/\@([\w\.]+)\/video\/(\d+)/);
|
||||
regex = new RegExp(/https:\/\/www\.tiktok\.com\/@([\w.]+)\/video\/(\d+)/);
|
||||
|
||||
createEmbed(url: string): HTMLElement {
|
||||
const regexMatch = url.match(this.regex);
|
||||
|
|
@ -19,8 +19,13 @@ export class TikTokEmbed extends EmbedBase {
|
|||
iframe.src = `https://www.tiktok.com/embed/v2/${tiktokId}/`;
|
||||
|
||||
iframe.classList.add(this.autoEmbedCssClass, "tiktok-embed");
|
||||
// iframe.dataset.tiktokId = tiktokId;
|
||||
iframe.setAttribute("allowfullscreen", "");
|
||||
|
||||
// console.log("Cache: " + JSON.stringify(this.sizeCache))
|
||||
iframe.dataset.tiktokId = tiktokId;
|
||||
if (this.sizeCache[tiktokId] && this.sizeCache[tiktokId].height) {
|
||||
iframe.style.height = this.sizeCache[tiktokId].height + "px";
|
||||
}
|
||||
|
||||
return iframe;
|
||||
}
|
||||
|
|
@ -58,6 +63,11 @@ export class TikTokEmbed extends EmbedBase {
|
|||
if (iframe.contentWindow == e.source)
|
||||
{
|
||||
iframe.style.height = data.height + "px";
|
||||
|
||||
const tiktokId = iframe.dataset.tiktokId;
|
||||
if (tiktokId)
|
||||
this.sizeCache[tiktokId] = { width: 0, height: height};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,17 @@ export class TwitterEmbed extends EmbedBase {
|
|||
url = `https://platform.twitter.com/embed/Tweet.html?dnt=true&theme=${this.plugin.settings.darkMode ? "dark" : "light"}&id=${postId}`;
|
||||
iframe.src = url;
|
||||
|
||||
iframe.classList.add(this.autoEmbedCssClass, "twitter-embed", "twitter-" + postId);
|
||||
iframe.classList.add(this.autoEmbedCssClass, "twitter-embed");
|
||||
|
||||
iframe.sandbox.add("allow-forms", "allow-presentation", "allow-same-origin", "allow-scripts", "allow-modals", "allow-popups");
|
||||
iframe.setAttribute("scrolling", "no");
|
||||
// iframe.setAttribute("allowfullscreen", "");
|
||||
|
||||
iframe.dataset.twitterPostId = postId;
|
||||
if (this.sizeCache[postId] && this.sizeCache[postId].height) {
|
||||
iframe.style.height = this.sizeCache[postId].height + "px";
|
||||
}
|
||||
|
||||
return iframe;
|
||||
}
|
||||
|
||||
|
|
@ -46,17 +51,20 @@ export class TwitterEmbed extends EmbedBase {
|
|||
}
|
||||
*/
|
||||
|
||||
// console.log("Twitter Message: " + JSON.stringify(e.data["twttr.embed"]))
|
||||
|
||||
// Only continue if the method is for resizing
|
||||
if (e.data["twttr.embed"]["method"] !== "twttr.private.resize")
|
||||
return;
|
||||
|
||||
const params = e.data["twttr.embed"]["params"][0];
|
||||
const postId = params["data"]["tweet_id"];
|
||||
// console.log("Tweet-id: " + params["data"]["tweet_id"]);
|
||||
|
||||
// Why use class instead of id for getting the reference:
|
||||
// Why use querySelectorAll instead of querySelector for getting the reference:
|
||||
// There might be multiple iframes, some in Reading mode and Live preview.
|
||||
// Some might even duplicates if they user has duplicates
|
||||
const iframes = document.getElementsByClassName("twitter-" + params["data"]["tweet_id"]);
|
||||
// console.log("Tweet-id: " + params["data"]["tweet_id"]);
|
||||
const iframes = document.querySelectorAll(`.twitter-embed[data-twitter-post-id="${postId}"]`);
|
||||
|
||||
if (iframes.length === 0)
|
||||
return;
|
||||
|
|
@ -64,8 +72,12 @@ export class TwitterEmbed extends EmbedBase {
|
|||
for (let i = 0; i < iframes.length; ++i) {
|
||||
const iframe = iframes[i] as HTMLIFrameElement;
|
||||
|
||||
iframe.style.height = ((params["height"] as number) + 1) + "px";
|
||||
const height = (params["height"] as number) + 1;
|
||||
// iframe.style.width = ((params["width"] as number) + 1) + "px";
|
||||
iframe.style.height = height + "px";
|
||||
|
||||
if (postId)
|
||||
this.sizeCache[postId] = { width: 0, height: height};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,13 @@ export function isURL(str: string) : boolean {
|
|||
|
||||
export function isLinkToImage(url: string) : boolean {
|
||||
return /\.(jpg|jpeg|png|webp|avif|gif)$/.test(url);
|
||||
}
|
||||
|
||||
export interface Dictionary<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
export interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
Loading…
Reference in a new issue