diff --git a/bun.lockb b/bun.lockb index a45f0b28..5306e1ca 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/manifest.json b/manifest.json index 9ca026fc..bd9620f6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "vault-explorer", "name": "Vault Explorer", - "version": "1.27.2", + "version": "1.28.0", "minAppVersion": "1.4.13", "description": "Explore your vault in visual format", "author": "DecafDev", diff --git a/package.json b/package.json index 39a709be..416f73d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-vault-explorer", - "version": "1.27.2", + "version": "1.28.0", "description": "Explore your vault in visual format", "main": "main.js", "scripts": { @@ -36,6 +36,7 @@ "typescript": "4.7.4" }, "dependencies": { + "idb": "^8.0.0", "js-logger": "^1.6.1", "lodash": "^4.17.21", "nanoid": "^5.0.7", diff --git a/src/svelte/app/components/grid-card.svelte b/src/svelte/app/components/grid-card.svelte index 5af8c12b..18b6f4ba 100644 --- a/src/svelte/app/components/grid-card.svelte +++ b/src/svelte/app/components/grid-card.svelte @@ -13,7 +13,7 @@ import Icon from "src/svelte/shared/components/icon.svelte"; import { getIconIdForFile } from "../services/file-icon"; import License from "src/svelte/shared/services/license"; - import { fetchSocialMediaImage } from "../services/social-media-image"; + import { fetchSocialImage } from "../services/social-media-image"; import { PluginEvent } from "src/event/types"; import GridCardContainer from "./grid-card-container.svelte"; import GridCardTitle from "./grid-card-title.svelte"; @@ -144,7 +144,7 @@ if (!loadSocialMediaImage) return; if (imageUrl === null && url !== null) { - imgSrc = await fetchSocialMediaImage(url); + imgSrc = await fetchSocialImage(url); } } diff --git a/src/svelte/app/services/social-media-image.ts b/src/svelte/app/services/social-media-image.ts index b543c962..a63cf899 100644 --- a/src/svelte/app/services/social-media-image.ts +++ b/src/svelte/app/services/social-media-image.ts @@ -1,7 +1,33 @@ +import { DBSchema, IDBPDatabase, openDB } from "idb"; import Logger from "js-logger"; import { requestUrl } from "obsidian"; -export const fetchSocialMediaImage = async (url: string) => { +const DATABASE_NAME = "vaultexplorer"; +const STORE_NAME = "socialMediaImage"; +const ENTRY_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 7; //1 week + +interface SocialImageDB extends DBSchema { + socialMediaImage: { + key: string; + value: { + url: string; + socialImageUrl: string; + timestamp: number; + }; + }; +} + +export const clearSocialImageCache = async () => { + Logger.trace({ + fileName: "social-media-image.ts", + functionName: "clearSocialMediaImageCache", + message: "called", + }); + const db = await openDatabase(); + await db.clear(STORE_NAME); +}; + +export const fetchSocialImage = async (url: string) => { Logger.trace({ fileName: "social-media-image.ts", functionName: "fetchSocialMediaImage", @@ -9,6 +35,28 @@ export const fetchSocialMediaImage = async (url: string) => { }); try { + const entry = await getCachedSocialImageEntry(url); + if (entry !== null) { + Logger.trace( + { + fileName: "social-media-image.ts", + functionName: "fetchSocialMediaImage", + message: "found cached entry", + }, + entry + ); + if (Date.now() - entry.timestamp < ENTRY_EXPIRATION_TIME) { + const { socialImageUrl } = entry; + Logger.debug({ + fileName: "social-media-image.ts", + functionName: "fetchSocialMediaImage", + message: + "timestamp is within expiration time. returning cached image url", + }); + return socialImageUrl; + } + } + const response = await requestUrl({ url, method: "GET", @@ -32,6 +80,7 @@ export const fetchSocialMediaImage = async (url: string) => { }, { imageUrl } ); + await putSocialImageUrl(url, imageUrl); } else { Logger.warn( { @@ -51,7 +100,7 @@ export const fetchSocialMediaImage = async (url: string) => { functionName: "fetchSocialMediaImage", message: "failed to fetch", }, - { url, error } + error ); return null; } @@ -63,3 +112,22 @@ const getMetaTagContent = (document: Document, property: string) => { document.querySelector(`meta[name='${property}']`); return tag ? tag.getAttribute("content") : ""; }; + +const putSocialImageUrl = async (url: string, socialImageUrl: string) => { + const db = await openDatabase(); + db.put(STORE_NAME, { url, socialImageUrl, timestamp: Date.now() }); +}; + +const getCachedSocialImageEntry = async (url: string) => { + const db = await openDatabase(); + const cachedEntry = await db.get(STORE_NAME, url); + return cachedEntry ?? null; +}; + +const openDatabase = (): Promise> => { + return openDB(DATABASE_NAME, 1, { + upgrade(db) { + db.createObjectStore(STORE_NAME, { keyPath: "url" }); + }, + }); +}; diff --git a/versions.json b/versions.json index fcb8469d..71014605 100644 --- a/versions.json +++ b/versions.json @@ -101,5 +101,6 @@ "1.26.3": "1.4.13", "1.27.0": "1.4.13", "1.27.1": "1.4.13", - "1.27.2": "1.4.13" + "1.27.2": "1.4.13", + "1.28.0": "1.4.13" }