Skip to content

Commit 6fd8598

Browse files
committed
refactor: various API and UI cleanups
1 parent 0be1e7e commit 6fd8598

File tree

11 files changed

+262
-393
lines changed

11 files changed

+262
-393
lines changed

browser-chat/frontend/src/app.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
@tailwind components;
33
@tailwind utilities;
44

5+
html {
6+
background: hsl(0 0% 3.9%);
7+
}
8+
59
body {
610
font-family: Arial, Helvetica, sans-serif;
11+
display: none;
712
}
813

914
@layer utilities {
@@ -91,4 +96,8 @@ body {
9196
body {
9297
@apply bg-background text-foreground;
9398
}
99+
.dark body,
100+
.light body {
101+
@apply block;
102+
}
94103
}

browser-chat/frontend/src/app.tsx

Lines changed: 97 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -7,65 +7,80 @@ import Header from "./components/header"
77
import LogView from "./components/logview"
88
import Sidebar from "./components/sidebar"
99
import { ThemeProvider } from "next-themes"
10-
import { Button } from "@/components/ui/button"
11-
import { Moon, Sun } from "lucide-react"
12-
import { useTheme } from "next-themes"
1310
import { InvitePopup } from "./components/invitepopup"
14-
import { api, type ChannelInfo } from "./lib/api"
15-
import { log } from "./lib/log"
11+
import { API, initApi, type ChannelInfo } from "./lib/api"
1612
import { generate as generateName } from 'yet-another-name-generator'
13+
import { log } from "./lib/log"
1714

18-
function ThemeToggle() {
19-
const { theme, setTheme } = useTheme()
20-
const [mounted, setMounted] = useState(false)
21-
22-
useEffect(() => setMounted(true), [])
15+
export default function AppWrapper() {
16+
const [api, setApi] = useState<API | null>(null)
17+
const [error, setError] = useState<string | null>(null)
18+
useEffect(() => {
19+
initApi()
20+
.then(setApi)
21+
.catch(err => setError(err.toString()))
22+
}, [])
23+
return (
24+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
25+
<div className="flex h-screen">
26+
{!api && (
27+
<SplashScreen>
28+
{!error && <div className="text-center">Spawning Iroh…<br /><Spinner /></div>}
29+
{error && <div>{error}</div>}
30+
</SplashScreen>
31+
)}
32+
{api && <App api={api} />}
33+
</div>
34+
</ThemeProvider>
35+
)
36+
}
2337

24-
if (!mounted) return null
38+
function Spinner() {
39+
return (
40+
<svg className="inline-block h-5 w-5 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
41+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
42+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
43+
</svg>
44+
)
45+
}
2546

47+
function SplashScreen({ children }: React.PropsWithChildren) {
48+
const [showLogView, setShowLogView] = useState(false)
2649
return (
27-
<Button variant="ghost" size="icon" onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
28-
{theme === "light" ? <Moon className="h-5 w-5" /> : <Sun className="h-5 w-5" />}
29-
<span className="sr-only">Toggle theme</span>
30-
</Button>
50+
<div className="flex flex-col flex-grow">
51+
<Header
52+
onLogsClick={() => setShowLogView(!showLogView)}
53+
/>
54+
{showLogView && <LogView onClose={() => setShowLogView(false)} />}
55+
<div className="flex items-center justify-center">
56+
{children}
57+
</div>
58+
</div>
3159
)
60+
61+
}
62+
63+
interface AppProps {
64+
api: API
3265
}
3366

34-
export default function App() {
35-
// ... (previous state declarations)
67+
function App({ api }: AppProps) {
3668
const [currentView, setCurrentView] = useState<"home" | "chat">("home")
3769
const [channels, setChannels] = useState<ChannelInfo[]>([])
3870
const [activeChannel, setActiveChannel] = useState<string | null>(null)
39-
const [isConnected, setIsConnected] = useState(false)
40-
const [neighbors, setNeighbors] = useState(0)
4171
const [showLogView, setShowLogView] = useState(false)
4272
const [nickname, setNickname] = useState(generateName())
43-
const [logs, setLogs] = useState<{ timestamp: Date; level: "info" | "warn" | "error"; message: string }[]>([])
4473
const [showInvitePopup, setShowInvitePopup] = useState(false)
45-
4674
const [showSidebar, setShowSidebar] = useState(false)
4775

48-
// ... (previous useEffect and other functions)
49-
50-
useEffect(() => {
51-
const unsubscribe = log.subscribe((logMessage) => {
52-
setLogs((prevLogs) => [...prevLogs, logMessage])
53-
})
54-
55-
return () => unsubscribe()
56-
}, [])
57-
5876
const joinChannel = async (ticket: string) => {
5977
try {
6078
const channel = await api.joinChannel(ticket, nickname)
6179
setChannels((prevChannels) => [...prevChannels, channel])
6280
setActiveChannel(channel.id)
6381
setCurrentView("chat")
64-
setIsConnected(true)
65-
const peers = await api.getPeers(channel.id)
66-
setNeighbors(peers.length)
6782
} catch (error) {
68-
console.error(`Failed to join channel: ${error}`)
83+
log.error("Failed to join channel", error)
6984
}
7085
}
7186

@@ -75,10 +90,8 @@ export default function App() {
7590
setChannels((prevChannels) => [...prevChannels, channel])
7691
setActiveChannel(channel.id)
7792
setCurrentView("chat")
78-
setIsConnected(true)
79-
setNeighbors(0)
8093
} catch (error) {
81-
console.error(`Failed to create channel: ${error}`)
94+
log.error("Failed to create channel", error)
8295
}
8396
}
8497

@@ -90,11 +103,10 @@ export default function App() {
90103
setActiveChannel(channels.length > 1 ? channels[0].id : null)
91104
if (channels.length === 1) {
92105
setCurrentView("home")
93-
setIsConnected(false)
94106
}
95107
}
96108
} catch (error) {
97-
console.error(`Failed to close channel: ${error}`)
109+
log.error("Failed to close channel", error)
98110
}
99111
}
100112

@@ -103,56 +115,56 @@ export default function App() {
103115
setShowSidebar(true)
104116
}
105117

118+
let title
119+
if (activeChannel) {
120+
title = '#' + channels.find((c) => c.id === activeChannel)?.name
121+
}
122+
106123
return (
107-
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
108-
<div className="flex h-screen">
109-
{(currentView === "chat" || showSidebar) && (
110-
<Sidebar
111-
channels={channels}
112-
activeChannel={activeChannel}
113-
onChannelSelect={(channelId) => {
114-
setActiveChannel(channelId)
115-
setCurrentView("chat")
124+
<>
125+
{(currentView === "chat" || showSidebar) && (
126+
<Sidebar
127+
channels={channels}
128+
activeChannel={activeChannel}
129+
onChannelSelect={(channelId) => {
130+
setActiveChannel(channelId)
131+
setCurrentView("chat")
132+
}}
133+
onNewChannel={handleNewChannel}
134+
/>
135+
)}
136+
<div className="flex flex-col flex-grow">
137+
<Header
138+
onLogsClick={() => setShowLogView(!showLogView)}
139+
title={title}
140+
onInviteClick={activeChannel ? (() => setShowInvitePopup(true)) : undefined}
141+
/>
142+
{currentView === "home" && (
143+
<HomeScreen
144+
name={nickname}
145+
onSetName={setNickname}
146+
onJoin={(ticket) => {
147+
joinChannel(ticket)
148+
setShowSidebar(false)
149+
}}
150+
onCreate={() => {
151+
createChannel()
152+
setShowSidebar(false)
116153
}}
117-
onNewChannel={handleNewChannel}
118154
/>
119155
)}
120-
<div className="flex flex-col flex-grow">
121-
<Header
122-
isConnected={isConnected}
123-
neighbors={neighbors}
124-
onMenuClick={() => setShowLogView(!showLogView)}
125-
themeToggle={<ThemeToggle />}
126-
channel={activeChannel ? channels.find((c) => c.id === activeChannel)?.name || null : null}
127-
onInviteClick={() => setShowInvitePopup(true)}
156+
{currentView === "chat" && activeChannel && (
157+
<ChatView api={api} channel={activeChannel} onClose={() => closeChannel(activeChannel)} />
158+
)}
159+
{showLogView && <LogView onClose={() => setShowLogView(false)} />}
160+
{showInvitePopup && activeChannel && (
161+
<InvitePopup
162+
onClose={() => setShowInvitePopup(false)}
163+
channel={channels.find((c) => c.id === activeChannel)?.name || ""}
164+
getTicket={(opts) => api.getTicket(activeChannel!, opts)}
128165
/>
129-
{currentView === "home" && (
130-
<HomeScreen
131-
name={nickname}
132-
onSetName={setNickname}
133-
onJoin={(ticket) => {
134-
joinChannel(ticket)
135-
setShowSidebar(false)
136-
}}
137-
onCreate={() => {
138-
createChannel()
139-
setShowSidebar(false)
140-
}}
141-
/>
142-
)}
143-
{currentView === "chat" && activeChannel && (
144-
<ChatView channel={activeChannel} onClose={() => closeChannel(activeChannel)} />
145-
)}
146-
{showLogView && <LogView logs={logs} onClose={() => setShowLogView(false)} />}
147-
{showInvitePopup && activeChannel && (
148-
<InvitePopup
149-
onClose={() => setShowInvitePopup(false)}
150-
channel={channels.find((c) => c.id === activeChannel)?.name || ""}
151-
getTicket={(opts) => api.getTicket(activeChannel!, opts)}
152-
/>
153-
)}
154-
</div>
166+
)}
155167
</div>
156-
</ThemeProvider>
168+
</>
157169
)
158170
}

0 commit comments

Comments
 (0)