Files
cinny/src/app/pages/auth/login/Login.tsx
T
Lotus Bot 689805ca6e fix(a11y): semantic headings, htmlFor/id associations, remove duplicate aria-labels
H-tag: add as=h1/h2 to dialog/UIA/auth headings (21 components)
Label: add htmlFor/id to PasswordRegisterForm (5 pairs) and PasswordResetForm (3 pairs)
Dupe: remove duplicate aria-label from Controls.tsx screenshare button, MembersDrawer, Members, RoomInput

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 15:36:59 -04:00

100 lines
3.6 KiB
TypeScript

import React, { useMemo } from 'react';
import { Box, Text, color } from 'folds';
import { Link, useSearchParams } from 'react-router-dom';
import { SSOAction } from 'matrix-js-sdk';
import { useAuthFlows } from '../../../hooks/useAuthFlows';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { useParsedLoginFlows } from '../../../hooks/useParsedLoginFlows';
import { PasswordLoginForm } from './PasswordLoginForm';
import { SSOLogin } from '../SSOLogin';
import { TokenLogin } from './TokenLogin';
import { OrDivider } from '../OrDivider';
import { getLoginPath, getRegisterPath, withSearchParam } from '../../pathUtils';
import { usePathWithOrigin } from '../../../hooks/usePathWithOrigin';
import { LoginPathSearchParams } from '../../paths';
import { useClientConfig } from '../../../hooks/useClientConfig';
const getLoginTokenSearchParam = () => {
// when using hasRouter query params in existing route
// gets ignored by react-router, so we need to read it ourself
// we only need to read loginToken as it's the only param that
// is provided by external entity. example: SSO login
const parmas = new URLSearchParams(window.location.search);
const loginToken = parmas.get('loginToken');
return loginToken ?? undefined;
};
const useLoginSearchParams = (searchParams: URLSearchParams): LoginPathSearchParams =>
useMemo(
() => ({
username: searchParams.get('username') ?? undefined,
email: searchParams.get('email') ?? undefined,
loginToken: searchParams.get('loginToken') ?? undefined,
}),
[searchParams]
);
export function Login() {
const server = useAuthServer();
const { hashRouter } = useClientConfig();
const { loginFlows } = useAuthFlows();
const [searchParams] = useSearchParams();
const loginSearchParams = useLoginSearchParams(searchParams);
const ssoRedirectUrl = usePathWithOrigin(getLoginPath(server));
const loginTokenForHashRouter = getLoginTokenSearchParam();
const absoluteLoginPath = usePathWithOrigin(getLoginPath(server));
if (hashRouter?.enabled && loginTokenForHashRouter) {
window.location.replace(
withSearchParam(absoluteLoginPath, {
loginToken: loginTokenForHashRouter,
})
);
}
const parsedFlows = useParsedLoginFlows(loginFlows.flows);
return (
<Box direction="Column" gap="500">
<Text as="h1" size="H2" priority="400">
Login
</Text>
{parsedFlows.token && loginSearchParams.loginToken && (
<TokenLogin token={loginSearchParams.loginToken} />
)}
{parsedFlows.password && (
<>
<PasswordLoginForm
defaultUsername={loginSearchParams.username}
defaultEmail={loginSearchParams.email}
/>
<span data-spacing-node />
{parsedFlows.sso && <OrDivider />}
</>
)}
{parsedFlows.sso && (
<>
<SSOLogin
providers={parsedFlows.sso.identity_providers}
redirectUrl={ssoRedirectUrl}
action={SSOAction.LOGIN}
saveScreenSpace={parsedFlows.password !== undefined}
/>
<span data-spacing-node />
</>
)}
{!parsedFlows.password && !parsedFlows.sso && (
<>
<Text style={{ color: color.Critical.Main }}>
{`This client does not support login on "${server}" homeserver. Password and SSO based login method not found.`}
</Text>
<span data-spacing-node />
</>
)}
<Text align="Center">
Do not have an account? <Link to={getRegisterPath(server)}>Register</Link>
</Text>
</Box>
);
}