import type { ChangeEvent } from 'react'; import type { FC } from '../../../lib/teact/teact'; import React, { useState, useCallback, memo, useEffect, useMemo, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import { ApiMediaFormat } from '../../../api/types'; import { ProfileEditProgress } from '../../../types'; import { TME_LINK_PREFIX } from '../../../config'; import { throttle } from '../../../util/schedulers'; import { selectUser } from '../../../global/selectors'; import { getChatAvatarHash } from '../../../global/helpers'; import useMedia from '../../../hooks/useMedia'; import useLang from '../../../hooks/useLang'; import { selectCurrentLimit } from '../../../global/selectors/limits'; import renderText from '../../common/helpers/renderText'; import useHistoryBack from '../../../hooks/useHistoryBack'; import AvatarEditable from '../../ui/AvatarEditable'; import FloatingActionButton from '../../ui/FloatingActionButton'; import Spinner from '../../ui/Spinner'; import InputText from '../../ui/InputText'; import UsernameInput from '../../common/UsernameInput'; import TextArea from '../../ui/TextArea'; type OwnProps = { isActive: boolean; onReset: () => void; }; type StateProps = { currentAvatarHash?: string; currentFirstName?: string; currentLastName?: string; currentBio?: string; currentUsername?: string; progress?: ProfileEditProgress; isUsernameAvailable?: boolean; maxBioLength: number; }; const runThrottled = throttle((cb) => cb(), 60000, true); const ERROR_FIRST_NAME_MISSING = 'Please provide your first name'; const SettingsEditProfile: FC = ({ isActive, onReset, currentAvatarHash, currentFirstName, currentLastName, currentBio, currentUsername, progress, isUsernameAvailable, maxBioLength, }) => { const { loadCurrentUser, updateProfile, checkUsername, } = getActions(); const lang = useLang(); const [isUsernameTouched, setIsUsernameTouched] = useState(false); const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false); const [error, setError] = useState(); const [photo, setPhoto] = useState(); const [firstName, setFirstName] = useState(currentFirstName || ''); const [lastName, setLastName] = useState(currentLastName || ''); const [bio, setBio] = useState(currentBio || ''); const [username, setUsername] = useState(currentUsername || ''); const currentAvatarBlobUrl = useMedia(currentAvatarHash, false, ApiMediaFormat.BlobUrl); const isLoading = progress === ProfileEditProgress.InProgress; const isUsernameError = username === false; const isSaveButtonShown = useMemo(() => { if (isUsernameError) { return false; } return Boolean(photo) || isProfileFieldsTouched || isUsernameAvailable === true; }, [photo, isProfileFieldsTouched, isUsernameError, isUsernameAvailable]); useHistoryBack({ isActive, onBack: onReset, }); // Due to the parent Transition, this component never gets unmounted, // that's why we use throttled API call on every update. useEffect(() => { runThrottled(() => { loadCurrentUser(); }); }, [loadCurrentUser]); useEffect(() => { setPhoto(undefined); }, [currentAvatarBlobUrl]); useEffect(() => { setFirstName(currentFirstName || ''); setLastName(currentLastName || ''); setBio(currentBio || ''); }, [currentFirstName, currentLastName, currentBio]); useEffect(() => { setUsername(currentUsername || ''); }, [currentUsername]); useEffect(() => { if (progress === ProfileEditProgress.Complete) { setIsProfileFieldsTouched(false); setIsUsernameTouched(false); setError(undefined); } }, [progress]); const handlePhotoChange = useCallback((newPhoto: File) => { setPhoto(newPhoto); }, []); const handleFirstNameChange = useCallback((e: ChangeEvent) => { setFirstName(e.target.value); setIsProfileFieldsTouched(true); }, []); const handleLastNameChange = useCallback((e: ChangeEvent) => { setLastName(e.target.value); setIsProfileFieldsTouched(true); }, []); const handleBioChange = useCallback((e: ChangeEvent) => { setBio(e.target.value); setIsProfileFieldsTouched(true); }, []); const handleUsernameChange = useCallback((value: string | false) => { setUsername(value); setIsUsernameTouched(true); }, []); const handleProfileSave = useCallback(() => { const trimmedFirstName = firstName.trim(); const trimmedLastName = lastName.trim(); const trimmedBio = bio.trim(); if (!trimmedFirstName.length) { setError(ERROR_FIRST_NAME_MISSING); return; } updateProfile({ photo, ...(isProfileFieldsTouched && { firstName: trimmedFirstName, lastName: trimmedLastName, bio: trimmedBio, }), ...(isUsernameTouched && { username, }), }); }, [ photo, firstName, lastName, bio, isProfileFieldsTouched, username, isUsernameTouched, updateProfile, ]); return (