Make the profile form autosave

This commit is contained in:
Robin Townsend 2023-05-22 14:33:20 -04:00
parent 85380c8142
commit ae40dea7ec
3 changed files with 38 additions and 49 deletions

View file

@ -72,6 +72,7 @@ interface InputFieldProps {
autoCorrect?: string; autoCorrect?: string;
autoCapitalize?: string; autoCapitalize?: string;
value?: string; value?: string;
defaultValue?: string;
placeholder?: string; placeholder?: string;
defaultChecked?: boolean; defaultChecked?: boolean;
onChange?: (event: ChangeEvent) => void; onChange?: (event: ChangeEvent) => void;

View file

@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { ChangeEvent, useCallback, useState } from "react"; import React, { useCallback, useEffect, useRef } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Button } from "../button";
import { useProfile } from "../profile/useProfile"; import { useProfile } from "../profile/useProfile";
import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { FieldRow, InputField, ErrorMessage } from "../input/Input";
import { AvatarInputField } from "../input/AvatarInputField"; import { AvatarInputField } from "../input/AvatarInputField";
@ -29,52 +28,47 @@ interface Props {
} }
export function ProfileSettingsTab({ client }: Props) { export function ProfileSettingsTab({ client }: Props) {
const { t } = useTranslation(); const { t } = useTranslation();
const { const { error, displayName, avatarUrl, saveProfile } = useProfile(client);
error,
loading,
displayName: initialDisplayName,
avatarUrl,
saveProfile,
} = useProfile(client);
const [displayName, setDisplayName] = useState(initialDisplayName || "");
const [removeAvatar, setRemoveAvatar] = useState(false);
const onRemoveAvatar = useCallback(() => { const formRef = useRef<HTMLFormElement | null>(null);
setRemoveAvatar(true);
const formChanged = useRef(false);
const onFormChange = useCallback(() => {
formChanged.current = true;
}, []); }, []);
const onChangeDisplayName = useCallback( const removeAvatar = useRef(false);
(e: ChangeEvent<HTMLInputElement>) => { const onRemoveAvatar = useCallback(() => {
setDisplayName(e.target.value); removeAvatar.current = true;
}, formChanged.current = true;
[setDisplayName] }, []);
);
const onSubmit = useCallback( useEffect(() => {
(e) => { const form = formRef.current!;
e.preventDefault(); return () => {
const data = new FormData(e.target); if (formChanged.current) {
const displayNameDataEntry = data.get("displayName"); const data = new FormData(form);
const avatar: File | string = data.get("avatar"); const displayNameDataEntry = data.get("displayName");
const avatar = data.get("avatar");
const avatarSize = const avatarSize =
typeof avatar == "string" ? avatar.length : avatar.size; typeof avatar == "string" ? avatar.length : avatar?.size ?? 0;
const displayName = const displayName =
typeof displayNameDataEntry == "string" typeof displayNameDataEntry == "string"
? displayNameDataEntry ? displayNameDataEntry
: displayNameDataEntry.name; : displayNameDataEntry?.name ?? null;
saveProfile({ saveProfile({
displayName, displayName,
avatar: avatar && avatarSize > 0 ? avatar : undefined, avatar: avatar && avatarSize > 0 ? avatar : undefined,
removeAvatar: removeAvatar && (!avatar || avatarSize === 0), removeAvatar: removeAvatar.current && (!avatar || avatarSize === 0),
}); });
}, }
[saveProfile, removeAvatar] };
); }, [saveProfile]);
return ( return (
<form onSubmit={onSubmit} className={styles.content}> <form onChange={onFormChange} ref={formRef} className={styles.content}>
<FieldRow className={styles.avatarFieldRow}> <FieldRow className={styles.avatarFieldRow}>
<AvatarInputField <AvatarInputField
id="avatar" id="avatar"
@ -92,7 +86,7 @@ export function ProfileSettingsTab({ client }: Props) {
label={t("Username")} label={t("Username")}
type="text" type="text"
disabled disabled
value={client.getUserId()} value={client.getUserId()!}
/> />
</FieldRow> </FieldRow>
<FieldRow> <FieldRow>
@ -104,8 +98,7 @@ export function ProfileSettingsTab({ client }: Props) {
required required
autoComplete="off" autoComplete="off"
placeholder={t("Display name")} placeholder={t("Display name")}
value={displayName} defaultValue={displayName}
onChange={onChangeDisplayName}
data-testid="profile_displayname" data-testid="profile_displayname"
/> />
</FieldRow> </FieldRow>
@ -114,11 +107,6 @@ export function ProfileSettingsTab({ client }: Props) {
<ErrorMessage error={error} /> <ErrorMessage error={error} />
</FieldRow> </FieldRow>
)} )}
<FieldRow rightAlign>
<Button type="submit" disabled={loading}>
{loading ? t("Saving…") : t("Save")}
</Button>
</FieldRow>
</form> </form>
); );
} }

View file

@ -25,8 +25,8 @@ import React, {
ReactNode, ReactNode,
useRef, useRef,
} from "react"; } from "react";
import { useClient } from "../ClientContext";
import { useClient } from "../ClientContext";
import { getNamedDevices } from "../media-utils"; import { getNamedDevices } from "../media-utils";
export interface MediaHandlerContextInterface { export interface MediaHandlerContextInterface {