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