5a8b82a12e
* bump to react 18 and install react-router-dom * Upgrade to react 18 root * update vite * add cs api's * convert state/auth to ts * add client config context * add auto discovery context * add spec version context * add auth flow context * add background dot pattern css * add promise utils * init url based routing * update auth route server path as effect * add auth server hook * always use server from discovery info in context * login - WIP * upgrade jotai to v2 * add atom with localStorage util * add multi account sessions atom * add default IGNORE res to auto discovery * add error type in async callback hook * handle password login error * fix async callback hook * allow password login * Show custom server not allowed error in mxId login * add sso login component * add token login * fix hardcoded m.login.password in login func * update server input on url change * Improve sso login labels * update folds * fix async callback batching state update in safari * wrap async callback set state in queueMicrotask * wip * wip - register * arrange auth file structure * add error codes * extract filed error component form password login * add register util function * handle register flow - WIP * update unsupported auth flow method reasons * improve password input styles * Improve UIA flow next stage calculation complete stages can have any order so we will look for first stage which is not in completed * process register UIA flow stages * Extract register UIA stages component * improve register error messages * add focus trap & step count in UIA stages * add reset password path and path utils * add path with origin hook * fix sso redirect url * rename register token query param to token * restyle auth screen header * add reset password component - WIP * add reset password form * add netlify rewrites * fix netlify file indentation * test netlify redirect * fix vite to include netlify toml * add more netlify redirects * add splat to public and assets path * fix vite base name * add option to use hash router in config and remove appVersion * add splash screen component * add client config loading and error screen * fix server picker bug * fix reset password email input type * make auth page small screen responsive * fix typo in reset password screen
141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import React, {
|
|
ChangeEventHandler,
|
|
KeyboardEventHandler,
|
|
MouseEventHandler,
|
|
useEffect,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
import {
|
|
Header,
|
|
Icon,
|
|
IconButton,
|
|
Icons,
|
|
Input,
|
|
Menu,
|
|
MenuItem,
|
|
PopOut,
|
|
Text,
|
|
config,
|
|
} from 'folds';
|
|
import FocusTrap from 'focus-trap-react';
|
|
|
|
import { useDebounce } from '../../hooks/useDebounce';
|
|
|
|
export function ServerPicker({
|
|
server,
|
|
serverList,
|
|
allowCustomServer,
|
|
onServerChange,
|
|
}: {
|
|
server: string;
|
|
serverList: string[];
|
|
allowCustomServer?: boolean;
|
|
onServerChange: (server: string) => void;
|
|
}) {
|
|
const [serverMenu, setServerMenu] = useState(false);
|
|
const serverInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
useEffect(() => {
|
|
// sync input with it outside server changes
|
|
if (serverInputRef.current && serverInputRef.current.value !== server) {
|
|
serverInputRef.current.value = server;
|
|
}
|
|
}, [server]);
|
|
|
|
const debounceServerSelect = useDebounce(onServerChange, { wait: 700 });
|
|
|
|
const handleServerChange: ChangeEventHandler<HTMLInputElement> = (evt) => {
|
|
const inputServer = evt.target.value.trim();
|
|
if (inputServer) debounceServerSelect(inputServer);
|
|
};
|
|
|
|
const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (evt) => {
|
|
if (evt.key === 'ArrowDown') {
|
|
evt.preventDefault();
|
|
setServerMenu(true);
|
|
}
|
|
if (evt.key === 'Enter') {
|
|
evt.preventDefault();
|
|
const inputServer = evt.currentTarget.value.trim();
|
|
if (inputServer) onServerChange(inputServer);
|
|
}
|
|
};
|
|
|
|
const handleServerSelect: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
|
const selectedServer = evt.currentTarget.getAttribute('data-server');
|
|
if (selectedServer) {
|
|
onServerChange(selectedServer);
|
|
}
|
|
setServerMenu(false);
|
|
};
|
|
|
|
return (
|
|
<Input
|
|
ref={serverInputRef}
|
|
style={{ paddingRight: config.space.S200 }}
|
|
variant={allowCustomServer ? 'Background' : 'Surface'}
|
|
outlined
|
|
defaultValue={server}
|
|
onChange={handleServerChange}
|
|
onKeyDown={handleKeyDown}
|
|
size="500"
|
|
readOnly={!allowCustomServer}
|
|
onClick={allowCustomServer ? undefined : () => setServerMenu(true)}
|
|
after={
|
|
serverList.length === 0 || (serverList.length === 1 && !allowCustomServer) ? undefined : (
|
|
<PopOut
|
|
open={serverMenu}
|
|
position="Bottom"
|
|
align="End"
|
|
offset={4}
|
|
content={
|
|
<FocusTrap
|
|
focusTrapOptions={{
|
|
initialFocus: false,
|
|
onDeactivate: () => setServerMenu(false),
|
|
clickOutsideDeactivates: true,
|
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
|
}}
|
|
>
|
|
<Menu>
|
|
<Header size="300" style={{ padding: `0 ${config.space.S200}` }}>
|
|
<Text size="L400">Homeserver List</Text>
|
|
</Header>
|
|
<div style={{ padding: config.space.S100, paddingTop: 0 }}>
|
|
{serverList?.map((serverName) => (
|
|
<MenuItem
|
|
key={serverName}
|
|
radii="300"
|
|
aria-pressed={serverName === server}
|
|
data-server={serverName}
|
|
onClick={handleServerSelect}
|
|
>
|
|
<Text>{serverName}</Text>
|
|
</MenuItem>
|
|
))}
|
|
</div>
|
|
</Menu>
|
|
</FocusTrap>
|
|
}
|
|
>
|
|
{(anchorRef) => (
|
|
<IconButton
|
|
ref={anchorRef}
|
|
onClick={() => setServerMenu(true)}
|
|
variant={allowCustomServer ? 'Background' : 'Surface'}
|
|
size="300"
|
|
aria-pressed={serverMenu}
|
|
radii="300"
|
|
>
|
|
<Icon src={Icons.ChevronBottom} />
|
|
</IconButton>
|
|
)}
|
|
</PopOut>
|
|
)
|
|
}
|
|
/>
|
|
);
|
|
}
|