Skip to content

Commit a86e7e9

Browse files
committed
Refactor AccountScreen and ServerScreen
Move big UI elements into their own individual components.
1 parent c73557d commit a86e7e9

6 files changed

Lines changed: 377 additions & 258 deletions

File tree

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ const App = () => {
106106
const accounts: Accounts = JSON.parse(accountsStore)
107107
const servers: Servers = JSON.parse(serversStore)
108108
const setAccounts = (newAccounts: Accounts) =>
109-
setAccountsStore(JSON.stringify({ ...accounts, ...newAccounts }))
109+
setAccountsStore(JSON.stringify(newAccounts))
110110
const setServers = (newServers: Servers) =>
111-
setServersStore(JSON.stringify({ ...servers, ...newServers }))
111+
setServersStore(JSON.stringify(newServers))
112112
const systemDefault = colorScheme === null ? true : colorScheme === 'dark'
113113
const darkMode =
114114
settings.darkMode === null ? systemDefault : settings.darkMode
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React from 'react'
2+
import { Image, Pressable, StyleSheet, View } from 'react-native'
3+
import { Account } from '../../context/accountsContext'
4+
import ElevatedView from '../ElevatedView'
5+
import Text from '../Text'
6+
7+
const AccountDisplay = ({
8+
uuid,
9+
account,
10+
darkMode,
11+
setActiveAccount,
12+
setDeleteAccount
13+
}: {
14+
uuid: string
15+
account: Account
16+
darkMode: boolean
17+
setActiveAccount: (uuid: string) => void
18+
setDeleteAccount: (uuid: string) => void
19+
}) => (
20+
<ElevatedView style={styles.accountView}>
21+
<Pressable
22+
onPress={() => setActiveAccount(uuid)}
23+
onLongPress={() => setDeleteAccount(uuid)}
24+
android_ripple={{ color: '#aaa' }}
25+
style={styles.accountPressable}
26+
>
27+
<Image
28+
source={{
29+
uri: `https://crafthead.net/avatar/${
30+
account.type ? uuid : account.username
31+
}/72`
32+
}}
33+
style={styles.accountImage}
34+
/>
35+
<View>
36+
<Text style={styles.username}>{account.username}</Text>
37+
<Text style={darkMode ? styles.authTxtDark : styles.authTxt}>
38+
{account.type === 'mojang'
39+
? 'Mojang: ' + account.email
40+
: account.type === 'microsoft'
41+
? 'Microsoft Account'
42+
: 'Offline Mode'}
43+
</Text>
44+
{account.active && <Text style={styles.active}>Active Account</Text>}
45+
</View>
46+
</Pressable>
47+
</ElevatedView>
48+
)
49+
50+
const styles = StyleSheet.create({
51+
accountView: { marginBottom: 12 },
52+
accountPressable: { padding: 8, flexDirection: 'row' },
53+
accountImage: {
54+
padding: 4,
55+
height: 72,
56+
width: 72,
57+
resizeMode: 'contain',
58+
marginRight: 16
59+
},
60+
active: { fontSize: 14, fontWeight: 'bold' },
61+
username: { fontSize: 20, fontWeight: 'bold' },
62+
authTxt: { fontSize: 12, color: '#666', fontWeight: '400' },
63+
authTxtDark: { fontSize: 12, color: '#aaa', fontWeight: '400' }
64+
})
65+
66+
export default AccountDisplay
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import React, { useEffect, useState } from 'react'
2+
import { StyleSheet, View, Pressable } from 'react-native'
3+
import { Picker } from '@react-native-picker/picker'
4+
5+
import globalStyle from '../../globalStyle'
6+
import { Servers } from '../../context/serversContext'
7+
import Dialog, { dialogStyles } from '../Dialog'
8+
import Text from '../Text'
9+
import TextField from '../TextField'
10+
import { protocolMap } from '../../minecraft/utils'
11+
12+
const EditServerDialog = ({
13+
servers,
14+
darkMode,
15+
editServer,
16+
deleteServer,
17+
editServerDialogOpen,
18+
setEditServerDialogOpen
19+
}: {
20+
servers: Servers
21+
darkMode: boolean
22+
editServer: (
23+
serverName: string,
24+
version: keyof typeof protocolMap,
25+
address: string
26+
) => void
27+
deleteServer: (server: string) => void
28+
editServerDialogOpen: string | boolean
29+
setEditServerDialogOpen: (server: string | boolean) => void
30+
}) => {
31+
const [ipAddr, setIpAddr] = useState('')
32+
const [ipAddrRed, setIpAddrRed] = useState(false)
33+
const [newServerName, setNewServerName] = useState('')
34+
const [serverNameRed, setServerNameRed] = useState(false)
35+
const [serverVersion, setServerVersion] =
36+
useState<keyof typeof protocolMap>('auto')
37+
38+
const invalidServerName = newServerName.length > 32
39+
40+
// Reset properties whenever dialog opens (could also happen when server changes, though, but this is unlikely).
41+
const server =
42+
typeof editServerDialogOpen === 'string' && servers[editServerDialogOpen]
43+
useEffect(() => {
44+
setIpAddrRed(false)
45+
setServerNameRed(false)
46+
if (server) {
47+
setNewServerName(editServerDialogOpen)
48+
setServerVersion(server.version)
49+
setIpAddr(server.address)
50+
} else {
51+
setNewServerName('')
52+
setServerVersion('auto')
53+
setIpAddr('')
54+
}
55+
}, [editServerDialogOpen, server])
56+
57+
const closeDialog = () => setEditServerDialogOpen(false)
58+
59+
const handleDeleteServer = () => {
60+
if (typeof editServerDialogOpen !== 'string') return
61+
deleteServer(editServerDialogOpen)
62+
closeDialog()
63+
}
64+
65+
const handleEditServer = () => {
66+
const edit = typeof editServerDialogOpen === 'string'
67+
if (
68+
!newServerName ||
69+
invalidServerName ||
70+
(!edit && servers[newServerName]) ||
71+
(edit && servers[newServerName] && newServerName !== editServerDialogOpen)
72+
) {
73+
return setServerNameRed(true)
74+
} else if (ipAddr === '') {
75+
return setIpAddrRed(true)
76+
}
77+
editServer(newServerName, serverVersion, ipAddr)
78+
closeDialog()
79+
}
80+
81+
const modalButtonCancelText = darkMode
82+
? dialogStyles.modalButtonCancelDarkText
83+
: dialogStyles.modalButtonCancelText
84+
85+
return (
86+
<Dialog visible={!!editServerDialogOpen} onRequestClose={closeDialog}>
87+
<Text style={dialogStyles.modalTitle}>
88+
{typeof editServerDialogOpen === 'string' ? 'Edit' : 'Add'} Server
89+
</Text>
90+
<TextField
91+
red={serverNameRed || invalidServerName}
92+
value={newServerName}
93+
onChangeText={setNewServerName}
94+
placeholder='Server Name'
95+
/>
96+
<TextField
97+
red={ipAddrRed}
98+
value={ipAddr}
99+
onChangeText={setIpAddr}
100+
placeholder='IP Address'
101+
/>
102+
<Picker
103+
selectedValue={serverVersion}
104+
style={darkMode ? styles.addServerPickerDark : styles.addServerPicker}
105+
onValueChange={itemValue => setServerVersion(itemValue)}
106+
dropdownIconColor={darkMode ? '#ffffff' : '#000000'}
107+
>
108+
<Picker.Item label='Auto' value='auto' />
109+
<Picker.Item label='1.19.1/1.19.2' value='1.19.1' />
110+
<Picker.Item label='1.19' value='1.19' />
111+
<Picker.Item label='1.18.2' value='1.18.2' />
112+
<Picker.Item label='1.18/1.18.1' value='1.18' />
113+
<Picker.Item label='1.17.1' value='1.17.1' />
114+
<Picker.Item label='1.17' value='1.17' />
115+
<Picker.Item label='1.16.4/1.16.5' value='1.16.4' />
116+
</Picker>
117+
<View style={dialogStyles.modalButtons}>
118+
{typeof editServerDialogOpen === 'string' && (
119+
<Pressable
120+
onPress={handleDeleteServer}
121+
android_ripple={{ color: '#aaa' }}
122+
style={dialogStyles.modalButton}
123+
>
124+
<Text style={styles.deleteServerButtonText}>DELETE</Text>
125+
</Pressable>
126+
)}
127+
<View style={globalStyle.flexSpacer} />
128+
<Pressable
129+
onPress={closeDialog}
130+
android_ripple={{ color: '#aaa' }}
131+
style={dialogStyles.modalButton}
132+
>
133+
<Text style={modalButtonCancelText}>CANCEL</Text>
134+
</Pressable>
135+
<Pressable
136+
onPress={handleEditServer}
137+
android_ripple={{ color: '#aaa' }}
138+
style={dialogStyles.modalButton}
139+
>
140+
<Text style={dialogStyles.modalButtonText}>
141+
{typeof editServerDialogOpen === 'string' ? 'EDIT' : 'ADD'}
142+
</Text>
143+
</Pressable>
144+
</View>
145+
</Dialog>
146+
)
147+
}
148+
149+
const styles = StyleSheet.create({
150+
addServerPickerDark: { color: '#ffffff' },
151+
addServerPicker: { color: '#000000' },
152+
deleteServerButtonText: { color: '#ff0000', fontWeight: 'bold' }
153+
})
154+
155+
export default EditServerDialog
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React from 'react'
2+
import {
3+
StyleSheet,
4+
View,
5+
Image,
6+
Pressable,
7+
ActivityIndicator,
8+
Platform
9+
} from 'react-native'
10+
11+
import Text from '../Text'
12+
import ElevatedView from '../ElevatedView'
13+
import { LegacyPing, Ping } from '../../minecraft/pingServer'
14+
import {
15+
ChatToJsx,
16+
lightColorMap,
17+
mojangColorMap
18+
} from '../../minecraft/chatToJsx'
19+
20+
const ServerDisplay = ({
21+
ping,
22+
server,
23+
darkMode,
24+
connectToServer,
25+
openEditServerDialog
26+
}: {
27+
ping: false | LegacyPing | Ping | null | undefined
28+
server: string
29+
darkMode: boolean
30+
connectToServer: (server: string) => void
31+
openEditServerDialog: (server: string) => void
32+
}) => (
33+
<ElevatedView style={styles.serverView}>
34+
<Pressable
35+
onPress={() => connectToServer(server)}
36+
onLongPress={() => openEditServerDialog(server)}
37+
android_ripple={{ color: '#aaa' }}
38+
style={styles.serverPressable}
39+
>
40+
{ping ? (
41+
<Image
42+
source={
43+
(ping as Ping).favicon
44+
? { uri: (ping as Ping).favicon }
45+
: require('../../assets/pack.png')
46+
}
47+
style={styles.serverImage}
48+
/>
49+
) : (
50+
<View style={styles.serverLoading}>
51+
<ActivityIndicator
52+
color='#00aaff'
53+
size={Platform.select<number | 'large'>({
54+
android: 48,
55+
default: 'large'
56+
})}
57+
/>
58+
</View>
59+
)}
60+
<View style={styles.serverContent}>
61+
<Text style={styles.serverName}>{server.trim()}</Text>
62+
{ping ? (
63+
<>
64+
<Text style={styles.serverPlayers}>
65+
{(ping as Ping).players?.online ?? (ping as LegacyPing).online}/
66+
{(ping as Ping).players?.max ?? (ping as LegacyPing).maxPlayers}{' '}
67+
players online | Ping: {ping.ping}ms
68+
</Text>
69+
<ChatToJsx
70+
chat={(ping as Ping).description ?? (ping as LegacyPing).motd}
71+
component={Text}
72+
colorMap={darkMode ? mojangColorMap : lightColorMap}
73+
componentProps={{ styles: styles.serverDescription }}
74+
trim
75+
/>
76+
</>
77+
) : (
78+
<Text style={styles.serverDescription}>
79+
{ping === null
80+
? 'An error occurred when pinging server.'
81+
: ping === false
82+
? 'No route to host!'
83+
: 'Pinging...'}
84+
</Text>
85+
)}
86+
</View>
87+
</Pressable>
88+
</ElevatedView>
89+
)
90+
91+
const styles = StyleSheet.create({
92+
serverView: { marginBottom: 12 },
93+
serverPressable: { padding: 8, flexDirection: 'row' },
94+
serverImage: { resizeMode: 'contain', padding: 4, height: 72, width: 72 },
95+
serverLoading: {
96+
justifyContent: 'center',
97+
alignItems: 'center',
98+
padding: 4
99+
// height: 72,
100+
// width: 72,
101+
},
102+
serverContent: { marginLeft: 8, flex: 2 },
103+
serverName: { fontSize: 20, fontWeight: 'bold' },
104+
serverPlayers: { fontSize: 12, fontWeight: 'bold' },
105+
serverDescription: { fontSize: 14 }
106+
})
107+
108+
export default ServerDisplay

0 commit comments

Comments
 (0)