pages235/src/react/AddServerOrConnect.tsx
Vitaly Turovsky a0a2c628b4 fix: preserve server selection after going back to the servers list (eg from edit modal)
fix: was not possible to remove saved MS account by clicking on the profile picture
fix: highlight active zone for interaction to avoid UI confusion
fix: save the proxy to memory immedieately after editing it in the servers list screen
2024-11-14 13:41:28 +03:00

175 lines
7.4 KiB
TypeScript

import React from 'react'
import Screen from './Screen'
import Input from './Input'
import Button from './Button'
import SelectGameVersion from './SelectGameVersion'
import { useIsSmallWidth } from './simpleHooks'
export interface BaseServerInfo {
ip: string
name?: string
versionOverride?: string
proxyOverride?: string
usernameOverride?: string
/** Username or always use new if true */
authenticatedAccountOverride?: string | true
}
interface Props {
onBack: () => void
onConfirm: (info: BaseServerInfo) => void
title?: string
initialData?: BaseServerInfo
parseQs?: boolean
onQsConnect?: (server: BaseServerInfo) => void
placeholders?: Pick<BaseServerInfo, 'proxyOverride' | 'usernameOverride'>
accounts?: string[]
authenticatedAccounts?: number
versions?: string[]
}
const ELEMENTS_WIDTH = 190
export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, placeholders, accounts, versions, authenticatedAccounts }: Props) => {
const qsParams = parseQs ? new URLSearchParams(window.location.search) : undefined
const qsParamName = qsParams?.get('name')
const qsParamIp = qsParams?.get('ip')
const qsParamVersion = qsParams?.get('version')
const qsParamProxy = qsParams?.get('proxy')
const qsParamUsername = qsParams?.get('username')
const qsParamLockConnect = qsParams?.get('lockConnect')
const qsIpParts = qsParamIp?.split(':')
const ipParts = initialData?.ip.split(':')
const [serverName, setServerName] = React.useState(initialData?.name ?? qsParamName ?? '')
const [serverIp, setServerIp] = React.useState(ipParts?.[0] ?? qsIpParts?.[0] ?? '')
const [serverPort, setServerPort] = React.useState(ipParts?.[1] ?? qsIpParts?.[1] ?? '')
const [versionOverride, setVersionOverride] = React.useState(initialData?.versionOverride ?? /* legacy */ initialData?.['version'] ?? qsParamVersion ?? '')
const [proxyOverride, setProxyOverride] = React.useState(initialData?.proxyOverride ?? qsParamProxy ?? '')
const [usernameOverride, setUsernameOverride] = React.useState(initialData?.usernameOverride ?? qsParamUsername ?? '')
const lockConnect = qsParamLockConnect === 'true'
const smallWidth = useIsSmallWidth()
const initialAccount = initialData?.authenticatedAccountOverride
const [accountIndex, setAccountIndex] = React.useState(initialAccount === true ? -2 : initialAccount ? (accounts?.includes(initialAccount) ? accounts.indexOf(initialAccount) : -2) : -1)
const freshAccount = accountIndex === -2
const noAccountSelected = accountIndex === -1
const authenticatedAccountOverride = noAccountSelected ? undefined : freshAccount ? true : accounts?.[accountIndex]
let ipFinal = serverIp.includes(':') ? serverIp : `${serverIp}:${serverPort}`
ipFinal = ipFinal.replace(/:$/, '')
const commonUseOptions: BaseServerInfo = {
name: serverName,
ip: ipFinal,
versionOverride: versionOverride || undefined,
proxyOverride: proxyOverride || undefined,
usernameOverride: usernameOverride || undefined,
authenticatedAccountOverride,
}
return <Screen title={qsParamIp ? 'Connect to Server' : title} backdrop>
<form
style={{
display: 'flex',
flexDirection: 'column',
height: '100%'
}}
onSubmit={(e) => {
e.preventDefault()
onConfirm(commonUseOptions)
}}
>
<div style={{
display: 'grid',
gap: 3,
gridTemplateColumns: smallWidth ? '1fr' : '1fr 1fr'
}}
>
{!lockConnect && <>
<div style={{ gridColumn: smallWidth ? '' : 'span 2', display: 'flex', justifyContent: 'center' }}>
<InputWithLabel label="Server Name" value={serverName} onChange={({ target: { value } }) => setServerName(value)} placeholder='Defaults to IP' />
</div>
</>}
<InputWithLabel required label="Server IP" value={serverIp} disabled={lockConnect && qsIpParts?.[0] !== null} onChange={({ target: { value } }) => setServerIp(value)} />
<InputWithLabel label="Server Port" value={serverPort} disabled={lockConnect && qsIpParts?.[1] !== null} onChange={({ target: { value } }) => setServerPort(value)} placeholder='25565' />
<div style={{ gridColumn: smallWidth ? '' : 'span 2' }}>Overrides:</div>
<div style={{
display: 'flex',
flexDirection: 'column',
}}>
<label style={{ fontSize: 12, marginBottom: 1, color: 'lightgray' }}>Version Override</label>
<SelectGameVersion
selected={{ value: versionOverride, label: versionOverride }}
versions={versions?.map(v => { return { value: v, label: v } }) ?? []}
onChange={(value) => {
setVersionOverride(value)
}}
placeholder="Optional, but recommended to specify"
disabled={lockConnect && qsParamVersion !== null}
/>
</div>
<InputWithLabel label="Proxy Override" value={proxyOverride} disabled={lockConnect && qsParamProxy !== null} onChange={({ target: { value } }) => setProxyOverride(value)} placeholder={placeholders?.proxyOverride} />
<InputWithLabel label="Username Override" value={usernameOverride} disabled={!noAccountSelected || lockConnect && qsParamUsername !== null} onChange={({ target: { value } }) => setUsernameOverride(value)} placeholder={placeholders?.usernameOverride} />
<label style={{
display: 'flex',
flexDirection: 'column',
}}
>
<span style={{ fontSize: 12, marginBottom: 1, color: 'lightgray' }}>Account Override</span>
<select
onChange={({ target: { value } }) => setAccountIndex(Number(value))}
style={{
background: 'gray',
color: 'white',
height: 20,
fontSize: 13,
}}
defaultValue={initialAccount === true ? -2 : initialAccount === undefined ? -1 : (fallbackIfNotFound((accounts ?? []).indexOf(initialAccount)) ?? -2)}
>
<option value={-1}>Offline Account (Username)</option>
{accounts?.map((account, i) => <option key={i} value={i}>{account} (Logged In)</option>)}
<option value={-2}>Any other MS account</option>
</select>
</label>
{!lockConnect && <>
<ButtonWrapper onClick={() => {
onBack()
}}>Cancel</ButtonWrapper>
<ButtonWrapper type='submit'>Save</ButtonWrapper>
</>}
{qsParamIp && <div style={{ gridColumn: smallWidth ? '' : 'span 2', display: 'flex', justifyContent: 'center' }}>
<ButtonWrapper
data-test-id='connect-qs'
onClick={() => {
onQsConnect?.(commonUseOptions)
}}
>Connect</ButtonWrapper>
</div>}
</div>
</form>
</Screen>
}
const ButtonWrapper = ({ ...props }: React.ComponentProps<typeof Button>) => {
props.style ??= {}
props.style.width = ELEMENTS_WIDTH
return <Button {...props} />
}
const InputWithLabel = ({ label, span, ...props }: React.ComponentProps<typeof Input> & { label, span? }) => {
return <div style={{
display: 'flex',
flexDirection: 'column',
gridRow: span ? 'span 2 / span 2' : undefined,
}}
>
<label style={{ fontSize: 12, marginBottom: 1, color: 'lightgray' }}>{label}</label>
<Input rootStyles={{ width: ELEMENTS_WIDTH }} {...props} />
</div>
}
const fallbackIfNotFound = (index: number) => (index === -1 ? undefined : index)