Make the profile form autosave
This commit is contained in:
parent
85380c8142
commit
ae40dea7ec
3 changed files with 38 additions and 49 deletions
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue