@@ -7,65 +7,80 @@ import Header from "./components/header"
7
7
import LogView from "./components/logview"
8
8
import Sidebar from "./components/sidebar"
9
9
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"
13
10
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"
16
12
import { generate as generateName } from 'yet-another-name-generator'
13
+ import { log } from "./lib/log"
17
14
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
+ }
23
37
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
+ }
25
46
47
+ function SplashScreen ( { children } : React . PropsWithChildren ) {
48
+ const [ showLogView , setShowLogView ] = useState ( false )
26
49
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 >
31
59
)
60
+
61
+ }
62
+
63
+ interface AppProps {
64
+ api : API
32
65
}
33
66
34
- export default function App ( ) {
35
- // ... (previous state declarations)
67
+ function App ( { api } : AppProps ) {
36
68
const [ currentView , setCurrentView ] = useState < "home" | "chat" > ( "home" )
37
69
const [ channels , setChannels ] = useState < ChannelInfo [ ] > ( [ ] )
38
70
const [ activeChannel , setActiveChannel ] = useState < string | null > ( null )
39
- const [ isConnected , setIsConnected ] = useState ( false )
40
- const [ neighbors , setNeighbors ] = useState ( 0 )
41
71
const [ showLogView , setShowLogView ] = useState ( false )
42
72
const [ nickname , setNickname ] = useState ( generateName ( ) )
43
- const [ logs , setLogs ] = useState < { timestamp : Date ; level : "info" | "warn" | "error" ; message : string } [ ] > ( [ ] )
44
73
const [ showInvitePopup , setShowInvitePopup ] = useState ( false )
45
-
46
74
const [ showSidebar , setShowSidebar ] = useState ( false )
47
75
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
-
58
76
const joinChannel = async ( ticket : string ) => {
59
77
try {
60
78
const channel = await api . joinChannel ( ticket , nickname )
61
79
setChannels ( ( prevChannels ) => [ ...prevChannels , channel ] )
62
80
setActiveChannel ( channel . id )
63
81
setCurrentView ( "chat" )
64
- setIsConnected ( true )
65
- const peers = await api . getPeers ( channel . id )
66
- setNeighbors ( peers . length )
67
82
} catch ( error ) {
68
- console . error ( ` Failed to join channel: ${ error } ` )
83
+ log . error ( " Failed to join channel" , error )
69
84
}
70
85
}
71
86
@@ -75,10 +90,8 @@ export default function App() {
75
90
setChannels ( ( prevChannels ) => [ ...prevChannels , channel ] )
76
91
setActiveChannel ( channel . id )
77
92
setCurrentView ( "chat" )
78
- setIsConnected ( true )
79
- setNeighbors ( 0 )
80
93
} catch ( error ) {
81
- console . error ( ` Failed to create channel: ${ error } ` )
94
+ log . error ( " Failed to create channel" , error )
82
95
}
83
96
}
84
97
@@ -90,11 +103,10 @@ export default function App() {
90
103
setActiveChannel ( channels . length > 1 ? channels [ 0 ] . id : null )
91
104
if ( channels . length === 1 ) {
92
105
setCurrentView ( "home" )
93
- setIsConnected ( false )
94
106
}
95
107
}
96
108
} catch ( error ) {
97
- console . error ( ` Failed to close channel: ${ error } ` )
109
+ log . error ( " Failed to close channel" , error )
98
110
}
99
111
}
100
112
@@ -103,56 +115,56 @@ export default function App() {
103
115
setShowSidebar ( true )
104
116
}
105
117
118
+ let title
119
+ if ( activeChannel ) {
120
+ title = '#' + channels . find ( ( c ) => c . id === activeChannel ) ?. name
121
+ }
122
+
106
123
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 )
116
153
} }
117
- onNewChannel = { handleNewChannel }
118
154
/>
119
155
) }
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 ) }
128
165
/>
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
+ ) }
155
167
</ div >
156
- </ ThemeProvider >
168
+ </ >
157
169
)
158
170
}
0 commit comments