Skip to content

Commit f7a6814

Browse files
committed
Support editing servers, ignore unknown settings.
1 parent 308cd7c commit f7a6814

File tree

4 files changed

+94
-82
lines changed

4 files changed

+94
-82
lines changed

src/App.tsx

+17-13
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,23 @@ const App = () => {
8585
const [connection, setConnection] = React.useState<Connection | undefined>()
8686
const [disconnect, setDisconnect] = React.useState<Disconnect | undefined>()
8787

88-
const [settings, setSettings] = useJsonAsyncStorage<Settings>('@settings', {
89-
// TODO: Better defaults and settings.
90-
joinMessage:
91-
"I'm using EnderChat, a well-built, ad-free ChatCraft alternative! Even this message can be disabled!",
92-
sendJoinMessage: true,
93-
sendSpawnCommand: true,
94-
chatTheme: 'Colorless',
95-
fontSize: 16,
96-
webLinks: true,
97-
darkMode: null,
98-
linkPrompt: true,
99-
disableAutoCorrect: false
100-
})
88+
const [settings, setSettings] = useJsonAsyncStorage<Settings>(
89+
'@settings',
90+
{
91+
// TODO: Better defaults and settings.
92+
joinMessage:
93+
"I'm using EnderChat, a well-built, ad-free ChatCraft alternative! Even this message can be disabled!",
94+
sendJoinMessage: true,
95+
sendSpawnCommand: true,
96+
chatTheme: 'Colorless',
97+
fontSize: 16,
98+
webLinks: true,
99+
darkMode: null,
100+
linkPrompt: true,
101+
disableAutoCorrect: false
102+
},
103+
true
104+
)
101105
const [accountsStore, setAccountsStore] = useAsyncStorage('@accounts', '{}')
102106
const [serversStore, setServersStore] = useAsyncStorage('@servers', '{}')
103107
const accounts: Accounts = JSON.parse(accountsStore)

src/helpers/useJsonAsyncStorage.ts

+22-12
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,43 @@ const useMemoisedValue = <T>(value: T) => {
99
return ref.current
1010
}
1111

12-
// This does not support deep merge, do not use it for deep merging.
13-
// LOW-TODO: This does not support removing existing keys.
1412
// This function requires JSON compatible objects to be passed.
13+
// This does not support deep merge, do not use it for nested objects.
1514
const useJsonAsyncStorage = <T extends {}>(
1615
name: string,
17-
passedDefaultValue: T
16+
defaultValue: T,
17+
ignoreUnknownKeys = false
1818
): [T, SetJsonAsyncStorage<T>] => {
19-
const defaultValue = useMemoisedValue(passedDefaultValue)
20-
const [state, setState] = useState(defaultValue)
19+
const defaultValueMemo = useMemoisedValue(defaultValue)
20+
const [state, setState] = useState(defaultValueMemo)
2121

2222
useEffect(() => {
2323
AsyncStorage.getItem(name)
2424
.then(res => {
25-
if (!res) return defaultValue
26-
setState({ ...defaultValue, ...JSON.parse(res) })
25+
if (res) {
26+
if (ignoreUnknownKeys) {
27+
const parsed = JSON.parse(res) as T
28+
const newValue = { ...defaultValueMemo }
29+
// Remove any values that aren't present in defaultValue.
30+
for (const key in parsed) {
31+
if (Object.prototype.hasOwnProperty.call(newValue, key)) {
32+
newValue[key] = parsed[key]
33+
}
34+
}
35+
setState(newValue)
36+
} else setState({ ...defaultValueMemo, ...JSON.parse(res) })
37+
}
2738
})
2839
.catch(err => console.error(err))
29-
}, [name, defaultValue])
40+
}, [name, defaultValueMemo, ignoreUnknownKeys])
3041

3142
return [
3243
state,
3344
(value: Partial<T>) => {
3445
const merge = { ...state, ...value }
35-
setState(merge)
36-
AsyncStorage.setItem(name, JSON.stringify(merge)).catch(err =>
37-
console.error(err)
38-
)
46+
AsyncStorage.setItem(name, JSON.stringify(merge))
47+
.then(() => setState(merge))
48+
.catch(err => console.error(err))
3949
}
4050
]
4151
}

src/screens/ServerScreen.tsx

+54-56
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ const ServerScreen = () => {
6363
const [serverNameRed, setServerNameRed] = useState(false)
6464
const [serverVersion, setServerVersion] =
6565
useState<keyof typeof protocolMap>('auto')
66-
const [addServerDialogOpen, setAddServerDialogOpen] = useState(false)
67-
const [editServerDialogOpen, setEditServerDialogOpen] = useState('')
66+
const [editServerDialogOpen, setEditServerDialogOpen] = useState<
67+
string | boolean
68+
>(false)
6869
const [pingResponses, setPingResponses] = useState<{
6970
// false - no route, null - unknown err, undefined - pinging
7071
[ip: string]: LegacyPing | Ping | false | null | undefined
@@ -102,32 +103,42 @@ const ServerScreen = () => {
102103
}, [servers, pingResponses])
103104

104105
const invalidServerName = newServerName.length > 32
106+
const openEditServerDialog = (server: string) => {
107+
setEditServerDialogOpen(server)
108+
setNewServerName(server)
109+
setServerVersion(servers[server].version)
110+
setIpAddr(servers[server].address)
111+
}
105112
const cancelAddServer = () => {
106-
setAddServerDialogOpen(false)
113+
setEditServerDialogOpen(false)
107114
setServerVersion('auto')
108115
setNewServerName('')
109116
setIpAddr('')
110117
setIpAddrRed(false)
111118
setServerNameRed(false)
112119
}
113-
const addServer = () => {
120+
const deleteServer = () => {
121+
if (typeof editServerDialogOpen !== 'string') return cancelAddServer()
122+
delete servers[editServerDialogOpen]
123+
setServers(servers)
124+
cancelAddServer()
125+
}
126+
const editServer = () => {
127+
const edit = typeof editServerDialogOpen === 'string'
114128
if (
115129
!newServerName ||
116130
invalidServerName ||
117-
ipAddr === '' ||
118-
servers[newServerName]
131+
(!edit && servers[newServerName]) ||
132+
(edit && servers[newServerName] && newServerName !== editServerDialogOpen)
119133
) {
120-
setIpAddrRed(ipAddr === '')
121-
setServerNameRed(!newServerName)
122-
return
134+
return setServerNameRed(true)
135+
} else if (ipAddr === '') {
136+
return setIpAddrRed(true)
123137
}
124-
setServers({
125-
...servers,
126-
[newServerName]: {
127-
version: serverVersion,
128-
address: ipAddr
129-
}
130-
})
138+
const newServers = { ...servers }
139+
if (edit) delete newServers[editServerDialogOpen]
140+
servers[newServerName] = { version: serverVersion, address: ipAddr }
141+
setServers(newServers)
131142
setPingResponses({})
132143
cancelAddServer()
133144
}
@@ -172,7 +183,7 @@ const ServerScreen = () => {
172183
selectedProfile: uuid,
173184
accessToken: accounts[activeAccount].accessToken
174185
})
175-
const onCloseOrError = () => {
186+
const onCloseOrError: () => void = () => {
176187
setConnection(undefined)
177188
if (newConn.disconnectReason) {
178189
setDisconnectReason({
@@ -191,28 +202,11 @@ const ServerScreen = () => {
191202
}
192203
}
193204

194-
// LOW-TODO: Support editing servers.
205+
const modalButtonCancelText = darkMode
206+
? dialogStyles.modalButtonCancelDarkText
207+
: dialogStyles.modalButtonCancelText
195208
return (
196209
<>
197-
<Dialog
198-
visible={!!editServerDialogOpen}
199-
onRequestClose={() => setEditServerDialogOpen('')}
200-
containerStyles={styles.deleteServerDialog}
201-
>
202-
<Pressable
203-
onPress={() => {
204-
delete servers[editServerDialogOpen]
205-
setEditServerDialogOpen('')
206-
setServers(servers)
207-
}}
208-
android_ripple={{ color: '#aaa' }}
209-
style={dialogStyles.modalButton}
210-
>
211-
<Text style={styles.deleteServerText}>
212-
Delete '{editServerDialogOpen}' server
213-
</Text>
214-
</Pressable>
215-
</Dialog>
216210
{disconnectReason && (
217211
<Dialog visible onRequestClose={() => setDisconnectReason()}>
218212
<Text style={dialogStyles.modalTitle}>
@@ -236,10 +230,12 @@ const ServerScreen = () => {
236230
</View>
237231
</Dialog>
238232
)}
239-
<Dialog visible={addServerDialogOpen} onRequestClose={cancelAddServer}>
240-
<Text style={dialogStyles.modalTitle}>Add Server</Text>
233+
<Dialog visible={!!editServerDialogOpen} onRequestClose={cancelAddServer}>
234+
<Text style={dialogStyles.modalTitle}>
235+
{typeof editServerDialogOpen === 'string' ? 'Edit' : 'Add'} Server
236+
</Text>
241237
<TextField
242-
red={!!servers[newServerName] || serverNameRed || invalidServerName}
238+
red={serverNameRed || invalidServerName}
243239
value={newServerName}
244240
onChangeText={setNewServerName}
245241
placeholder='Server Name'
@@ -264,28 +260,31 @@ const ServerScreen = () => {
264260
<Picker.Item label='1.16.4/1.16.5' value='1.16.4' />
265261
</Picker>
266262
<View style={dialogStyles.modalButtons}>
263+
{typeof editServerDialogOpen === 'string' && (
264+
<Pressable
265+
onPress={deleteServer}
266+
android_ripple={{ color: '#aaa' }}
267+
style={dialogStyles.modalButton}
268+
>
269+
<Text style={styles.deleteServerButtonText}>DELETE</Text>
270+
</Pressable>
271+
)}
267272
<View style={globalStyle.flexSpacer} />
268273
<Pressable
269274
onPress={cancelAddServer}
270275
android_ripple={{ color: '#aaa' }}
271276
style={dialogStyles.modalButton}
272277
>
273-
<Text
274-
style={
275-
darkMode
276-
? dialogStyles.modalButtonCancelDarkText
277-
: dialogStyles.modalButtonCancelText
278-
}
279-
>
280-
CANCEL
281-
</Text>
278+
<Text style={modalButtonCancelText}>CANCEL</Text>
282279
</Pressable>
283280
<Pressable
284-
onPress={addServer}
281+
onPress={() => editServer()}
285282
android_ripple={{ color: '#aaa' }}
286283
style={dialogStyles.modalButton}
287284
>
288-
<Text style={dialogStyles.modalButtonText}>ADD</Text>
285+
<Text style={dialogStyles.modalButtonText}>
286+
{typeof editServerDialogOpen === 'string' ? 'EDIT' : 'ADD'}
287+
</Text>
289288
</Pressable>
290289
</View>
291290
</Dialog>
@@ -294,7 +293,7 @@ const ServerScreen = () => {
294293
<View style={globalStyle.flexSpacer} />
295294
<Ionicons.Button
296295
name='add'
297-
onPress={() => setAddServerDialogOpen(true)}
296+
onPress={() => setEditServerDialogOpen(true)}
298297
iconStyle={globalStyle.iconStyle}
299298
>
300299
<Text style={globalStyle.iconButtonText}>Add</Text>
@@ -319,7 +318,7 @@ const ServerScreen = () => {
319318
<ElevatedView key={server} style={styles.serverView}>
320319
<Pressable
321320
onPress={async () => await connectToServer(server)}
322-
onLongPress={() => setEditServerDialogOpen(server)}
321+
onLongPress={() => openEditServerDialog(server)}
323322
android_ripple={{ color: '#aaa' }}
324323
style={styles.serverPressable}
325324
>
@@ -406,10 +405,9 @@ const styles = StyleSheet.create({
406405
serverName: { fontSize: 20, fontWeight: 'bold' },
407406
serverPlayers: { fontSize: 12, fontWeight: 'bold' },
408407
serverDescription: { fontSize: 14 },
409-
deleteServerText: { fontSize: 16 },
410-
deleteServerDialog: { padding: 0 },
411408
addServerPickerDark: { color: '#ffffff' },
412-
addServerPicker: { color: '#000000' }
409+
addServerPicker: { color: '#000000' },
410+
deleteServerButtonText: { color: '#ff0000', fontWeight: 'bold' }
413411
})
414412

415413
export default ServerScreen

src/screens/accounts/MicrosoftLogin.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ const MicrosoftLogin = ({ close }: { close: () => void }) => {
131131
onNavigationStateChange={handleNavigationStateChange}
132132
androidLayerType={
133133
Platform.OS === 'android' && Platform.Version > 30
134-
? 'software' // TODO: Really choppy. Solve when you get Android 12?
134+
? 'software' // TODO: Really choppy. Seems to only happen on Google Pixel?
135135
: 'hardware'
136136
}
137137
/>

0 commit comments

Comments
 (0)