From afa44751b7f9d39c4842d5a91a9e3ce28d74bfce Mon Sep 17 00:00:00 2001
From: sowgro <tpoke.ferrari@gmail.com>
Date: Mon, 2 Dec 2024 23:07:26 -0500
Subject: a lot of improvements

---
 extension/src/components/Bookmark.tsx | 71 ++++++++++++++++++++++++++++-------
 1 file changed, 57 insertions(+), 14 deletions(-)

(limited to 'extension/src/components/Bookmark.tsx')

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
-- 
cgit v1.2.3

