From 27c09e334ff2e7b7690c49331040064642a0c46e Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 29 Dec 2024 23:20:05 -0500 Subject: Refactoring --- extension/src/components/BMIcon.tsx | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 extension/src/components/BMIcon.tsx (limited to 'extension/src/components/BMIcon.tsx') diff --git a/extension/src/components/BMIcon.tsx b/extension/src/components/BMIcon.tsx new file mode 100644 index 0000000..e7c2a26 --- /dev/null +++ b/extension/src/components/BMIcon.tsx @@ -0,0 +1,103 @@ +import React, {SyntheticEvent, useEffect} from "react"; +import ColorThief from "colorthief"; +import {getBrowser} from "../main.tsx"; + +function BMIcon (props: {url: string, id: string}) { + + let [favicon, setFavicon] = React.useState(null); + let [iconMode, setIconMode] = React.useState<"large" | "small" | "letter">("letter"); + let [bgColor, setBgColor] = React.useState<[number, number, number] | null>(null) + let [bgColorPriority, setBgColorPriority] = React.useState(0); + + useEffect(() => { + faviconURL(props).then(r => { + if (r) { + setFavicon(r) + setIconMode("small"); + } + }) + }, []); + + function handleImageLoad(e: SyntheticEvent) { + if (e.currentTarget.naturalWidth >= 75 || favicon!.startsWith("data:image/svg+xml")) { + setIconMode("large") + } else if(bgColorPriority < 2) { + setBgColor(new ColorThief().getColor(e.currentTarget)) + setBgColorPriority(2); + } + } + + return ( +
+ {(() => { + switch (iconMode) { + case "letter": { + let url = new URL(props.url); + if (bgColorPriority < 1) { + setBgColor(hashStringToColor(url.hostname)); + setBgColorPriority(1); + } + return ({url.hostname.charAt(0)}) + } + case "small": { + return (Bookmark icon) + } + case "large": { + return (Bookmark icon) + } + } + })()} +
+ ) +} + +/** + * Gets the icon for a bookmark + * + * @param data The URL of the link + * @return The URL of the icon + */ +async function faviconURL(data: {url: string, id:string}) { + let i = (await getBrowser().storage.local.get("icon-cache-"+data.id))["icon-cache-"+data.id]; + if (i) return i + // if (i == null) return i; + + const url = new URL('https://www.google.com/s2/favicons'); + url.searchParams.set("sz", "256"); + url.searchParams.set("domain_url", data.url!); + let resp = await fetch(url) + let imgData = resp.ok ? await toDataURL(url.toString()) : null; + getBrowser().storage.local.set({["icon-cache-"+data.id]: imgData}); + return imgData; +} + +function toDataURL(url: string): string { + // @ts-ignore + return fetch(url) + .then(response => response.blob()) + .then(blob => new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onloadend = () => resolve(reader.result) + reader.onerror = reject + reader.readAsDataURL(blob) + })) +} + +function djb2(str: string){ + let hash = 5381; + for (var i = 0; i < str.length; i++) { + hash = ((hash << 5) + hash) + str.charCodeAt(i); /* hash * 33 + c */ + } + return hash; +} + +function hashStringToColor(str: string): [number, number, number] { + let hash = djb2(str); + let r = (hash & 0xFF0000) >> 16; + let g = (hash & 0x00FF00) >> 8; + let b = hash & 0x0000FF; + return [r, g, b]; +} + +export default BMIcon; \ No newline at end of file -- cgit v1.2.3