Skip to content
This repository has been archived by the owner on Sep 10, 2024. It is now read-only.

Commit

Permalink
Split out password change success to its own route
Browse files Browse the repository at this point in the history
  • Loading branch information
reivilibre committed Jun 24, 2024
1 parent a626ae2 commit e34f212
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 119 deletions.
11 changes: 11 additions & 0 deletions frontend/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Route as rootRoute } from './routes/__root'
import { Route as ResetCrossSigningImport } from './routes/reset-cross-signing'
import { Route as AccountImport } from './routes/_account'
import { Route as AccountIndexImport } from './routes/_account.index'
import { Route as PasswordChangesuccessImport } from './routes/password.change_success'
import { Route as PasswordChangeImport } from './routes/password.change'
import { Route as DevicesSplatImport } from './routes/devices.$'
import { Route as ClientsIdImport } from './routes/clients.$id'
Expand All @@ -39,6 +40,11 @@ const AccountIndexRoute = AccountIndexImport.update({
getParentRoute: () => AccountRoute,
} as any)

const PasswordChangesuccessRoute = PasswordChangesuccessImport.update({
path: '/password/change_success',
getParentRoute: () => rootRoute,
} as any)

const PasswordChangeRoute = PasswordChangeImport.update({
path: '/password/change',
getParentRoute: () => rootRoute,
Expand Down Expand Up @@ -98,6 +104,10 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof PasswordChangeImport
parentRoute: typeof rootRoute
}
'/password/change_success': {
preLoaderRoute: typeof PasswordChangesuccessImport
parentRoute: typeof rootRoute
}
'/_account/': {
preLoaderRoute: typeof AccountIndexImport
parentRoute: typeof AccountImport
Expand Down Expand Up @@ -134,6 +144,7 @@ export const routeTree = rootRoute.addChildren([
ClientsIdRoute,
DevicesSplatRoute,
PasswordChangeRoute,
PasswordChangesuccessRoute,
EmailsIdVerifyRoute,
])

Expand Down
228 changes: 109 additions & 119 deletions frontend/src/routes/password.change.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import LoadingSpinner from "../components/LoadingSpinner";
import PageHeading from "../components/PageHeading";
import { graphql } from "../gql";
import { SetPasswordStatus } from "../gql/graphql";

Check failure on line 29 in frontend/src/routes/password.change.tsx

View workflow job for this annotation

GitHub Actions / Check frontend style

There should be at least one empty line between import groups
import { useRouter } from "@tanstack/react-router";

Check failure on line 30 in frontend/src/routes/password.change.tsx

View workflow job for this annotation

GitHub Actions / Check frontend style

`@tanstack/react-router` import should occur before import of `@vector-im/compound-design-tokens/icons/check-circle-solid.svg?react`

Check warning on line 30 in frontend/src/routes/password.change.tsx

View workflow job for this annotation

GitHub Actions / Check frontend style

'/home/runner/work/matrix-authentication-service/matrix-authentication-service/frontend/node_modules/@tanstack/react-router/dist/esm/index.d.ts' imported multiple times

const CURRENT_VIEWER_QUERY = graphql(/* GraphQL */ `
query CurrentViewerQuery {
Expand Down Expand Up @@ -74,6 +75,7 @@ export const Route = createFileRoute("/password/change")({
function ChangePassword(): React.ReactNode {
const { t } = useTranslation();
const [viewer] = useQuery({ query: CURRENT_VIEWER_QUERY });
const router = useRouter();
if (viewer.error) throw viewer.error;
if (viewer.data?.viewer.__typename !== "User") throw notFound();
const userId = viewer.data.viewer.id;
Expand All @@ -84,7 +86,7 @@ function ChangePassword(): React.ReactNode {

const [result, changePassword] = useMutation(CHANGE_PASSWORD_MUTATION);

const onSubmit = (event: FormEvent<HTMLFormElement>): void => {
const onSubmit = async (event: FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();

const formData = new FormData(event.currentTarget);
Expand All @@ -97,13 +99,15 @@ function ChangePassword(): React.ReactNode {
throw new Error("passwords mismatch; this should be checked by the form");
}

changePassword({ userId, oldPassword, newPassword });
const response = await changePassword({ userId, oldPassword, newPassword });

if (response.data?.setPassword.status === SetPasswordStatus.Allowed) {
router.navigate({ to: "/password/change_success" });
}
};

const success =
result.data && result.data.setPassword.status == SetPasswordStatus.Allowed;
const handleableError =
result.data && result.data.setPassword.status != SetPasswordStatus.Allowed;
result.data && result.data.setPassword.status !== SetPasswordStatus.Allowed;
const unhandleableError = result.error !== undefined;

const errorMsg: string | undefined = ((): string | undefined => {
Expand Down Expand Up @@ -148,108 +152,104 @@ function ChangePassword(): React.ReactNode {
which could be logged, if for some reason the event handler fails.
*/}

{!success && (
<>
<Form.Field
name="current_password"
serverInvalid={
result.data?.setPassword.status ===
SetPasswordStatus.WrongPassword
}
>
<Form.Label>
{t("frontend.password_change.current_password_label")}
</Form.Label>

<Form.PasswordControl
required
autoComplete="current-password"
ref={currentPasswordRef}
/>

<Form.ErrorMessage match="valueMissing">
{t("frontend.errors.field_required")}
</Form.ErrorMessage>

{result.data &&
result.data.setPassword.status ==
SetPasswordStatus.WrongPassword && (
<Form.ErrorMessage>
{t(
"frontend.password_change.failure.description.wrong_password",
)}
</Form.ErrorMessage>
<Form.Field
name="current_password"
serverInvalid={
result.data?.setPassword.status ===
SetPasswordStatus.WrongPassword
}
>
<Form.Label>
{t("frontend.password_change.current_password_label")}
</Form.Label>

<Form.PasswordControl
required
autoComplete="current-password"
ref={currentPasswordRef}
/>

<Form.ErrorMessage match="valueMissing">
{t("frontend.errors.field_required")}
</Form.ErrorMessage>

{result.data &&
result.data.setPassword.status ===
SetPasswordStatus.WrongPassword && (
<Form.ErrorMessage>
{t(
"frontend.password_change.failure.description.wrong_password",
)}
</Form.Field>

<Separator />

<Form.Field name="new_password">
<Form.Label>
{t("frontend.password_change.new_password_label")}
</Form.Label>

<Form.PasswordControl
required
autoComplete="new-password"
ref={newPasswordRef}
onBlur={() =>
newPasswordAgainRef.current!.value &&
newPasswordAgainRef.current!.reportValidity()
}
/>

{/* TODO Show a password bar. https://github.com/matrix-org/matrix-authentication-service/issues/2854 */}

<Form.ErrorMessage match="valueMissing">
{t("frontend.errors.field_required")}
</Form.ErrorMessage>

{result.data &&
result.data.setPassword.status ==
SetPasswordStatus.InvalidNewPassword && (
<Form.ErrorMessage>
{t(
"frontend.password_change.failure.description.invalid_new_password",
)}
</Form.ErrorMessage>
)}
</Form.Field>

<Separator />

<Form.Field name="new_password">
<Form.Label>
{t("frontend.password_change.new_password_label")}
</Form.Label>

<Form.PasswordControl
required
autoComplete="new-password"
ref={newPasswordRef}
onBlur={() =>
newPasswordAgainRef.current!.value &&
newPasswordAgainRef.current!.reportValidity()
}
/>

{/* TODO Show a password bar. https://github.com/matrix-org/matrix-authentication-service/issues/2854 */}

<Form.ErrorMessage match="valueMissing">
{t("frontend.errors.field_required")}
</Form.ErrorMessage>

{result.data &&
result.data.setPassword.status ===
SetPasswordStatus.InvalidNewPassword && (
<Form.ErrorMessage>
{t(
"frontend.password_change.failure.description.invalid_new_password",
)}
</Form.Field>

<Form.Field name="new_password_again">
{/*
TODO This field has validation defects,
some caused by Radix-UI upstream bugs.
https://github.com/matrix-org/matrix-authentication-service/issues/2855
*/}
<Form.Label>
{t("frontend.password_change.new_password_again_label")}
</Form.Label>

<Form.PasswordControl
required
ref={newPasswordAgainRef}
autoComplete="new-password"
/>

<Form.ErrorMessage match="valueMissing">
{t("frontend.errors.field_required")}
</Form.ErrorMessage>

<Form.ErrorMessage
match={(v, form) => v !== form.get("new_password")}
>
{t("frontend.password_change.passwords_no_match")}
</Form.ErrorMessage>
)}
</Form.Field>

<Form.Field name="new_password_again">
{/*
TODO This field has validation defects,
some caused by Radix-UI upstream bugs.
https://github.com/matrix-org/matrix-authentication-service/issues/2855
*/}
<Form.Label>
{t("frontend.password_change.new_password_again_label")}
</Form.Label>

<Form.PasswordControl
required
ref={newPasswordAgainRef}
autoComplete="new-password"
/>

<Form.ErrorMessage match="valueMissing">
{t("frontend.errors.field_required")}
</Form.ErrorMessage>

<Form.ErrorMessage
match={(v, form) => v !== form.get("new_password")}
>
{t("frontend.password_change.passwords_no_match")}
</Form.ErrorMessage>

<Form.HelpMessage match="valid">
{/* TODO Use SuccessMessage once ready. https://github.com/matrix-org/matrix-authentication-service/issues/2856 */}
<IconCheckCircleSolid />
{t("frontend.password_change.passwords_match")}
</Form.HelpMessage>
</Form.Field>
</>
)}
<Form.HelpMessage match="valid">
{/* TODO Use SuccessMessage once ready. https://github.com/matrix-org/matrix-authentication-service/issues/2856 */}
<IconCheckCircleSolid />
{t("frontend.password_change.passwords_match")}
</Form.HelpMessage>
</Form.Field>

{unhandleableError && (
<Alert
Expand All @@ -259,22 +259,12 @@ function ChangePassword(): React.ReactNode {
{t("frontend.password_change.failure.description.unspecified")}
</Alert>
)}
{!success && (
<>
<Form.Submit kind="primary" disabled={result.fetching}>
{!!result.fetching && <LoadingSpinner inline />}
{t("action.save")}
</Form.Submit>
</>
)}
{success && (
<Alert
type="success"
title={t("frontend.password_change.success.title")}
>
{t("frontend.password_change.success.description")}
</Alert>
)}

<Form.Submit kind="primary" disabled={result.fetching}>
{!!result.fetching && <LoadingSpinner inline />}
{t("action.save")}
</Form.Submit>

{handleableError && (
<Alert
type="critical"
Expand All @@ -285,7 +275,7 @@ function ChangePassword(): React.ReactNode {
)}

<ButtonLink to="/" kind="tertiary">
{(result.data && t("action.back")) || t("action.cancel")}
{t("action.cancel")}
</ButtonLink>
</Form.Root>
</BlockList>
Expand Down
Loading

0 comments on commit e34f212

Please sign in to comment.