diff --git a/src/svelte/app/components/grid-card.svelte b/src/svelte/app/components/grid-card.svelte index 6330ed5..57321f2 100644 --- a/src/svelte/app/components/grid-card.svelte +++ b/src/svelte/app/components/grid-card.svelte @@ -5,7 +5,7 @@ import store from "../../shared/services/store"; import Wrap from "src/svelte/shared/components/wrap.svelte"; import Stack from "src/svelte/shared/components/stack.svelte"; - import { afterUpdate, createEventDispatcher, onMount } from "svelte"; + import { createEventDispatcher, onMount } from "svelte"; import { HOVER_LINK_SOURCE_ID } from "src/constants"; import EventManager from "src/event/event-manager"; import Icon from "src/svelte/shared/components/icon.svelte"; @@ -13,7 +13,6 @@ import { PluginEvent } from "src/event/types"; import { openContextMenu } from "../services/context-menu"; import { openInCurrentTab } from "../services/open-file"; - import Flex from "src/svelte/shared/components/flex.svelte"; import { getDomainFromUrl } from "../services/utils/url-utils"; import Spacer from "src/svelte/shared/components/spacer.svelte"; import Divider from "src/svelte/shared/components/divider.svelte"; @@ -25,6 +24,11 @@ } from "../services/social-media-image-cache"; import { CoverImageFit } from "src/types"; + type SocialMediaImageResult = { + status: "SUCCESS" | "NOT_FOUND" | "EXPIRED" | "NO_IMAGE"; + url?: string; // Only present when status is 'SUCCESS' + }; + export let displayName: string; export let path: string; export let baseName: string; @@ -40,9 +44,8 @@ let plugin: VaultExplorerPlugin; let enableFileIcons: boolean = false; let loadSocialMediaImage: boolean = true; - let imgSrc: string | null; - - let isCoverImageLoaded = false; + let imgSrc: string | null = null; + let isImageLoaded = false; store.plugin.subscribe((p) => { plugin = p; @@ -52,25 +55,10 @@ const dispatch = createEventDispatcher(); - let renderKey = 0; - - onMount(() => { - updateImgSrc(); - }); - - afterUpdate(() => { - if (imageUrl === null) { - imgSrc = null; - } else { - updateImgSrc(); - } - }); - onMount(() => { function handleLoadSocialMediaImageChange() { const newValue = plugin.settings.views.grid.loadSocialMediaImage; loadSocialMediaImage = newValue; - renderKey++; } EventManager.getInstance().on( @@ -102,29 +90,6 @@ }; }); - async function updateImgSrc() { - if (imageUrl !== null) { - const entry = await getSocialMediaImageEntry(imageUrl); - if (entry) { - const isExpired = await isSocialMediaImageEntryExpired(entry); - if (!isExpired) { - const socialUrl = entry.socialMediaImageUrl; - - //The url is null when the social media image does not exist - //This will always happen for sites like Twitter (x.com) - //To avoid fetching the same non-existent image, we will set imgSrc to null - if (socialUrl === null) { - imgSrc = null; - } else { - imgSrc = socialUrl; - } - return; - } - } - imgSrc = imageUrl; - } - } - function handleUrlClick(e: Event) { e.stopPropagation(); } @@ -164,29 +129,71 @@ handleCardClick(); } - $: hasFooterContent = - tags != null || custom1 != null || custom2 != null || custom3 != null; + function handleImageLoad() { + isImageLoaded = true; + } async function handleImageError(event: Event) { const target = event.target as HTMLImageElement; target.onerror = null; // Prevent infinite loop - if (!imgSrc) return; + let websiteUrl = target.src; + if (websiteUrl.endsWith("/")) { + websiteUrl = websiteUrl.slice(0, -1); // Remove the trailing slash + } if (loadSocialMediaImage) { - const socialUrl = await fetchSocialMediaImage(imgSrc); + const socialUrl = await fetchSocialMediaImage(websiteUrl); if (socialUrl) { - putSocialMediaImageUrl(imgSrc, socialUrl); + await putSocialMediaImageUrl(websiteUrl, socialUrl); target.src = socialUrl; } else { - putSocialMediaImageUrl(imgSrc, null); + await putSocialMediaImageUrl(websiteUrl, null); } } } - function handleImageLoad() { - isCoverImageLoaded = true; + async function getCachedSocialMediaImageUrl( + websiteUrl: string, + ): Promise { + const entry = await getSocialMediaImageEntry(websiteUrl); + + if (entry) { + const { socialMediaImageUrl } = entry; + + if (socialMediaImageUrl) { + const isExpired = await isSocialMediaImageEntryExpired(entry); + if (!isExpired) { + return { status: "SUCCESS", url: socialMediaImageUrl }; + } else { + return { status: "EXPIRED" }; // Image found but expired + } + } else { + return { status: "NO_IMAGE" }; // Social image was fetched but doesn't exist + } + } + + return { status: "NOT_FOUND" }; // Image not cached } + + $: if (imageUrl) { + isImageLoaded = false; + getCachedSocialMediaImageUrl(imageUrl).then((result) => { + const { status, url } = result; + if (status === "SUCCESS") { + imgSrc = url!; + } else if (status === "EXPIRED" || status === "NOT_FOUND") { + imgSrc = imageUrl; + } else if (status === "NO_IMAGE") { + //Do nothing + //This is for websites like x.com where the social image is not found + //We don't want to keep trying to fetch the image + } + }); + } + + $: hasFooterContent = + tags != null || custom1 != null || custom2 != null || custom3 != null;
- {#each [renderKey] as k (k)} - {#if imgSrc !== null} - - - {/if} - {/each} + {#if imgSrc !== null} + + + {/if} {#if imageUrl === null}
{/if} -
{ const ogImage = getMetaTagContent(document, "og:image"); const twitterImage = getMetaTagContent(document, "twitter:image"); - // const maskIcon = getLinkTagContent(document, "mask-icon"); - // const appleTouchIcon = getLinkTagContent(document, "apple-touch-icon"); let imageUrl = ogImage || twitterImage; - // || appleTouchIcon || maskIcon; if (imageUrl) { //Handle edge case where social media image URL has slashes at the beginning @@ -84,11 +81,6 @@ export const fetchSocialMediaImage = async (url: string) => { } }; -// const getLinkTagContent = (document: Document, property: string) => { -// const tag = document.querySelector(`link[rel='${property}']`); -// return tag ? tag.getAttribute("href") : null; -// }; - const getMetaTagContent = (document: Document, property: string) => { const tag = document.querySelector(`meta[property='${property}']`) || diff --git a/src/svelte/app/services/social-media-image-cache.ts b/src/svelte/app/services/social-media-image-cache.ts index 72e44c7..b00cf15 100644 --- a/src/svelte/app/services/social-media-image-cache.ts +++ b/src/svelte/app/services/social-media-image-cache.ts @@ -45,7 +45,7 @@ export const putSocialMediaImageUrl = async ( socialMediaImageUrl: string | null ) => { const db = await openDatabase(); - db.put(STORE_NAME, { + await db.put(STORE_NAME, { url, socialMediaImageUrl, timestamp: Date.now(), @@ -76,7 +76,7 @@ export const clearSocialMediaImageCache = async () => { } }; -const openDatabase = (): Promise> => { +const openDatabase = () => { return openDB(DATABASE_NAME, 1, { upgrade(db) { db.createObjectStore(STORE_NAME, { keyPath: "url" });