From afa44751b7f9d39c4842d5a91a9e3ce28d74bfce Mon Sep 17 00:00:00 2001 From: sowgro Date: Mon, 2 Dec 2024 23:07:26 -0500 Subject: a lot of improvements --- extension/src/components/Body.tsx | 17 ++- extension/src/components/Bookmark-backup.txt | 161 +++++++++++++++++++++++++++ extension/src/components/Bookmark.tsx | 71 +++++++++--- extension/src/components/FolderButton.tsx | 25 +++-- extension/src/components/SettingsEditor.tsx | 13 +-- 5 files changed, 250 insertions(+), 37 deletions(-) create mode 100644 extension/src/components/Bookmark-backup.txt (limited to 'extension/src/components') diff --git a/extension/src/components/Body.tsx b/extension/src/components/Body.tsx index 6498a0a..e575d89 100644 --- a/extension/src/components/Body.tsx +++ b/extension/src/components/Body.tsx @@ -1,6 +1,7 @@ import React, {useEffect, useState} from "react"; import SettingsEditor from "./SettingsEditor.tsx"; -import imageUrl from "../assets/settings.svg" +import SettingsIcon from "../assets/settings.svg?react"; +import EditIcon from "../assets/edit.svg?react"; import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; import FolderBody from "./FolderBody.tsx"; import {defaultSettings, ISettings, loadSettings, writeSettings} from "../Settings.ts"; @@ -33,7 +34,7 @@ function Body() { useEffect(() => { writeSettings(settings); - if (settings?.rootFolder) { + if (settings.rootFolder) { getBrowser().bookmarks.getSubTree(settings.rootFolder).then(t => { setSelectedBookmarkTree(t); }); @@ -51,9 +52,15 @@ function Body() { case "image": return () }})()} - +
+ {settings.editMode && Move mode: Drag bookmarks to change order} + + +
{selectedBookmarkTree[0] && ()} diff --git a/extension/src/components/Bookmark-backup.txt b/extension/src/components/Bookmark-backup.txt new file mode 100644 index 0000000..4d1eb60 --- /dev/null +++ b/extension/src/components/Bookmark-backup.txt @@ -0,0 +1,161 @@ +import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; +import React, {SyntheticEvent, useEffect} from "react"; +import {getBrowser} from "../main.tsx"; +import {Settings} from "./Body.tsx"; +import react from "@vitejs/plugin-react"; + +/** + * A component for a single bookmark + * + * @param props.data The BookmarkTreeNode with the data for the bookmark + */ +function Bookmark(props: {data: BookmarkTreeNode}) { + let [favicon, setFavicon] = React.useState(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) + // let [dropRight, setDropRight] = React.useState(false); + // let [dropLeft, setDropLeft] = React.useState(false); + // let [dropCenter, setDropCenter] = React.useState(false); + + useEffect(() => { + setBgColor([255, 0, 0]) + // faviconURL(props.data.url).then(o => o && setFavicon(o)) + faviconURL(props.data).then(r => { + if (r) { + setFavicon(r) + setIconMode("small"); + } + }) + }, []); + + // useEffect(() => { + // console.log("signal received", dropCenter) + // }, [dropCenter]); + + function handleImageLoad(e: SyntheticEvent) { + if (e.currentTarget.naturalWidth >= 75 || favicon!.startsWith("data:image/svg+xml")) { + setIconMode("large") + } + } + + return( +
+ { + // console.log("dragStart") + // e.dataTransfer.dropEffect = "move"; + // e.dataTransfer.setData("bm-id", props.data.id); + // }} + > +
+ {favicon ? ( + Bookmark icon + ) : ( + {new URL(props.data.url!).hostname.charAt(0)} + )} + +
+ {props.data.title} +
+ {/*
*/} + {/* setDropLeft(true)}*/} + {/* onDragLeave={_ => setDropRight(false)}*/} + {/* onDrop={e => getBrowser().bookmarks.move(e.dataTransfer.getData("bm-id"), {parentId: props.data.parentId, index: props.data.index})}*/} + {/* style={dropLeft ? undefined : {visibility: "hidden"}}*/} + {/* // hidden={!dropLeft}*/} + {/* >*/} + {/*
*/} + {/*
*/} + {/* setDropLeft(true)}*/} + {/* onDragLeave={_ => setDropRight(false)}*/} + {/* onDrop={e => getBrowser().bookmarks.move(e.dataTransfer.getData("bm-id"), {parentId: props.data.parentId, index: (props.data.index! + 1)})}*/} + {/* style={dropRight ? undefined : {visibility: "hidden"}}*/} + {/* // hidden={!dropRight}*/} + {/* >*/} + {/*
*/} + {/*
*/} + {/* {*/} + {/* e.preventDefault()*/} + {/* // console.log("dropped")*/} + {/* }}*/} + {/* onDragEnter={e => {*/} + {/* e.preventDefault()*/} + {/* setDropCenter(true)*/} + {/* console.log("enter")*/} + {/* }}*/} + {/* onDragLeave={_ => {*/} + {/* setDropCenter(false)*/} + {/* console.log("exit")*/} + {/* }}*/} + {/* onDrop={e => {*/} + {/* e.preventDefault();*/} + {/* console.log("dropped")*/} + {/* }}*/} + {/* style={dropCenter ? undefined : {visibility: "hidden"}}*/} + {/* // hidden={!dropCenter}*/} + {/* >*/} + {/* +*/} + {/*
*/} + {/*
*/} + + ); +} + +/** + * Gets the icon for a bookmark + * + * @param data The URL of the link + * @return The URL of the icon + */ +async function faviconURL(data: BookmarkTreeNode) { + let i = (await getBrowser().storage.local.get("icon-cache-"+data.id))["icon-cache-"+data.id]; + if (i) 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 hashColor(url:string) { +// hashCode(String) +// } +// +// function hashCode(str: string) { +// let hash = 0; +// for (let i = 0, len = str.length; i < len; i++) { +// let chr = str.charCodeAt(i); +// hash = (hash << 5) - hash + chr; +// hash |= 0; // Convert to 32bit integer +// } +// return hash; +// } + +export default Bookmark; \ No newline at end of file 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(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) { + if (e.currentTarget.naturalWidth >= 75 || favicon!.startsWith("data:image/svg+xml")) { + setIconMode("large") + } else if(!bgColor) { + setBgColor(new ColorThief().getColor(e.currentTarget)) + } + } + return( - -
- Bookmark icon setSmall(e.currentTarget.naturalWidth < 75 && !favicon.startsWith("data:image/svg+xml"))} - > -
- {props.data.title} -
+ ); } @@ -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 diff --git a/extension/src/components/FolderButton.tsx b/extension/src/components/FolderButton.tsx index 42e5d60..2b59d56 100644 --- a/extension/src/components/FolderButton.tsx +++ b/extension/src/components/FolderButton.tsx @@ -1,8 +1,9 @@ import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; import FolderBody from "./FolderBody.tsx"; -import folderIcon from "../assets/folder.svg" -import folderIconOpen from "../assets/folder_open.svg" +import FolderIcon from "../assets/folder.svg?react" +import FolderIconOpen from "../assets/folder_open.svg?react" import {useState} from "react"; +import bookmark from "./Bookmark.tsx"; /** * A component for the button used to open a bookmark folder. @@ -15,16 +16,18 @@ function FolderButton(props: {data: BookmarkTreeNode}) { return( <> - setFolderOpen(!folderOpen)}> -
- Folder icon -
- {props.data.title} -
+ {folderOpen - && props.data.children - && props.data.children.length > 0 - && ()} + && props.data.children + && props.data.children.length > 0 + && ()} ); } diff --git a/extension/src/components/SettingsEditor.tsx b/extension/src/components/SettingsEditor.tsx index edd99f9..d11158e 100644 --- a/extension/src/components/SettingsEditor.tsx +++ b/extension/src/components/SettingsEditor.tsx @@ -1,8 +1,9 @@ import RadioButtonGroup from "./RadioButtonGroup.tsx"; import React, {useContext} from "react"; -import imageUrl from "../assets/close.svg" +import CloseIcon from "../assets/close.svg?react" import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; import {Settings} from "./Body.tsx"; +import {getBrowser} from "../main.tsx"; /** * A component for the settings sidebar @@ -17,7 +18,7 @@ function SettingsEditor(props: {tree: BookmarkTreeNode[], isOpen: [boolean, Rea return (

Settings

@@ -28,7 +29,6 @@ function SettingsEditor(props: {tree: BookmarkTreeNode[], isOpen: [boolean, Rea }}> -
@@ -79,10 +79,9 @@ function SettingsEditor(props: {tree: BookmarkTreeNode[], isOpen: [boolean, Rea )} - {/*
*/} - {/*sort: {settings.sort}*/} - {/*rootFolder: {settings.rootFolder}*/} - {/*bgmode: {settings.backgroundMode}*/} +

Icon Cache

+ +
) } -- cgit v1.2.3