Skip to content

Commit 8859ce5

Browse files
committed
Simplify logic and fix prettier errors
1 parent d8bcc1c commit 8859ce5

File tree

1 file changed

+70
-101
lines changed

1 file changed

+70
-101
lines changed

src/components/Browser.tsx

Lines changed: 70 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
import { ChevronLeft, ChevronRight, Home, Lock, Maximize2, Menu, MoreVertical, Plus, RotateCw, Star, X } from "lucide-react";
22
import { useEffect, useMemo, useRef, useState } from "react";
3-
import { actionBarClass, addressInputClass, classNames, closeButtonClass, encodeProxyUrl, formatUrl, getActualUrl, getDefaultUrl, iconButtonClass, type Tab, tabButtonClass } from "@/lib/tabs";
3+
import { actionBarClass, addressInputClass, classNames, closeButtonClass, encodeProxyUrl, formatUrl, getActualUrl, getDefaultUrl, type Tab, tabButtonClass } from "@/lib/tabs";
4+
5+
const IconButton = ({ onClick, icon: Icon, className = "", disabled = false, title = "" }: { onClick?: () => void; icon: React.ComponentType<{ className?: string }>; className?: string; disabled?: boolean; title?: string }) => (
6+
<button
7+
type="button"
8+
onClick={onClick}
9+
disabled={disabled}
10+
title={title}
11+
className={`group p-2 rounded-full text-text-secondary transition-all duration-200
12+
hover:bg-interactive hover:text-text disabled:opacity-50 disabled:cursor-not-allowed active:scale-95 ${className}`}
13+
>
14+
<Icon className="h-4 w-4 stroke-[2.5px] group-hover:stroke-text" />
15+
</button>
16+
);
417

518
export default function Browser() {
619
const [tabs, setTabs] = useState<Tab[]>([{ id: 1, title: "Tab 1", url: "about:blank", active: true, reloadKey: 0 }]);
@@ -14,7 +27,7 @@ export default function Browser() {
1427
let firstTabUrl = getDefaultUrl();
1528
try {
1629
const goUrl = sessionStorage.getItem("goUrl");
17-
if (goUrl && goUrl.trim()) {
30+
if (goUrl?.trim()) {
1831
firstTabUrl = goUrl;
1932
}
2033
} catch (error) {
@@ -46,7 +59,7 @@ export default function Browser() {
4659
const iframe = iframeRefs.current[activeTab.id];
4760
if (!iframe) return;
4861

49-
const updateUrlBar = () => {
62+
const updateState = () => {
5063
const actualUrl = getActualUrl(iframe);
5164
if (actualUrl && actualUrl !== url) {
5265
setUrl(actualUrl);
@@ -69,74 +82,56 @@ export default function Browser() {
6982
const urlObj = new URL(actualUrl);
7083
const defaultFavicon = `${urlObj.origin}/favicon.ico`;
7184
setFavicons((prev) => ({ ...prev, [activeTab.id]: defaultFavicon }));
72-
} catch (e) {}
85+
} catch (_e) {}
7386
}
7487
}
75-
} catch (e) {}
88+
} catch (_e) {}
7689
};
7790

78-
iframe.addEventListener("load", updateUrlBar);
79-
80-
const interval = setInterval(updateUrlBar, 1000);
91+
iframe.addEventListener("load", updateState);
92+
const interval = setInterval(updateState, 1000);
8193

8294
return () => {
83-
iframe.removeEventListener("load", updateUrlBar);
95+
iframe.removeEventListener("load", updateState);
8496
clearInterval(interval);
8597
};
8698
}, [activeTab, url]);
8799

88100
const setActiveTab = (id: number) => {
89-
const target = tabs.find((tab) => tab.id === id);
90-
if (!target) return;
91-
setTabs((prev) =>
92-
prev.map((tab) => ({
93-
...tab,
94-
active: tab.id === id,
95-
})),
96-
);
97-
98-
const iframe = iframeRefs.current[id];
99-
const actualUrl = getActualUrl(iframe);
100-
setUrl(actualUrl || target.url);
101+
setTabs((prev) => prev.map((tab) => ({ ...tab, active: tab.id === id })));
101102
};
102103

103104
const addNewTab = () => {
104-
setTabs((prev) => {
105-
const nextId = prev.length ? Math.max(...prev.map((tab) => tab.id)) + 1 : 1;
106-
const newTabs = prev.map((tab) => ({ ...tab, active: false }));
107-
return [...newTabs, { id: nextId, title: `Tab ${nextId}`, url: getDefaultUrl(), active: true, reloadKey: 0 }];
108-
});
109-
setUrl(getDefaultUrl());
105+
const newId = tabs.length ? Math.max(...tabs.map((tab) => tab.id)) + 1 : 1;
106+
const defaultUrl = getDefaultUrl();
107+
108+
setTabs((prev) => [...prev.map((tab) => ({ ...tab, active: false })), { id: newId, title: `Tab ${newId}`, url: defaultUrl, active: true, reloadKey: 0 }]);
109+
setUrl(defaultUrl);
110110
};
111111

112112
const closeTab = (id: number) => {
113113
setTabs((prev) => {
114-
let nextUrl = url;
115-
const filtered = prev.filter((tab) => tab.id !== id);
114+
const remaining = prev.filter((tab) => tab.id !== id);
116115

117-
if (filtered.length === 0) {
116+
if (remaining.length === 0) {
118117
let firstTabUrl = getDefaultUrl();
119118
try {
120119
const goUrl = sessionStorage.getItem("goUrl");
121-
if (goUrl && goUrl.trim()) {
120+
if (goUrl?.trim()) {
122121
firstTabUrl = goUrl;
123122
}
124123
} catch (error) {
125124
console.warn("Session storage access failed:", error);
126125
}
127126
return [{ id: Date.now(), title: "Tab 1", url: firstTabUrl, active: true, reloadKey: 0 }];
128-
} else if (!filtered.some((tab) => tab.active)) {
129-
filtered[0] = { ...filtered[0], active: true };
130-
nextUrl = filtered[0].url;
131-
} else {
132-
const currentActive = filtered.find((tab) => tab.active);
133-
if (currentActive) {
134-
nextUrl = currentActive.url;
135-
}
136127
}
137128

138-
setUrl(nextUrl);
139-
return filtered;
129+
if (prev.find((tab) => tab.id === id)?.active) {
130+
remaining[remaining.length - 1].active = true;
131+
setUrl(remaining[remaining.length - 1].url);
132+
}
133+
134+
return remaining;
140135
});
141136
};
142137

@@ -158,36 +153,38 @@ export default function Browser() {
158153
setUrl(formattedUrl);
159154
};
160155

161-
const goHome = () => {
162-
window.location.href = "/";
163-
};
164-
165-
const removeBookmark = (index: number) => {
156+
const removeBookmark = (bookmarkUrl: string, bookmarkTitle: string) => {
166157
try {
167-
const updatedBookmarks = bookmarks.filter((_, i) => i !== index);
158+
const updatedBookmarks = bookmarks.filter((b) => !(b.url === bookmarkUrl && b.Title === bookmarkTitle));
168159
setBookmarks(updatedBookmarks);
169160
localStorage.setItem("bookmarks", JSON.stringify(updatedBookmarks));
170161
} catch (e) {
171162
console.error("Failed to remove bookmark:", e);
172163
}
173164
};
174165

175-
const goBack = () => {
166+
const Action = (action: "back" | "forward" | "reload" | "home") => {
176167
if (!activeTab) return;
177168
const iframe = iframeRefs.current[activeTab.id];
178-
iframe?.contentWindow?.history.back();
179-
};
180169

181-
const goForward = () => {
182-
if (!activeTab) return;
183-
const iframe = iframeRefs.current[activeTab.id];
184-
iframe?.contentWindow?.history.forward();
185-
};
170+
if (action === "home") {
171+
window.location.href = "/";
172+
return;
173+
}
186174

187-
const reloadTab = () => {
188-
if (!activeTab) return;
189-
const iframe = iframeRefs.current[activeTab.id];
190-
iframe?.contentWindow?.location.reload();
175+
if (!iframe?.contentWindow) return;
176+
177+
switch (action) {
178+
case "back":
179+
iframe.contentWindow.history.back();
180+
break;
181+
case "forward":
182+
iframe.contentWindow.history.forward();
183+
break;
184+
case "reload":
185+
iframe.contentWindow.location.reload();
186+
break;
187+
}
191188
};
192189

193190
const toggleFullscreen = () => {
@@ -215,7 +212,7 @@ export default function Browser() {
215212
try {
216213
const urlObj = new URL(actualUrl);
217214
faviconUrl = `${urlObj.origin}/favicon.ico`;
218-
} catch (e) {
215+
} catch (_e) {
219216
faviconUrl = "";
220217
}
221218
}
@@ -237,19 +234,7 @@ export default function Browser() {
237234
<div className="flex h-screen flex-col bg-background">
238235
<div className="flex items-center gap-1 bg-background px-3 pt-1.5 pb-0">
239236
{tabs.map((tab) => (
240-
<div
241-
key={tab.id}
242-
role="button"
243-
tabIndex={0}
244-
onClick={() => setActiveTab(tab.id)}
245-
onKeyDown={(event) => {
246-
if (event.key === "Enter" || event.key === " ") {
247-
event.preventDefault();
248-
setActiveTab(tab.id);
249-
}
250-
}}
251-
className={classNames(tabButtonClass, tab.active ? "bg-background-secondary text-text shadow-sm" : "bg-background text-text-secondary hover:bg-interactive")}
252-
>
237+
<button key={tab.id} type="button" onClick={() => setActiveTab(tab.id)} className={classNames(tabButtonClass, tab.active ? "bg-background-secondary text-text shadow-sm" : "bg-background text-text-secondary hover:bg-interactive")}>
253238
<div className="flex min-w-0 flex-1 items-center gap-2">
254239
{favicons[tab.id] ? (
255240
<img
@@ -276,7 +261,7 @@ export default function Browser() {
276261
>
277262
<X className="h-3 w-3" />
278263
</button>
279-
</div>
264+
</button>
280265
))}
281266
<button type="button" className="inline-flex h-8 w-8 items-center justify-center rounded-md text-sm font-medium text-text-secondary transition-colors hover:bg-background-secondary/50 hover:text-text" onClick={addNewTab} aria-label="Add tab">
282267
<Plus className="h-4 w-4" />
@@ -285,18 +270,10 @@ export default function Browser() {
285270

286271
<div className="flex items-center justify-between gap-3 bg-background-secondary px-3 py-2 backdrop-blur-xl">
287272
<div className="flex items-center gap-1">
288-
<button type="button" className={iconButtonClass} onClick={goHome} aria-label="Home">
289-
<Home className="h-4 w-4" />
290-
</button>
291-
<button type="button" className={iconButtonClass} onClick={goBack} aria-label="Back">
292-
<ChevronLeft className="h-4 w-4" />
293-
</button>
294-
<button type="button" className={iconButtonClass} onClick={goForward} aria-label="Forward">
295-
<ChevronRight className="h-4 w-4" />
296-
</button>
297-
<button type="button" className={iconButtonClass} onClick={reloadTab} aria-label="Reload">
298-
<RotateCw className="h-4 w-4" />
299-
</button>
273+
<IconButton icon={Home} onClick={() => Action("home")} title="Home" />
274+
<IconButton icon={ChevronLeft} onClick={() => Action("back")} title="Back" />
275+
<IconButton icon={ChevronRight} onClick={() => Action("forward")} title="Forward" />
276+
<IconButton icon={RotateCw} onClick={() => Action("reload")} title="Reload" />
300277
</div>
301278

302279
<div className="flex-1">
@@ -317,34 +294,26 @@ export default function Browser() {
317294
</div>
318295

319296
<div className="flex items-center gap-1">
320-
<button type="button" className={iconButtonClass} onClick={toggleFullscreen} aria-label="Fullscreen">
321-
<Maximize2 className="h-4 w-4" />
322-
</button>
323-
<button type="button" className={iconButtonClass} onClick={addBookmark} aria-label="Bookmark">
324-
<Star className="h-4 w-4" />
325-
</button>
326-
<button type="button" className={iconButtonClass} aria-label="Menu">
327-
<Menu className="h-4 w-4" />
328-
</button>
329-
<button type="button" className={iconButtonClass} aria-label="More">
330-
<MoreVertical className="h-4 w-4" />
331-
</button>
297+
<IconButton icon={Maximize2} onClick={toggleFullscreen} title="Fullscreen" />
298+
<IconButton icon={Star} onClick={addBookmark} title="Bookmark" />
299+
<IconButton icon={Menu} title="Menu" />
300+
<IconButton icon={MoreVertical} title="More" />
332301
</div>
333302
</div>
334303

335304
{bookmarks.length > 0 && (
336305
<div className="flex items-center gap-0.5 bg-background-secondary px-3 py-1.5 overflow-x-auto border-b border-border/50">
337-
{bookmarks.map((bookmark, index) => (
306+
{bookmarks.map((bookmark) => (
338307
<button
339-
key={index}
308+
key={`${bookmark.url}-${bookmark.Title}`}
340309
type="button"
341310
className="inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-sm text-text-secondary hover:bg-interactive hover:scale-105 transition-all shrink-0"
342311
style={{ maxWidth: "195px" }}
343312
onClick={() => handleNavigate(bookmark.url)}
344313
onContextMenu={(e) => {
345314
e.preventDefault();
346315
if (confirm(`Remove bookmark "${bookmark.Title}"?`)) {
347-
removeBookmark(index);
316+
removeBookmark(bookmark.url, bookmark.Title);
348317
}
349318
}}
350319
>

0 commit comments

Comments
 (0)