import type { FC } from '../../lib/teact/teact'; import type React from '../../lib/teact/teact'; import { memo, useCallback, useEffect, useMemo, useState, } from '../../lib/teact/teact'; import { getActions } from '../../global'; import { TME_LINK_PREFIX } from '../../config'; import { isUsernameValid, MAX_USERNAME_LENGTH, MIN_UPDATE_USERNAME_LENGTH, USERNAME_REGEX, } from '../../util/entities/username'; import { debounce } from '../../util/schedulers'; import useOldLang from '../../hooks/useOldLang'; import usePreviousDeprecated from '../../hooks/usePreviousDeprecated'; import InputText from '../ui/InputText'; type OwnProps = { currentUsername?: string; asLink?: boolean; isLoading?: boolean; isUsernameAvailable?: boolean; checkedUsername?: string; onChange: (value: string) => void; }; const LINK_PREFIX_REGEX = /https:\/\/t\.me\/?/i; const runDebouncedForCheckUsername = debounce((cb) => cb(), 250, false); const UsernameInput: FC = ({ currentUsername, asLink, isLoading, isUsernameAvailable, checkedUsername, onChange, }) => { const { checkUsername, checkPublicLink } = getActions(); const [username, setUsername] = useState(currentUsername || ''); const lang = useOldLang(); const langPrefix = asLink ? 'SetUrl' : 'Username'; const label = asLink ? lang('SetUrlPlaceholder') : lang('Username'); const previousIsUsernameAvailable = usePreviousDeprecated(isUsernameAvailable); const renderingIsUsernameAvailable = currentUsername !== username ? (isUsernameAvailable ?? previousIsUsernameAvailable) : undefined; const isChecking = username && currentUsername !== username && checkedUsername !== username; const [usernameSuccess, usernameError] = useMemo(() => { if (!username.length) { return []; } if (username.length < MIN_UPDATE_USERNAME_LENGTH) { return [undefined, lang(`${langPrefix}InvalidShort`)]; } if (username.length > MAX_USERNAME_LENGTH) { return [undefined, lang(`${langPrefix}InvalidLong`)]; } if (!USERNAME_REGEX.test(username)) { return [undefined, lang(`${langPrefix}Invalid`)]; } if (renderingIsUsernameAvailable === undefined || isChecking) { return []; } // Variable `isUsernameAvailable` is initialized with `undefined`, so a strict false check is required return [ renderingIsUsernameAvailable ? lang(`${langPrefix}Available`, label) : undefined, renderingIsUsernameAvailable === false ? lang(`${langPrefix}InUse`) : undefined, ]; }, [username, renderingIsUsernameAvailable, isChecking, lang, langPrefix, label]); useEffect(() => { setUsername(currentUsername || ''); }, [asLink, currentUsername]); const handleUsernameChange = useCallback((e: React.ChangeEvent) => { const value = e.target.value.trim(); // Prevent prefix editing if (asLink && !value.match(LINK_PREFIX_REGEX)) { if (!value.length) { setUsername(''); onChange?.(''); } return; } const newUsername = value.replace(LINK_PREFIX_REGEX, ''); setUsername(newUsername); const isValid = newUsername === '' ? true : isUsernameValid(newUsername, true); if (!isValid) return; onChange?.(newUsername); runDebouncedForCheckUsername(() => { if (newUsername !== currentUsername) { const check = asLink ? checkPublicLink : checkUsername; check({ username: newUsername }); } }); }, [asLink, checkPublicLink, checkUsername, currentUsername, onChange]); return ( ); }; export default memo(UsernameInput);