Skip to content

Commit 69c3287

Browse files
committed
Delegate connection creation to ChatScreen.
1 parent 24c1eb7 commit 69c3287

File tree

4 files changed

+102
-82
lines changed

4 files changed

+102
-82
lines changed

src/screens/ServerScreen.tsx

+18-50
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useContext, useEffect, useRef, useState } from 'react'
1+
import React, { useContext, useEffect, useState } from 'react'
22
import {
33
StyleSheet,
44
View,
@@ -27,17 +27,13 @@ import TextField from '../components/TextField'
2727
import ElevatedView from '../components/ElevatedView'
2828
import useDarkMode from '../context/useDarkMode'
2929
import ServersContext from '../context/serversContext'
30-
import useSessionStore from '../context/sessionStore'
31-
import SettingsContext from '../context/settingsContext'
32-
import AccountsContext from '../context/accountsContext'
3330
import ConnectionContext from '../context/connectionContext'
3431
import { parseIp, protocolMap } from '../minecraft/utils'
3532
import {
3633
ChatToJsx,
3734
lightColorMap,
3835
mojangColorMap
3936
} from '../minecraft/chatToJsx'
40-
import { createConnection } from './chat/sessionBuilder'
4137

4238
interface RootStackParamList {
4339
[index: string]: any
@@ -48,13 +44,8 @@ type Props = NativeStackScreenProps<RootStackParamList, 'Chat'>
4844

4945
const ServerScreen = (props: Props) => {
5046
const darkMode = useDarkMode()
51-
const { settings } = useContext(SettingsContext)
5247
const { servers, setServers } = useContext(ServersContext)
53-
const { accounts, setAccounts } = useContext(AccountsContext)
54-
const { connection, setConnection, setDisconnectReason } =
55-
useContext(ConnectionContext)
56-
const { sessions, setSession } = useSessionStore()
57-
const initiatingConnection = useRef(false)
48+
const { setDisconnectReason } = useContext(ConnectionContext)
5849

5950
const [ipAddr, setIpAddr] = useState('')
6051
const [ipAddrRed, setIpAddrRed] = useState(false)
@@ -147,46 +138,23 @@ const ServerScreen = (props: Props) => {
147138
cancelAddServer()
148139
}
149140

150-
const connectToServer = async (server: string) => {
151-
if (initiatingConnection.current) return
152-
if (!connection) {
153-
initiatingConnection.current = true
154-
let version = protocolMap[servers[server].version]
155-
if (version === -1) {
156-
const ping = pingResponses[servers[server].address]
157-
// Try the latest.
158-
if (!ping) version = protocolMap['1.19.1']
159-
else if (typeof ping.version === 'object') {
160-
version = ping.version.protocol
161-
} else version = (ping as LegacyPing).protocol
162-
}
163-
if (version < 754) {
164-
initiatingConnection.current = false
165-
return setDisconnectReason({
166-
server,
167-
reason: 'EnderChat only supports 1.16.4 and newer (for now).'
168-
})
169-
}
170-
// LOW-TODO: Creating a session would be better with a loading screen, since Microsoft Login is slow.
171-
// Maybe setConnection(null) to bring up ChatScreen while still being in a logged out state?
172-
// Or delegate this task to ChatScreen? Would make initiatingConnectionRef unnecessary.
173-
try {
174-
await createConnection(
175-
server,
176-
version,
177-
servers,
178-
settings,
179-
accounts,
180-
sessions,
181-
setSession,
182-
setAccounts,
183-
setConnection,
184-
setDisconnectReason,
185-
() => {}
186-
)
187-
} catch (e) {}
188-
initiatingConnection.current = false
141+
const connectToServer = async (serverName: string) => {
142+
let version = protocolMap[servers[serverName].version]
143+
if (version === -1) {
144+
const ping = pingResponses[servers[serverName].address]
145+
// Try the latest.
146+
if (!ping) version = protocolMap['1.19.1']
147+
else if (typeof ping.version === 'object') {
148+
version = ping.version.protocol
149+
} else version = (ping as LegacyPing).protocol
189150
}
151+
if (version < 754) {
152+
return setDisconnectReason({
153+
server: serverName,
154+
reason: 'EnderChat only supports 1.16.4 and newer (for now).'
155+
})
156+
}
157+
props.navigation.push('Chat', { serverName, version: version }) // getId prevents duplicate navigation.
190158
}
191159

192160
const modalButtonCancelText = darkMode

src/screens/chat/ChatScreen.tsx

+73-22
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ import {
1616
enderChatPrefix,
1717
sendMessageError
1818
} from './packetHandler'
19+
import { createConnection } from './sessionBuilder'
1920
import globalStyle from '../../globalStyle'
2021
import useDarkMode from '../../context/useDarkMode'
22+
import AccountsContext from '../../context/accountsContext'
23+
import ServersContext from '../../context/serversContext'
24+
import useSessionStore from '../../context/sessionStore'
2125
import SettingsContext from '../../context/settingsContext'
22-
import ConnectionContext from '../../context/connectionContext'
26+
import ConnectionContext, { Connection } from '../../context/connectionContext'
2327
import {
2428
ChatToJsx,
2529
mojangColorMap,
@@ -41,6 +45,8 @@ interface RootStackParamList {
4145
}
4246
type Props = NativeStackScreenProps<RootStackParamList, 'Chat'>
4347

48+
export type Status = 'OPENING' | 'CONNECTING' | 'CONNECTED' | 'CLOSED'
49+
4450
interface Message {
4551
key: number
4652
text: MinecraftChat
@@ -84,17 +90,26 @@ const handleError =
8490
addMessage(enderChatPrefix + translated)
8591
}
8692

93+
const isConnection = (connection: any): connection is Connection =>
94+
!!(connection as Connection).connection
95+
8796
// TODO: Ability to copy text.
8897
const ChatScreen = ({ navigation, route }: Props) => {
8998
const darkMode = useDarkMode()
9099
const { settings } = useContext(SettingsContext)
91-
const { connection, setConnection } = useContext(ConnectionContext)
100+
const { servers } = useContext(ServersContext)
101+
const { accounts, setAccounts } = useContext(AccountsContext)
102+
const { connection, setConnection, setDisconnectReason } =
103+
useContext(ConnectionContext)
104+
const { sessions, setSession } = useSessionStore()
92105
// TODO: Show command history.
93106
const [, setCommandHistory] = useState<string[]>([])
94107
const [settingsOpen, setSettingsOpen] = useState(false)
95108
const [messages, setMessages] = useState<Message[]>([])
96109
const [loggedIn, setLoggedIn] = useState(false)
97110
const [message, setMessage] = useState('')
111+
const healthRef = useRef<number | null>(null)
112+
const statusRef = useRef<Status>(connection ? 'CONNECTING' : 'OPENING')
98113
const idRef = useRef(0)
99114

100115
const charLimit =
@@ -106,17 +121,67 @@ const ChatScreen = ({ navigation, route }: Props) => {
106121
const trunc = m.length > 500 ? m.slice(0, 499) : m
107122
return [{ key: idRef.current++, text }].concat(trunc)
108123
})
124+
const closeChatScreen = () => {
125+
if (navigation.canGoBack() && statusRef.current !== 'CLOSED') {
126+
navigation.goBack()
127+
}
128+
}
129+
130+
// Screen cleanup function.
131+
useEffect(() =>
132+
navigation.addListener('beforeRemove', () => {
133+
statusRef.current = 'CLOSED'
134+
// Gracefully disconnect on unmount, destroy will be called in 20s automatically if needed.
135+
if (connection) {
136+
connection.connection.close()
137+
setConnection(undefined)
138+
}
139+
})
140+
)
141+
142+
// Create connection useEffect.
143+
useEffect(() => {
144+
if (statusRef.current === 'OPENING') {
145+
statusRef.current = 'CONNECTING'
146+
createConnection(
147+
route.params.serverName,
148+
route.params.version,
149+
servers,
150+
settings,
151+
accounts,
152+
sessions,
153+
setSession,
154+
setAccounts,
155+
setConnection,
156+
setDisconnectReason,
157+
closeChatScreen
158+
)
159+
.then(conn => {
160+
console.log(statusRef.current)
161+
console.log(isConnection(conn))
162+
if (statusRef.current !== 'CLOSED') {
163+
if (isConnection(conn)) setConnection(conn)
164+
else setDisconnectReason(conn)
165+
} else if (isConnection(conn)) conn.connection.close() // No memory leaky
166+
})
167+
.catch(() => {
168+
closeChatScreen()
169+
setDisconnectReason({
170+
server: route.params.serverName,
171+
reason: 'An error occurred resolving the server hostname!'
172+
})
173+
})
174+
}
175+
})
109176

110177
// Packet handler useEffect.
111-
const loggedInRef = useRef(false)
112-
const healthRef = useRef<number | null>(null)
113178
useEffect(() => {
114179
if (!connection) return
115180
connection.connection.on(
116181
'packet',
117182
packetHandler(
118183
healthRef,
119-
loggedInRef,
184+
statusRef,
120185
setLoggedIn,
121186
connection.connection,
122187
addMessage,
@@ -138,19 +203,6 @@ const ChatScreen = ({ navigation, route }: Props) => {
138203
settings.sendSpawnCommand
139204
])
140205

141-
// Cleanup useEffect on unload.
142-
useEffect(() => {
143-
if (!connection) {
144-
navigation.goBack() // Safety net.
145-
return
146-
}
147-
return () => {
148-
// Gracefully disconnect, destroy will be called in 20s automatically if needed.
149-
connection.connection.close()
150-
setConnection(undefined)
151-
}
152-
}, [connection, setConnection, navigation])
153-
154206
const sendMessage = (msg: string, saveHistory: boolean) => {
155207
if (!connection || !msg) return
156208
setMessage('')
@@ -191,7 +243,6 @@ const ChatScreen = ({ navigation, route }: Props) => {
191243
}
192244
}
193245

194-
if (!connection) return <></> // This should never be hit hopefully.
195246
const title =
196247
route.params.serverName.length > 12
197248
? route.params.serverName.substring(0, 9) + '...'
@@ -203,7 +254,7 @@ const ChatScreen = ({ navigation, route }: Props) => {
203254
iconStyle={styles.backButtonIcon}
204255
backgroundColor='#363636'
205256
onPress={() => {
206-
settingsOpen ? setSettingsOpen(false) : navigation.goBack()
257+
settingsOpen ? setSettingsOpen(false) : closeChatScreen()
207258
}}
208259
/>
209260
</View>
@@ -223,7 +274,7 @@ const ChatScreen = ({ navigation, route }: Props) => {
223274
onPress={() => setSettingsOpen(true)}
224275
/>
225276
</View>
226-
{!loggedIn && (
277+
{(!loggedIn || !connection) && (
227278
<View style={styles.loadingScreen}>
228279
<ActivityIndicator
229280
color='#00aaff'
@@ -235,7 +286,7 @@ const ChatScreen = ({ navigation, route }: Props) => {
235286
<Text style={styles.loadingScreenText}>Connecting...</Text>
236287
</View>
237288
)}
238-
{loggedIn && (
289+
{loggedIn && connection && (
239290
<>
240291
<ChatMessageListMemo
241292
messages={messages}

src/screens/chat/packetHandler.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react'
2+
import { Status } from './ChatScreen'
23
import { MinecraftChat, parseValidJson } from '../../minecraft/chatToJsx'
34
import { ServerConnection } from '../../minecraft/connection'
45
import { concatPacketData, Packet } from '../../minecraft/packet'
@@ -47,7 +48,7 @@ const handleSystemMessage = (
4748
export const packetHandler =
4849
(
4950
healthRef: React.MutableRefObject<number | null>,
50-
loggedInRef: React.MutableRefObject<boolean>,
51+
statusRef: React.MutableRefObject<Status>,
5152
setLoggedIn: React.Dispatch<React.SetStateAction<boolean>>,
5253
connection: ServerConnection,
5354
addMessage: (text: MinecraftChat) => any,
@@ -58,9 +59,9 @@ export const packetHandler =
5859
charLimit: number
5960
) =>
6061
(packet: Packet) => {
61-
if (!loggedInRef.current && connection.loggedIn) {
62+
if (statusRef.current === 'CONNECTING' && connection.loggedIn) {
6263
setLoggedIn(true)
63-
loggedInRef.current = true
64+
statusRef.current = 'CONNECTED'
6465
if (sendJoinMessage) {
6566
connection
6667
.writePacket(

src/screens/chat/sessionBuilder.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,25 @@ export const createConnection = async (
2828
setConnection: (conn?: Connection) => void,
2929
setDisconnectReason: (reason: DisconnectReason) => void,
3030
closeChatScreen: () => void
31-
) => {
31+
): Promise<Connection | DisconnectReason> => {
3232
const [hostname, portNumber] = parseIp(servers[server].address)
3333
const [host, port] = await resolveHostname(hostname, portNumber)
3434
const activeAccount = Object.keys(accounts).find(e => accounts[e].active)
3535
if (!activeAccount) {
3636
closeChatScreen()
37-
return setDisconnectReason({
37+
return {
3838
server,
3939
reason:
4040
'No active account selected! Open the Accounts tab and add an account.'
41-
})
41+
}
4242
}
4343
const uuid = accounts[activeAccount].type ? activeAccount : undefined
4444
// Create an updated "session" containing access tokens and certificates.
4545
let session = sessions[activeAccount]
4646
const is119 = version >= protocolMap[1.19]
4747
if (uuid && (!session || (!session.certificate && is119))) {
4848
// LOW-TODO: Certificates and access tokens should be updated regularly.
49+
// We should probably lock access to them via a semaphore.
4950
try {
5051
// Create a session with the latest access token.
5152
const account = accounts[activeAccount]
@@ -88,8 +89,7 @@ export const createConnection = async (
8889
closeChatScreen()
8990
const reason =
9091
'Failed to create session! You may need to re-login with your Microsoft Account in the Accounts tab.'
91-
setDisconnectReason({ server, reason })
92-
return
92+
return { server, reason }
9393
}
9494
}
9595

@@ -114,9 +114,9 @@ export const createConnection = async (
114114
}
115115
newConn.on('close', onCloseOrError)
116116
newConn.on('error', onCloseOrError)
117-
setConnection({ serverName: server, connection: newConn })
117+
return { serverName: server, connection: newConn }
118118
} catch (e) {
119119
closeChatScreen()
120-
setDisconnectReason({ server, reason: 'Failed to connect to server!' })
120+
return { server, reason: 'Failed to connect to server!' }
121121
}
122122
}

0 commit comments

Comments
 (0)