1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
import BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode;
import React, {useContext, useEffect, useState} from "react";
import {getBrowser} from "../main.tsx";
import {ActiveDrag, ActiveEdit, Settings} from "./Body.tsx";
import DropTargets from "./DropTargets.tsx";
import ContextMenu from "./ContextMenu.tsx";
import BMIcon from "./BMIcon.tsx";
/**
* A component for a single bookmark
*/
function Bookmark(props: {id: string}) {
let [settings] = React.useContext(Settings);
let [activeDrag, setActiveDrag] = React.useContext(ActiveDrag);
const [, setActiveEdit] = useContext(ActiveEdit)
const [bmData, setBmData] = useState<BookmarkTreeNode | undefined>()
useEffect(() => {
getBrowser().bookmarks.get(props.id).then(r => {
setBmData(r[0]);
})
getBrowser().bookmarks.onChanged.addListener((id: string) => {
if (id !== props.id) return;
getBrowser().bookmarks.get(props.id).then(r => {
setBmData(r[0]);
})
})
}, []);
if (!bmData) return;
// Dragging
const handleDrag = () => {
setActiveDrag(bmData);
};
const handleDragEnd = () => {
setActiveDrag(null);
};
// Dropping
const handleDropLeft = () => {
getBrowser().bookmarks.move(activeDrag!.id, {
parentId: bmData.parentId,
index: bmData.index
})
location.reload()
};
const handleDropRight = () => {
getBrowser().bookmarks.move(activeDrag!.id, {
parentId: bmData.parentId,
index: (bmData.index! + 1)
})
location.reload();
};
const handleDropCenter = () => {
chrome.bookmarks.create({
parentId: bmData.parentId,
index: bmData.index,
title: "New Folder"
}).then(r => {
getBrowser().bookmarks.move(bmData.id, {parentId: r.id});
getBrowser().bookmarks.move(activeDrag!.id, {parentId: r.id});
location.reload()
})
};
// actions
const handleDelete = () => {
getBrowser().bookmarks.remove(bmData.id);
location.reload();
};
const handleEdit = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
setActiveEdit(null);
setActiveEdit(bmData);
};
return(
<div className={"bookmark"}>
<a href={bmData.url} draggable={settings.editMode} onDrag={handleDrag} onDragEnd={handleDragEnd}>
<IconPre id={bmData.id} bmUrl={bmData.url!}/>
<span>{bmData.title}</span>
</a>
{settings.editMode && <ContextMenu onEdit={handleEdit} onDelete={handleDelete}/>}
{activeDrag && activeDrag !== bmData &&
<DropTargets onDropLeft={handleDropLeft} onDropRight={handleDropRight} onDropCenter={handleDropCenter}/>}
</div>
);
}
export default Bookmark;
function IconPre(props: {bmUrl: string, id:string}) {
const [data, setData] = useState()
useEffect(() => {
findIcon(props.bmUrl, props.id).then(r => {
setData(r);
})
}, []);
// if (!data) return;
return <BMIcon bmUrl={props.bmUrl} imgSrc={data}/>
}
/**
* 1. check for icon cache
* 2. check if the user selected an icon
* 3. use the best icon from the available icons
* 4. search googles icon database
*/
async function findIcon(bmUrl: string, id:string) {
let cachedIcon = (await getBrowser().storage.local.get("icon-cache-"+id))["icon-cache-"+id];
if (cachedIcon) return cachedIcon
// let selectedUrl: string[] = (await getBrowser().storage.local.get("icon-aval-"+data.id))["icon-aval-"+data.id]
// if (selectedUrl.length > 0) {
// await getBrowser().storage.local.set({["icon-cache-"+data.id]: selectedUrl[0]});
// return selectedUrl[0];
// }
// const url = new URL('https://www.google.com/s2/favicons');
// url.searchParams.set("sz", "256");
// url.searchParams.set("domain_url", bmUrl);
// let resp = await fetch(url)
// let imgData = resp.ok ? await toDataURL(url.toString()) : null;
// getBrowser().storage.local.set({["icon-cache-"+bmUrl]: 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)
}))
}
|