From 03b7ccaa5c152c8d7ed73374be8ad4d4d034845b Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 7 Nov 2024 12:40:42 -0500 Subject: Implement sorting and refactor --- extension/src/Body.tsx | 56 -------------- extension/src/Bookmark.tsx | 24 ------ extension/src/FolderBody.tsx | 20 ----- extension/src/FolderButton.tsx | 25 ------ extension/src/RadioButtonGroup.tsx | 36 --------- extension/src/Settings.ts | 2 +- extension/src/SettingsEditor.tsx | 94 ----------------------- extension/src/components/Body.tsx | 60 +++++++++++++++ extension/src/components/Bookmark.tsx | 34 +++++++++ extension/src/components/FolderBody.tsx | 72 ++++++++++++++++++ extension/src/components/FolderButton.tsx | 30 ++++++++ extension/src/components/RadioButtonGroup.tsx | 37 +++++++++ extension/src/components/SettingsEditor.tsx | 105 ++++++++++++++++++++++++++ extension/src/index.css | 2 + extension/src/main.tsx | 2 +- extension/tsconfig.app.tsbuildinfo | 2 +- 16 files changed, 343 insertions(+), 258 deletions(-) delete mode 100644 extension/src/Body.tsx delete mode 100644 extension/src/Bookmark.tsx delete mode 100644 extension/src/FolderBody.tsx delete mode 100644 extension/src/FolderButton.tsx delete mode 100644 extension/src/RadioButtonGroup.tsx delete mode 100644 extension/src/SettingsEditor.tsx create mode 100644 extension/src/components/Body.tsx create mode 100644 extension/src/components/Bookmark.tsx create mode 100644 extension/src/components/FolderBody.tsx create mode 100644 extension/src/components/FolderButton.tsx create mode 100644 extension/src/components/RadioButtonGroup.tsx create mode 100644 extension/src/components/SettingsEditor.tsx diff --git a/extension/src/Body.tsx b/extension/src/Body.tsx deleted file mode 100644 index 2a46a67..0000000 --- a/extension/src/Body.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React, {useEffect, useState} from "react"; -import SettingsEditor from "./SettingsEditor.tsx"; -import imageUrl from "./assets/settings.svg" -import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; -import FolderBody from "./FolderBody.tsx"; -import {defaultSettings, ISettings, loadSettings, writeSettings} from "./Settings.ts"; -import {getBrowser} from "./main.tsx"; - -export const Settings = - React.createContext<[ISettings, (arg0: ISettings) => void]>([ - defaultSettings, - () => {} -]); - -function Body() { - const [settingsOpen, setSettingsOpen] = useState(false); - const [settings, setSettings] = useState(defaultSettings); - const [bookmarkTree, setBookmarkTree] = useState([]) - const [ogBookmarkTree, setOgBookmarkTree] = useState([]) - useEffect(() => { - loadSettings().then(r => { - setSettings(r); - }) - getBrowser().bookmarks.getTree().then(t => { - setOgBookmarkTree(t); - }) - }, []) - useEffect(() => { - writeSettings(settings); - if (settings?.rootFolder) { - getBrowser().bookmarks.getSubTree(settings.rootFolder).then(t => { - setBookmarkTree(t); - }); - } else { - getBrowser().bookmarks.getTree().then(t => { - setBookmarkTree(t); - }) - } - }, [settings]); - - return ( - - {(() => {switch (settings.backgroundMode) { - case "color": return () - case "image": return () - }})()} - - - {bookmarkTree[0] && ()} - - ) -} - -export default Body \ No newline at end of file diff --git a/extension/src/Bookmark.tsx b/extension/src/Bookmark.tsx deleted file mode 100644 index 64c2811..0000000 --- a/extension/src/Bookmark.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; - -function Bookmark(props: {data: BookmarkTreeNode}) { - - return( - - Bookmark icon - {props.data.title} - - ); -} - -function faviconURL(u: string | undefined) { - if (!u) return ""; - u = new URL(u).hostname.toString(); - const url = new URL('https://www.google.com/s2/favicons'); - url.searchParams.set("sz", "256"); - // u = u.split(".")[u.split(".").length-2] +"."+ u.split(".")[u.split(".").length-1] - url.searchParams.set("domain_url", u); - return url.toString(); - -} - -export default Bookmark; \ No newline at end of file diff --git a/extension/src/FolderBody.tsx b/extension/src/FolderBody.tsx deleted file mode 100644 index b013d97..0000000 --- a/extension/src/FolderBody.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; -import Bookmark from "./Bookmark.tsx"; -import FolderButton from "./FolderButton.tsx"; - -function FolderBody (props: {data: BookmarkTreeNode}) { - return ( -
- { - props.data.children && - props.data.children.map(child => - child.children - ? - : - ) - } -
- ) -} - -export default FolderBody; \ No newline at end of file diff --git a/extension/src/FolderButton.tsx b/extension/src/FolderButton.tsx deleted file mode 100644 index e74cdbf..0000000 --- a/extension/src/FolderButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; -import FolderBody from "./FolderBody.tsx"; -import folderIcon from "./assets/folder.svg" -import folderIconOpen from "./assets/folder_open.svg" -import {useState} from "react"; - -function FolderButton(props: {data: BookmarkTreeNode}) { - - const [folderOpen, setFolderOpen] = useState(false); - - return( - <> - setFolderOpen(!folderOpen)}> - Folder icon - {props.data.title} - - { folderOpen - && props.data.children - && props.data.children.length > 0 - && ()} - -); -} - -export default FolderButton \ No newline at end of file diff --git a/extension/src/RadioButtonGroup.tsx b/extension/src/RadioButtonGroup.tsx deleted file mode 100644 index da1b521..0000000 --- a/extension/src/RadioButtonGroup.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, {ReactElement, useEffect, useId, useState} from "react"; - -interface RadioProps { - children: ReactElement[], - value: any, - onChange?: (arg0: any) => void -} - -function RadioButtonGroup(props: RadioProps) { - const [selected, setSelected] = useState(props.value); - useEffect(() => { - setSelected(props.value); - }, [props.value]); - useEffect(() => { - props.onChange && props.onChange(selected); - }, [selected]) - - return ( -
- { props.children.map((item) => ( - - )) } -
- ) -} - -export default RadioButtonGroup \ No newline at end of file diff --git a/extension/src/Settings.ts b/extension/src/Settings.ts index 2f2c8e1..2bf490b 100644 --- a/extension/src/Settings.ts +++ b/extension/src/Settings.ts @@ -1,7 +1,7 @@ import {getBrowser} from "./main.tsx"; export interface ISettings { - sort: "from-bookmarks" | "alphabetical" | "frequency" | "recent" + sort: "from-bookmarks" | "alphabetical" | "recent" foldersFirst: boolean backgroundMode: "theme" | "color" | "image" backgroundColor: string diff --git a/extension/src/SettingsEditor.tsx b/extension/src/SettingsEditor.tsx deleted file mode 100644 index ca0feeb..0000000 --- a/extension/src/SettingsEditor.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import RadioButtonGroup from "./RadioButtonGroup.tsx"; -import React, {useContext} from "react"; -import imageUrl from "./assets/close.svg" -import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; -import {Settings} from "./Body.tsx"; - -function SettingsEditor(props: {tree: BookmarkTreeNode[], isOpen: [boolean, React.Dispatch>]}) { - const [settings, setSettings] = useContext(Settings) - const [open, setOpen] = props.isOpen; - - return ( -
- -

Settings

- -

Sort

- { - setSettings({...settings, sort: e}) - }}> - - - - - -
- - -

Background Type

- setSettings({...settings, backgroundMode: e})}> - - - - - - {(() => { - switch (settings.backgroundMode) { - case "image": - return (<> -

Background Image URL

- setSettings({...settings, backgroundImage: e.target.value})}/> - ) - case "color": - return (<> -

Background Color

- setSettings({...settings, backgroundColor: e.target.value})}/> - ) - } - })()} - -

Root folder

- - - {/*
*/} - {/*sort: {settings.sort}*/} - {/*rootFolder: {settings.rootFolder}*/} - {/*bgmode: {settings.backgroundMode}*/} -
-) -} - -function getFoldersFromTree(tree: BookmarkTreeNode[]) { - let folderList: BookmarkTreeNode[] = []; - rec(tree); - - function rec(tree: BookmarkTreeNode[]) { - tree.forEach(item => { - if (item.children) { - folderList.push(item); - rec(item.children); - } - }) - } - return folderList; -} - -export default SettingsEditor; \ No newline at end of file diff --git a/extension/src/components/Body.tsx b/extension/src/components/Body.tsx new file mode 100644 index 0000000..193e68e --- /dev/null +++ b/extension/src/components/Body.tsx @@ -0,0 +1,60 @@ +import React, {useEffect, useState} from "react"; +import SettingsEditor from "./SettingsEditor.tsx"; +import imageUrl from "../assets/settings.svg" +import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; +import FolderBody from "./FolderBody.tsx"; +import {defaultSettings, ISettings, loadSettings, writeSettings} from "../Settings.ts"; +import {getBrowser} from "../main.tsx"; + +export const Settings = + React.createContext<[ISettings, (arg0: ISettings) => void]>([ + defaultSettings, + () => {} +]); + +/** + * A component for the full body of the application + * Also stores the trees and settings + */ +function Body() { + const [settingsOpen, setSettingsOpen] = useState(false); + const [settings, setSettings] = useState(defaultSettings); + const [bookmarkTree, setBookmarkTree] = useState([]) + const [ogBookmarkTree, setOgBookmarkTree] = useState([]) + useEffect(() => { + loadSettings().then(r => { + setSettings(r); + }) + getBrowser().bookmarks.getTree().then(t => { + setOgBookmarkTree(t); + }) + }, []) + useEffect(() => { + writeSettings(settings); + if (settings?.rootFolder) { + getBrowser().bookmarks.getSubTree(settings.rootFolder).then(t => { + setBookmarkTree(t); + }); + } else { + getBrowser().bookmarks.getTree().then(t => { + setBookmarkTree(t); + }) + } + }, [settings]); + + return ( + + {(() => {switch (settings.backgroundMode) { + case "color": return () + case "image": return () + }})()} + + + {bookmarkTree[0] && ()} + + ) +} + +export default Body \ No newline at end of file diff --git a/extension/src/components/Bookmark.tsx b/extension/src/components/Bookmark.tsx new file mode 100644 index 0000000..75badd7 --- /dev/null +++ b/extension/src/components/Bookmark.tsx @@ -0,0 +1,34 @@ +import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; + +/** + * A component for a single bookmark + * + * @param props.data The BookmarkTreeNode with the data for the bookmark + */ +function Bookmark(props: {data: BookmarkTreeNode}) { + return( + + Bookmark icon + {props.data.title} + + ); +} + +/** + * Gets the icon for a bookmark + * + * @param u The URL of the link + * @return The URL of the icon + */ +function faviconURL(u: string | undefined) { + if (!u) return ""; + u = new URL(u).hostname.toString(); + const url = new URL('https://www.google.com/s2/favicons'); + url.searchParams.set("sz", "256"); + // u = u.split(".")[u.split(".").length-2] +"."+ u.split(".")[u.split(".").length-1] + url.searchParams.set("domain_url", u); + return url.toString(); + +} + +export default Bookmark; \ No newline at end of file diff --git a/extension/src/components/FolderBody.tsx b/extension/src/components/FolderBody.tsx new file mode 100644 index 0000000..a573a6e --- /dev/null +++ b/extension/src/components/FolderBody.tsx @@ -0,0 +1,72 @@ +import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; +import Bookmark from "./Bookmark.tsx"; +import FolderButton from "./FolderButton.tsx"; +import {useContext} from "react"; +import {Settings} from "./Body.tsx"; + +/** + * A component that displays the contents of a bookmark folder + * + * @param props.data The BookmarkTreeNode with data for the folder + * @constructor + */ +function FolderBody (props: {data: BookmarkTreeNode}) { + const [settings, _] = useContext(Settings) + + if (!props.data.children) return; + + let content = [...props.data.children].sort(getSortFunction(settings.sort)) + if (settings.foldersFirst) { + let [bookmarks, folders] = separateFolders(content) + content = folders.concat(bookmarks) + } + + return ( +
+ {content.map(child => + child.children + ? + : + )} +
+ ) +} + +/** + * Gets the correct sort function based on the sort setting + * @param sort The sort setting state + * @return The corresponding sort function + */ +function getSortFunction(sort: "from-bookmarks" | "alphabetical" | "recent"): ((a:BookmarkTreeNode, b:BookmarkTreeNode) => number) | undefined { + switch (sort) { + case "alphabetical": return (a, b) => { + return a.title.localeCompare(b.title); + } + case "recent": return (a, b) => { + // @ts-ignore + return a.dateLastUsed - b.dateLastUsed + } + } +} + +/** + * Separate the folders and the bookmarks into two separate lists + * @param content THe bookmark list + * @returns tuple in the format [bookmarks, folders] + */ +function separateFolders(content: BookmarkTreeNode[]) { + let bookmarks = []; + let folders = []; + for (let bookmarkTreeNode of content) { + if (bookmarkTreeNode.children) { + folders.push(bookmarkTreeNode) + } else { + bookmarks.push(bookmarkTreeNode) + } + } + return [bookmarks, folders] +} + + + +export default FolderBody; \ No newline at end of file diff --git a/extension/src/components/FolderButton.tsx b/extension/src/components/FolderButton.tsx new file mode 100644 index 0000000..2d7cc9e --- /dev/null +++ b/extension/src/components/FolderButton.tsx @@ -0,0 +1,30 @@ +import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; +import FolderBody from "./FolderBody.tsx"; +import folderIcon from "../assets/folder.svg" +import folderIconOpen from "../assets/folder_open.svg" +import {useState} from "react"; + +/** + * A component for the button used to open a bookmark folder. + * This is themed the same as a bookmark + * + * @param props.data The BookmarkTreeNode containing the data of the folder + */ +function FolderButton(props: {data: BookmarkTreeNode}) { + const [folderOpen, setFolderOpen] = useState(false); + + return( + <> + setFolderOpen(!folderOpen)}> + Folder icon + {props.data.title} + + { folderOpen + && props.data.children + && props.data.children.length > 0 + && ()} + +); +} + +export default FolderButton \ No newline at end of file diff --git a/extension/src/components/RadioButtonGroup.tsx b/extension/src/components/RadioButtonGroup.tsx new file mode 100644 index 0000000..297800d --- /dev/null +++ b/extension/src/components/RadioButtonGroup.tsx @@ -0,0 +1,37 @@ +import React, {ReactElement, useEffect, useId, useState} from "react"; + +/** + * A component for a group of radio buttons where only one can be selected + * + * @param props.children html