aboutsummaryrefslogtreecommitdiff
path: root/extension/src/components/BMIcon.tsx
diff options
context:
space:
mode:
authorsowgro <tpoke.ferrari@gmail.com>2024-12-29 23:20:05 -0500
committersowgro <tpoke.ferrari@gmail.com>2024-12-29 23:20:05 -0500
commit27c09e334ff2e7b7690c49331040064642a0c46e (patch)
treeb7cc8914c9dfd790e4467a9c36d24c6f430631d8 /extension/src/components/BMIcon.tsx
parent514a33483697ff231c075cbb9130606af19d9298 (diff)
downloadbookmarks-home-drag-n-drop.tar.gz
bookmarks-home-drag-n-drop.tar.bz2
bookmarks-home-drag-n-drop.zip
Refactoringdrag-n-drop
Diffstat (limited to 'extension/src/components/BMIcon.tsx')
-rw-r--r--extension/src/components/BMIcon.tsx103
1 files changed, 103 insertions, 0 deletions
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<string | null>(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<HTMLImageElement, Event>) {
+ 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 (
+ <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.url);
+ if (bgColorPriority < 1) {
+ setBgColor(hashStringToColor(url.hostname));
+ setBgColorPriority(1);
+ }
+ 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>
+ )
+}
+
+/**
+ * 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