Skip to content

Commit

Permalink
fix: preserve server selection after going back to the servers list (…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
zardoy committed Nov 14, 2024
1 parent e28608f commit a0a2c62
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 79 deletions.
8 changes: 4 additions & 4 deletions src/react/AddServerOrConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ interface Props {
initialData?: BaseServerInfo
parseQs?: boolean
onQsConnect?: (server: BaseServerInfo) => void
defaults?: Pick<BaseServerInfo, 'proxyOverride' | 'usernameOverride'>
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, defaults, accounts, versions, authenticatedAccounts }: Props) => {
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')
Expand Down Expand Up @@ -111,8 +111,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ
/>
</div>

<InputWithLabel label="Proxy Override" value={proxyOverride} disabled={lockConnect && qsParamProxy !== null} onChange={({ target: { value } }) => setProxyOverride(value)} placeholder={defaults?.proxyOverride} />
<InputWithLabel label="Username Override" value={usernameOverride} disabled={!noAccountSelected || lockConnect && qsParamUsername !== null} onChange={({ target: { value } }) => setUsernameOverride(value)} placeholder={defaults?.usernameOverride} />
<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',
Expand Down
17 changes: 13 additions & 4 deletions src/react/SelectOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const state = proxy({
title: '',
options: [] as string[],
showCancel: true,
behavior: 'resolve-close' as 'resolve-close' | 'close-resolve',
})

let resolve
Expand All @@ -32,18 +33,26 @@ export default () => {
const isModalActive = useIsModalActive('general-select')
if (!isModalActive) return

const resolveClose = (value: string | undefined) => {
if (state.behavior === 'resolve-close') {
resolve(value)
hideCurrentModal()
} else {
hideCurrentModal()
resolve(value)
}
}

return <Screen title={title} backdrop>
{options.map(option => <Button
key={option} onClick={() => {
hideCurrentModal()
resolve(option)
resolveClose(option)
}}
>{option}
</Button>)}
{showCancel && <Button
style={{ marginTop: 30 }} onClick={() => {
hideCurrentModal()
resolve(undefined)
resolveClose(undefined)
}}
>Cancel
</Button>}
Expand Down
37 changes: 33 additions & 4 deletions src/react/ServersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Props extends React.ComponentProps<typeof Singleplayer> {
username: string
setUsername: (username: string) => void
onProfileClick?: () => void
setQuickConnectIp?: (ip: string) => void
}

export interface SavedProxiesLocalStorage {
Expand All @@ -29,7 +30,7 @@ type ProxyStatusResult = {
status: 'success' | 'error' | 'unknown'
}

export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer, username, setUsername, onProfileClick, ...props }: Props) => {
export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer, username, setUsername, onProfileClick, setQuickConnectIp, ...props }: Props) => {
const [proxies, setProxies] = React.useState(initialProxies)

const updateProxies = (newData: SavedProxiesLocalStorage) => {
Expand All @@ -39,6 +40,17 @@ export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer,

const [serverIp, setServerIp] = React.useState('')
const [save, setSave] = React.useState(true)
const [activeHighlight, setActiveHighlight] = React.useState(undefined as 'quick-connect' | 'server-list' | undefined)

const getActiveHighlightStyles = (type: typeof activeHighlight) => {
const styles: React.CSSProperties = {
transition: 'filter 0.2s',
}
if (activeHighlight && activeHighlight !== type) {
styles.filter = 'brightness(0.7)'
}
return styles
}

return <Singleplayer
{...props}
Expand Down Expand Up @@ -66,9 +78,22 @@ export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer,
})
}}
>
<div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
<div
style={{ display: 'flex', gap: 5, alignItems: 'center', ...getActiveHighlightStyles('quick-connect') }}
className='quick-connect-row'
onMouseEnter={() => setActiveHighlight('quick-connect')}
onMouseLeave={() => setActiveHighlight(undefined)}
>
{/* todo history */}
<Input required placeholder='Quick Connect IP (:version)' value={serverIp} onChange={({ target: { value } }) => setServerIp(value)} />
<Input
required
placeholder='Quick Connect IP (:version)'
value={serverIp}
onChange={({ target: { value } }) => {
setQuickConnectIp?.(value)
setServerIp(value)
}}
/>
<label style={{ fontSize: 10, display: 'flex', alignItems: 'center', gap: 5, height: '100%', marginTop: '-1px' }}>
<input
type='checkbox' checked={save}
Expand Down Expand Up @@ -108,6 +133,10 @@ export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer,
}
props.onWorldAction?.(action, serverName)
}}
setListHovered={(hovered) => {
setActiveHighlight(hovered ? 'server-list' : undefined)
}}
listStyle={getActiveHighlightStyles('server-list')}
secondRowStyles={getActiveHighlightStyles('server-list')}
/>
}

129 changes: 71 additions & 58 deletions src/react/ServersListProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useEffect, useMemo, useState } from 'react'
import { useUtilsEffect } from '@zardoy/react-util'
import { useSnapshot } from 'valtio'
import { ConnectOptions } from '../connect'
import { hideCurrentModal, miscUiState, showModal } from '../globalState'
import { activeModalStack, hideCurrentModal, miscUiState, showModal } from '../globalState'
import supportedVersions from '../supportedVersions.mjs'
import ServersList from './ServersList'
import AddServerOrConnect, { BaseServerInfo } from './AddServerOrConnect'
Expand Down Expand Up @@ -132,29 +133,31 @@ export const updateAuthenticatedAccountData = (callback: (data: AuthenticatedAcc
// todo move to base
const normalizeIp = (ip: string) => ip.replace(/https?:\/\//, '').replace(/\/(:|$)/, '')

const Inner = () => {
const Inner = ({ hidden }: { hidden?: boolean }) => {
const [proxies, setProxies] = useState<readonly string[]>(localStorage['proxies'] ? JSON.parse(localStorage['proxies']) : getInitialProxies())
const [selectedProxy, setSelectedProxy] = useState(localStorage.getItem('selectedProxy') ?? proxies?.[0] ?? '')
const [serverEditScreen, setServerEditScreen] = useState<StoreServerItem | true | null>(null) // true for add
const [defaultUsername, setDefaultUsername] = useState(localStorage['username'] ?? (`mcrafter${Math.floor(Math.random() * 1000)}`))
const [authenticatedAccounts, setAuthenticatedAccounts] = useState<AuthenticatedAccount[]>(JSON.parse(localStorage['authenticatedAccounts'] || '[]'))
const [defaultUsername, _setDefaultUsername] = useState(localStorage['username'] ?? (`mcrafter${Math.floor(Math.random() * 1000)}`))
const [authenticatedAccounts, _setAuthenticatedAccounts] = useState<AuthenticatedAccount[]>(JSON.parse(localStorage['authenticatedAccounts'] || '[]'))
const [quickConnectIp, setQuickConnectIp] = useState('')

useEffect(() => {
localStorage.setItem('authenticatedAccounts', JSON.stringify(authenticatedAccounts))
}, [authenticatedAccounts])
const setAuthenticatedAccounts = (newState: typeof authenticatedAccounts) => {
_setAuthenticatedAccounts(newState)
localStorage.setItem('authenticatedAccounts', JSON.stringify(newState))
}

useEffect(() => {
localStorage.setItem('username', defaultUsername)
}, [defaultUsername])
const setDefaultUsername = (newState: typeof defaultUsername) => {
_setDefaultUsername(newState)
localStorage.setItem('username', newState)
}

useEffect(() => {
// TODO! do not unmount on connecting screen
// if (proxies.length) {
// localStorage.setItem('proxies', JSON.stringify(proxies))
// }
// if (selectedProxy) {
// localStorage.setItem('selectedProxy', selectedProxy)
// }
if (proxies.length) {
localStorage.setItem('proxies', JSON.stringify(proxies))
}
if (selectedProxy) {
localStorage.setItem('selectedProxy', selectedProxy)
}
}, [proxies])

const [serversList, setServersList] = useState<StoreServerItem[]>(() => getInitialServersList())
Expand Down Expand Up @@ -215,46 +218,46 @@ const Inner = () => {
}
}, [isEditScreenModal])

if (isEditScreenModal) {
return <AddServerOrConnect
defaults={{
proxyOverride: selectedProxy,
usernameOverride: defaultUsername,
}}
parseQs={!serverEditScreen}
onBack={() => {
hideCurrentModal()
}}
onConfirm={(info) => {
if (!serverEditScreen) return
if (serverEditScreen === true) {
const server: StoreServerItem = { ...info, lastJoined: Date.now() } // so it appears first
setServersList(old => [...old, server])
} else {
const index = serversList.indexOf(serverEditScreen)
const { lastJoined } = serversList[index]
serversList[index] = { ...info, lastJoined }
setServersList([...serversList])
}
setServerEditScreen(null)
}}
accounts={authenticatedAccounts.map(a => a.username)}
initialData={!serverEditScreen || serverEditScreen === true ? undefined : serverEditScreen}
onQsConnect={(info) => {
const connectOptions: ConnectOptions = {
username: info.usernameOverride || defaultUsername,
server: normalizeIp(info.ip),
proxy: info.proxyOverride || selectedProxy,
botVersion: info.versionOverride,
ignoreQs: true,
}
dispatchEvent(new CustomEvent('connect', { detail: connectOptions }))
}}
versions={supportedVersions}
/>
}
const editModalJsx = isEditScreenModal ? <AddServerOrConnect
placeholders={{
proxyOverride: selectedProxy,
usernameOverride: defaultUsername,
}}
parseQs={!serverEditScreen}
onBack={() => {
hideCurrentModal()
}}
onConfirm={(info) => {
if (!serverEditScreen) return
if (serverEditScreen === true) {
const server: StoreServerItem = { ...info, lastJoined: Date.now() } // so it appears first
setServersList(old => [...old, server])
} else {
const index = serversList.indexOf(serverEditScreen)
const { lastJoined } = serversList[index]
serversList[index] = { ...info, lastJoined }
setServersList([...serversList])
}
setServerEditScreen(null)
}}
accounts={authenticatedAccounts.map(a => a.username)}
initialData={!serverEditScreen || serverEditScreen === true ? {
ip: quickConnectIp
} : serverEditScreen}
onQsConnect={(info) => {
const connectOptions: ConnectOptions = {
username: info.usernameOverride || defaultUsername,
server: normalizeIp(info.ip),
proxy: info.proxyOverride || selectedProxy,
botVersion: info.versionOverride,
ignoreQs: true,
}
dispatchEvent(new CustomEvent('connect', { detail: connectOptions }))
}}
versions={supportedVersions}
/> : null

return <ServersList
const serversListJsx = <ServersList
joinServer={(overrides, { shouldSave }) => {
const indexOrIp = overrides.ip
let ip = indexOrIp
Expand Down Expand Up @@ -327,10 +330,11 @@ const Inner = () => {
}}
username={defaultUsername}
setUsername={setDefaultUsername}
setQuickConnectIp={setQuickConnectIp}
onProfileClick={async () => {
const username = await showOptionsModal('Select authenticated account to remove', authenticatedAccounts.map(a => a.username))
if (!username) return
setAuthenticatedAccounts(old => old.filter(a => a.username !== username))
setAuthenticatedAccounts(authenticatedAccounts.filter(a => a.username !== username))
}}
onWorldAction={(action, index) => {
const server = serversList[index]
Expand Down Expand Up @@ -372,12 +376,21 @@ const Inner = () => {
setProxies(proxies)
setSelectedProxy(selected)
}}
hidden={hidden}
/>
return <>
{serversListJsx}
{editModalJsx}
</>
}

export default () => {
const modalStack = useSnapshot(activeModalStack)
const hasServersListModal = modalStack.some(x => x.reactType === 'serversList')
const editServerModalActive = useIsModalActive('editServer')
const isServersListModalActive = useIsModalActive('serversList')

const eitherModal = isServersListModalActive || editServerModalActive
return eitherModal ? <Inner /> : null
const render = eitherModal || hasServersListModal
return render ? <Inner hidden={!isServersListModalActive} /> : null
}
Loading

0 comments on commit a0a2c62

Please sign in to comment.