diff options
Diffstat (limited to 'extension/src/components/Bookmark.tsx')
-rw-r--r-- | extension/src/components/Bookmark.tsx | 71 |
1 files changed, 57 insertions, 14 deletions
diff --git a/extension/src/components/Bookmark.tsx b/extension/src/components/Bookmark.tsx index 839a9f5..d1c1756 100644 --- a/extension/src/components/Bookmark.tsx +++ b/extension/src/components/Bookmark.tsx @@ -1,7 +1,9 @@ import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; -import GlobeIcon from "../assets/globe.svg" import React, {SyntheticEvent, useEffect} from "react"; import {getBrowser} from "../main.tsx"; +import {Settings} from "./Body.tsx"; +import ColorThief from 'colorthief' +import react from "@vitejs/plugin-react"; /** * A component for a single bookmark @@ -9,27 +11,51 @@ import {getBrowser} from "../main.tsx"; * @param props.data The BookmarkTreeNode with the data for the bookmark */ function Bookmark(props: {data: BookmarkTreeNode}) { - let [favicon, setFavicon] = React.useState(GlobeIcon); - let [isSmall, setSmall] = React.useState(true); + let [favicon, setFavicon] = React.useState<string | null>(null); + let [iconMode, setIconMode] = React.useState<"large" | "small" | "letter">("letter"); + let [settings, setSettings] = React.useContext(Settings); + let [bgColor, setBgColor] = React.useState<[number, number, number] | null>(null) + useEffect(() => { - // faviconURL(props.data.url).then(o => o && setFavicon(o)) faviconURL(props.data).then(r => { if (r) { setFavicon(r) + setIconMode("small"); } }) }, []); + function handleImageLoad(e: SyntheticEvent<HTMLImageElement, Event>) { + if (e.currentTarget.naturalWidth >= 75 || favicon!.startsWith("data:image/svg+xml")) { + setIconMode("large") + } else if(!bgColor) { + setBgColor(new ColorThief().getColor(e.currentTarget)) + } + } + return( - <a className="bookmark draggable" href={props.data.url}> - <div className={"icon-box" + (isSmall ? " small" : "")}> - <img alt="Bookmark icon" - src={favicon} - onLoad={(e) => setSmall(e.currentTarget.naturalWidth < 75 && !favicon.startsWith("data:image/svg+xml"))} - ></img> - </div> - <span>{props.data.title}</span> - </a> + <div className={"bookmark"}> + <a draggable={settings.editMode} href={props.data.url}> + <div className={"icon-box " + (iconMode)} style={bgColor ? {"--icon-bg": `rgba(${bgColor[0]}, ${bgColor[1]}, ${bgColor[2]}, 0.2)`} as React.CSSProperties : undefined}> + {(() => { switch (iconMode) { + case "letter": { + let url = new URL(props.data.url!); + if (!bgColor) { + setBgColor(hashStringToColor(url.hostname)); + } + return (<span className={"letter"}>{url.hostname.charAt(0)}</span>) + } + case "small": { + return (<img alt="Bookmark icon" src={favicon!} onLoad={handleImageLoad}/>) + } + case "large": { + return(<img alt="Bookmark icon" src={favicon!}/>) + } + }})()} + </div> + <span>{props.data.title}</span> + </a> + </div> ); } @@ -42,12 +68,13 @@ function Bookmark(props: {data: BookmarkTreeNode}) { async function faviconURL(data: BookmarkTreeNode) { 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()) : GlobeIcon; + let imgData = resp.ok ? await toDataURL(url.toString()) : null; getBrowser().storage.local.set({["icon-cache-"+data.id]: imgData}); return imgData; } @@ -64,4 +91,20 @@ function toDataURL(url:string):string { })) } +function djb2(str: string){ + var 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] { + var hash = djb2(str); + var r = (hash & 0xFF0000) >> 16; + var g = (hash & 0x00FF00) >> 8; + var b = hash & 0x0000FF; + return [r, g, b]; +} + export default Bookmark;
\ No newline at end of file |