mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-06-06 19:52:17 +00:00
162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import {
|
|
Alert,
|
|
Control,
|
|
Field,
|
|
Label,
|
|
Root,
|
|
Button,
|
|
} from "@vector-im/compound-web";
|
|
import { useAtomValue, useAtom, useSetAtom, atom } from "jotai";
|
|
import { atomFamily } from "jotai/utils";
|
|
import { atomWithMutation } from "jotai-urql";
|
|
import { useState, useEffect, ChangeEventHandler } from "react";
|
|
|
|
import { graphql } from "../../gql";
|
|
import LoadingSpinner from "../LoadingSpinner/LoadingSpinner";
|
|
import { userGreetingFamily } from "../UserGreeting";
|
|
|
|
import styles from "./UserName.module.css";
|
|
|
|
const SET_DISPLAYNAME_MUTATION = graphql(/* GraphQL */ `
|
|
mutation SetDisplayName($userId: ID!, $displayName: String) {
|
|
setDisplayName(input: { userId: $userId, displayName: $displayName }) {
|
|
status
|
|
user {
|
|
id
|
|
matrix {
|
|
displayName
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const setDisplayNameFamily = atomFamily((userId: string) => {
|
|
const setDisplayName = atomWithMutation(SET_DISPLAYNAME_MUTATION);
|
|
|
|
// A proxy atom which pre-sets the id variable in the mutation
|
|
const setDisplayNameAtom = atom(
|
|
(get) => get(setDisplayName),
|
|
(get, set, displayName: string | null) =>
|
|
set(setDisplayName, { userId, displayName }),
|
|
);
|
|
|
|
return setDisplayNameAtom;
|
|
});
|
|
|
|
const getErrorMessage = (result: {
|
|
error?: unknown;
|
|
data?: { setDisplayName: { status: string } };
|
|
}): string | undefined => {
|
|
if (result.error) {
|
|
return "Failed to save display name. Please try again.";
|
|
}
|
|
if (result.data?.setDisplayName.status === "INVALID") {
|
|
return "Failed to save invalid display name.";
|
|
}
|
|
};
|
|
|
|
const UserName: React.FC<{ userId: string }> = ({ userId }) => {
|
|
const result = useAtomValue(userGreetingFamily(userId));
|
|
|
|
const [setDisplayNameResult, setDisplayName] = useAtom(
|
|
setDisplayNameFamily(userId),
|
|
);
|
|
const [inProgress, setInProgress] = useState(false);
|
|
|
|
const user = result.data?.user;
|
|
const displayName = user?.matrix.displayName || "";
|
|
const [fieldValue, setFieldValue] = useState(displayName);
|
|
|
|
const userGreeting = useSetAtom(userGreetingFamily(userId));
|
|
|
|
useEffect(() => {
|
|
setFieldValue(displayName);
|
|
}, [displayName]);
|
|
|
|
const hasChanges = fieldValue !== displayName;
|
|
|
|
const onChange: ChangeEventHandler<HTMLInputElement> = (event): void => {
|
|
setFieldValue(event.target.value);
|
|
};
|
|
|
|
const onSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
|
event.preventDefault();
|
|
|
|
let newDisplayName = fieldValue || null;
|
|
|
|
// set null to remove an existing username
|
|
if (newDisplayName === "") {
|
|
newDisplayName = null;
|
|
}
|
|
|
|
// do nothing if no change
|
|
if ((!newDisplayName && !displayName) || newDisplayName === displayName) {
|
|
return;
|
|
}
|
|
|
|
setInProgress(true);
|
|
setDisplayName(newDisplayName).then((result) => {
|
|
if (!result.data) {
|
|
console.error("Failed to set display name", result.error);
|
|
} else if (result.data.setDisplayName.status === "SET") {
|
|
// refresh the user greeting after changing the display name
|
|
userGreeting({
|
|
requestPolicy: "network-only",
|
|
});
|
|
} else if (result.data.setDisplayName.status === "INVALID") {
|
|
// reset to current saved display name
|
|
setFieldValue(displayName);
|
|
}
|
|
setInProgress(false);
|
|
});
|
|
};
|
|
|
|
const errorMessage = getErrorMessage(setDisplayNameResult);
|
|
|
|
return (
|
|
<Root onSubmit={onSubmit} className={styles.form}>
|
|
<Field name="displayname">
|
|
<Label>Display Name</Label>
|
|
<Control
|
|
value={fieldValue}
|
|
onChange={onChange}
|
|
inputMode="text"
|
|
max={250}
|
|
/>
|
|
</Field>
|
|
{!inProgress && errorMessage && (
|
|
<Alert type="critical" title="Error">
|
|
{errorMessage}
|
|
</Alert>
|
|
)}
|
|
|
|
<Button
|
|
className={styles.saveButton}
|
|
disabled={inProgress || !hasChanges}
|
|
kind="primary"
|
|
size="sm"
|
|
type="submit"
|
|
>
|
|
{!!inProgress && <LoadingSpinner inline />}Save
|
|
</Button>
|
|
</Root>
|
|
);
|
|
};
|
|
|
|
export default UserName;
|