Settings, Management: Various fixes for username input (#2088)
This commit is contained in:
parent
55e1f85267
commit
76c1816eba
@ -2,7 +2,9 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
|||||||
|
|
||||||
import { invokeRequest } from './client';
|
import { invokeRequest } from './client';
|
||||||
import { buildInputEntity, buildInputPeer } from '../gramjsBuilders';
|
import { buildInputEntity, buildInputPeer } from '../gramjsBuilders';
|
||||||
import type { ApiChat, ApiUser, OnApiUpdate } from '../../types';
|
import type {
|
||||||
|
ApiChat, ApiError, ApiUser, OnApiUpdate,
|
||||||
|
} from '../../types';
|
||||||
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
||||||
import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats';
|
import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats';
|
||||||
import { buildApiUser } from '../apiBuilders/users';
|
import { buildApiUser } from '../apiBuilders/users';
|
||||||
@ -18,7 +20,12 @@ export function checkChatUsername({ username }: { username: string }) {
|
|||||||
return invokeRequest(new GramJs.channels.CheckUsername({
|
return invokeRequest(new GramJs.channels.CheckUsername({
|
||||||
channel: new GramJs.InputChannelEmpty(),
|
channel: new GramJs.InputChannelEmpty(),
|
||||||
username,
|
username,
|
||||||
}));
|
}), undefined, true).catch((error) => {
|
||||||
|
if ((error as ApiError).message === 'USERNAME_INVALID') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setChatUsername(
|
export async function setChatUsername(
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiAppConfig,
|
ApiAppConfig,
|
||||||
|
ApiChat,
|
||||||
|
ApiError,
|
||||||
ApiLangString,
|
ApiLangString,
|
||||||
ApiLanguage,
|
ApiLanguage,
|
||||||
ApiNotifyException,
|
ApiNotifyException,
|
||||||
@ -52,7 +54,12 @@ export function updateProfile({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function checkUsername(username: string) {
|
export function checkUsername(username: string) {
|
||||||
return invokeRequest(new GramJs.account.CheckUsername({ username }));
|
return invokeRequest(new GramJs.account.CheckUsername({ username }), undefined, true).catch((error) => {
|
||||||
|
if ((error as ApiError).message === 'USERNAME_INVALID') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateUsername(username: string) {
|
export function updateUsername(username: string) {
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
import type { ChangeEvent } from 'react';
|
|
||||||
import type { FC } from '../../lib/teact/teact';
|
|
||||||
import React, {
|
import React, {
|
||||||
useState, useCallback, memo, useEffect, useMemo,
|
useState, useCallback, memo, useEffect, useMemo,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../global';
|
||||||
|
|
||||||
|
import type { FC } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import { TME_LINK_PREFIX } from '../../config';
|
import { TME_LINK_PREFIX } from '../../config';
|
||||||
import { debounce } from '../../util/schedulers';
|
import { debounce } from '../../util/schedulers';
|
||||||
|
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
|
|
||||||
import InputText from '../ui/InputText';
|
import InputText from '../ui/InputText';
|
||||||
|
|
||||||
@ -15,14 +18,14 @@ type OwnProps = {
|
|||||||
asLink?: boolean;
|
asLink?: boolean;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
isUsernameAvailable?: boolean;
|
isUsernameAvailable?: boolean;
|
||||||
checkUsername: AnyToVoidFunction;
|
checkedUsername?: string;
|
||||||
onChange: (value: string | false) => void;
|
onChange: (value: string | false) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MIN_USERNAME_LENGTH = 5;
|
const MIN_USERNAME_LENGTH = 5;
|
||||||
const MAX_USERNAME_LENGTH = 32;
|
const MAX_USERNAME_LENGTH = 32;
|
||||||
const LINK_PREFIX_REGEX = /https:\/\/t\.me\/?/i;
|
const LINK_PREFIX_REGEX = /https:\/\/t\.me\/?/i;
|
||||||
const USERNAME_REGEX = /^([a-zA-Z0-9_]+)$/;
|
const USERNAME_REGEX = /^[^\d]([a-zA-Z0-9_]+)$/;
|
||||||
|
|
||||||
const runDebouncedForCheckUsername = debounce((cb) => cb(), 250, false);
|
const runDebouncedForCheckUsername = debounce((cb) => cb(), 250, false);
|
||||||
|
|
||||||
@ -32,78 +35,84 @@ function isUsernameValid(username: string) {
|
|||||||
&& USERNAME_REGEX.test(username);
|
&& USERNAME_REGEX.test(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsEditProfile: FC<OwnProps> = ({
|
const UsernameInput: FC<OwnProps> = ({
|
||||||
currentUsername,
|
currentUsername,
|
||||||
asLink,
|
asLink,
|
||||||
isLoading,
|
isLoading,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
checkUsername,
|
checkedUsername,
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { checkUsername, checkPublicLink } = getActions();
|
||||||
const [username, setUsername] = useState(currentUsername || '');
|
const [username, setUsername] = useState(currentUsername || '');
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const langPrefix = asLink ? 'SetUrl' : 'Username';
|
const langPrefix = asLink ? 'SetUrl' : 'Username';
|
||||||
const label = asLink ? lang('SetUrlPlaceholder') : lang('Username');
|
const label = asLink ? lang('SetUrlPlaceholder') : lang('Username');
|
||||||
|
|
||||||
|
const previousIsUsernameAvailable = usePrevious(isUsernameAvailable);
|
||||||
|
const renderingIsUsernameAvailable = currentUsername !== username
|
||||||
|
? (isUsernameAvailable ?? previousIsUsernameAvailable) : undefined;
|
||||||
|
const isChecking = username && currentUsername !== username && checkedUsername !== username;
|
||||||
|
|
||||||
const [usernameSuccess, usernameError] = useMemo(() => {
|
const [usernameSuccess, usernameError] = useMemo(() => {
|
||||||
if (!username.length) {
|
if (!username.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username.length < MIN_USERNAME_LENGTH) {
|
if (username.length < MIN_USERNAME_LENGTH) {
|
||||||
return [undefined, `${label} is too short`];
|
return [undefined, lang(`${langPrefix}InvalidShort`)];
|
||||||
}
|
}
|
||||||
if (username.length > MAX_USERNAME_LENGTH) {
|
if (username.length > MAX_USERNAME_LENGTH) {
|
||||||
return [undefined, `${label} is too long`];
|
return [undefined, lang(`${langPrefix}InvalidLong`)];
|
||||||
}
|
}
|
||||||
if (!USERNAME_REGEX.test(username)) {
|
if (!USERNAME_REGEX.test(username)) {
|
||||||
return [undefined, `${label} contains invalid characters`];
|
return [undefined, lang(`${langPrefix}Invalid`)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUsernameAvailable === undefined) {
|
if (renderingIsUsernameAvailable === undefined || isChecking) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variable `isUsernameAvailable` is initialized with `undefined`, so a strict false check is required
|
// Variable `isUsernameAvailable` is initialized with `undefined`, so a strict false check is required
|
||||||
return [
|
return [
|
||||||
isUsernameAvailable ? lang(`${langPrefix}Available`, 'Username') : undefined,
|
renderingIsUsernameAvailable ? lang(`${langPrefix}Available`, label) : undefined,
|
||||||
isUsernameAvailable === false ? lang(`${langPrefix}InUse`) : undefined,
|
renderingIsUsernameAvailable === false ? lang(`${langPrefix}InUse`) : undefined,
|
||||||
];
|
];
|
||||||
}, [username, isUsernameAvailable, lang, langPrefix, label]);
|
}, [username, renderingIsUsernameAvailable, isChecking, lang, langPrefix, label]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUsername(currentUsername || '');
|
setUsername(currentUsername || '');
|
||||||
}, [asLink, currentUsername]);
|
}, [asLink, currentUsername]);
|
||||||
|
|
||||||
const handleUsernameChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
const handleUsernameChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const newUsername = e.target.value.trim().replace(LINK_PREFIX_REGEX, '');
|
const newUsername = e.target.value.trim().replace(LINK_PREFIX_REGEX, '');
|
||||||
setUsername(newUsername);
|
setUsername(newUsername);
|
||||||
e.target.value = `${asLink ? TME_LINK_PREFIX : ''}${newUsername}`;
|
|
||||||
|
|
||||||
const isValid = isUsernameValid(newUsername);
|
const isValid = isUsernameValid(newUsername);
|
||||||
|
if (!isValid) return;
|
||||||
|
|
||||||
if (isValid) {
|
onChange?.(newUsername);
|
||||||
runDebouncedForCheckUsername(() => {
|
|
||||||
checkUsername({ username: newUsername });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onChange) {
|
runDebouncedForCheckUsername(() => {
|
||||||
onChange(isValid ? newUsername : false);
|
if (newUsername !== currentUsername) {
|
||||||
}
|
const check = asLink ? checkPublicLink : checkUsername;
|
||||||
}, [asLink, checkUsername, onChange]);
|
check({ username: newUsername });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [asLink, checkPublicLink, checkUsername, currentUsername, onChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputText
|
<InputText
|
||||||
value={`${asLink ? TME_LINK_PREFIX : ''}${username}`}
|
value={`${asLink ? TME_LINK_PREFIX : ''}${username}`}
|
||||||
onChange={handleUsernameChange}
|
onChange={handleUsernameChange}
|
||||||
label={label}
|
label={isChecking ? lang('Checking') : label}
|
||||||
error={usernameError}
|
error={usernameError}
|
||||||
success={usernameSuccess}
|
success={usernameSuccess}
|
||||||
readOnly={isLoading}
|
readOnly={isLoading}
|
||||||
|
teactExperimentControlled
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(SettingsEditProfile);
|
export default memo(UsernameInput);
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
import usePrevious from '../../../hooks/usePrevious';
|
||||||
|
|
||||||
import AvatarEditable from '../../ui/AvatarEditable';
|
import AvatarEditable from '../../ui/AvatarEditable';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -37,6 +38,7 @@ type StateProps = {
|
|||||||
currentBio?: string;
|
currentBio?: string;
|
||||||
currentUsername?: string;
|
currentUsername?: string;
|
||||||
progress?: ProfileEditProgress;
|
progress?: ProfileEditProgress;
|
||||||
|
checkedUsername?: string;
|
||||||
isUsernameAvailable?: boolean;
|
isUsernameAvailable?: boolean;
|
||||||
maxBioLength: number;
|
maxBioLength: number;
|
||||||
};
|
};
|
||||||
@ -47,20 +49,20 @@ const ERROR_FIRST_NAME_MISSING = 'Please provide your first name';
|
|||||||
|
|
||||||
const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onReset,
|
|
||||||
currentAvatarHash,
|
currentAvatarHash,
|
||||||
currentFirstName,
|
currentFirstName,
|
||||||
currentLastName,
|
currentLastName,
|
||||||
currentBio,
|
currentBio,
|
||||||
currentUsername,
|
currentUsername,
|
||||||
progress,
|
progress,
|
||||||
|
checkedUsername,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
maxBioLength,
|
maxBioLength,
|
||||||
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
loadCurrentUser,
|
loadCurrentUser,
|
||||||
updateProfile,
|
updateProfile,
|
||||||
checkUsername,
|
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -80,13 +82,16 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
|||||||
const isLoading = progress === ProfileEditProgress.InProgress;
|
const isLoading = progress === ProfileEditProgress.InProgress;
|
||||||
const isUsernameError = username === false;
|
const isUsernameError = username === false;
|
||||||
|
|
||||||
|
const previousIsUsernameAvailable = usePrevious(isUsernameAvailable);
|
||||||
|
const renderingIsUsernameAvailable = isUsernameAvailable ?? previousIsUsernameAvailable;
|
||||||
|
|
||||||
const isSaveButtonShown = useMemo(() => {
|
const isSaveButtonShown = useMemo(() => {
|
||||||
if (isUsernameError) {
|
if (isUsernameError) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Boolean(photo) || isProfileFieldsTouched || isUsernameAvailable === true;
|
return Boolean(photo) || isProfileFieldsTouched || (isUsernameTouched && renderingIsUsernameAvailable === true);
|
||||||
}, [photo, isProfileFieldsTouched, isUsernameError, isUsernameAvailable]);
|
}, [isUsernameError, photo, isProfileFieldsTouched, isUsernameTouched, renderingIsUsernameAvailable]);
|
||||||
|
|
||||||
useHistoryBack({
|
useHistoryBack({
|
||||||
isActive,
|
isActive,
|
||||||
@ -144,8 +149,8 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const handleUsernameChange = useCallback((value: string | false) => {
|
const handleUsernameChange = useCallback((value: string | false) => {
|
||||||
setUsername(value);
|
setUsername(value);
|
||||||
setIsUsernameTouched(true);
|
setIsUsernameTouched(currentUsername !== value);
|
||||||
}, []);
|
}, [currentUsername]);
|
||||||
|
|
||||||
const handleProfileSave = useCallback(() => {
|
const handleProfileSave = useCallback(() => {
|
||||||
const trimmedFirstName = firstName.trim();
|
const trimmedFirstName = firstName.trim();
|
||||||
@ -216,10 +221,10 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
|||||||
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('Username')}</h4>
|
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('Username')}</h4>
|
||||||
|
|
||||||
<UsernameInput
|
<UsernameInput
|
||||||
currentUsername={username || ''}
|
currentUsername={currentUsername}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isUsernameAvailable={isUsernameAvailable}
|
isUsernameAvailable={isUsernameAvailable}
|
||||||
checkUsername={checkUsername}
|
checkedUsername={checkedUsername}
|
||||||
onChange={handleUsernameChange}
|
onChange={handleUsernameChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -239,7 +244,7 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
|||||||
isShown={isSaveButtonShown}
|
isShown={isSaveButtonShown}
|
||||||
onClick={handleProfileSave}
|
onClick={handleProfileSave}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
ariaLabel="Save changes"
|
ariaLabel={lang('Save')}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Spinner color="white" />
|
<Spinner color="white" />
|
||||||
@ -254,7 +259,7 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
|||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const { currentUserId } = global;
|
const { currentUserId } = global;
|
||||||
const { progress, isUsernameAvailable } = global.profileEdit || {};
|
const { progress, isUsernameAvailable, checkedUsername } = global.profileEdit || {};
|
||||||
const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined;
|
const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined;
|
||||||
|
|
||||||
const maxBioLength = selectCurrentLimit(global, 'aboutLength');
|
const maxBioLength = selectCurrentLimit(global, 'aboutLength');
|
||||||
@ -262,6 +267,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return {
|
return {
|
||||||
progress,
|
progress,
|
||||||
|
checkedUsername,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
maxBioLength,
|
maxBioLength,
|
||||||
};
|
};
|
||||||
@ -284,6 +290,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
currentUsername,
|
currentUsername,
|
||||||
progress,
|
progress,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
|
checkedUsername,
|
||||||
maxBioLength,
|
maxBioLength,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,10 +10,12 @@ import { ManagementProgress } from '../../../types';
|
|||||||
|
|
||||||
import { selectChat, selectManagement } from '../../../global/selectors';
|
import { selectChat, selectManagement } from '../../../global/selectors';
|
||||||
import { isChatChannel } from '../../../global/helpers';
|
import { isChatChannel } from '../../../global/helpers';
|
||||||
|
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
import usePrevious from '../../../hooks/usePrevious';
|
||||||
|
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -37,22 +39,23 @@ type StateProps = {
|
|||||||
isChannel: boolean;
|
isChannel: boolean;
|
||||||
progress?: ManagementProgress;
|
progress?: ManagementProgress;
|
||||||
isUsernameAvailable?: boolean;
|
isUsernameAvailable?: boolean;
|
||||||
|
checkedUsername?: string;
|
||||||
isProtected?: boolean;
|
isProtected?: boolean;
|
||||||
maxPublicLinks: number;
|
maxPublicLinks: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||||
chat,
|
chat,
|
||||||
onClose,
|
|
||||||
isActive,
|
isActive,
|
||||||
isChannel,
|
isChannel,
|
||||||
progress,
|
progress,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
|
checkedUsername,
|
||||||
isProtected,
|
isProtected,
|
||||||
maxPublicLinks,
|
maxPublicLinks,
|
||||||
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
checkPublicLink,
|
|
||||||
updatePublicLink,
|
updatePublicLink,
|
||||||
updatePrivateLink,
|
updatePrivateLink,
|
||||||
toggleIsProtected,
|
toggleIsProtected,
|
||||||
@ -66,8 +69,11 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
|||||||
const [username, setUsername] = useState();
|
const [username, setUsername] = useState();
|
||||||
const [isRevokeConfirmDialogOpen, openRevokeConfirmDialog, closeRevokeConfirmDialog] = useFlag();
|
const [isRevokeConfirmDialogOpen, openRevokeConfirmDialog, closeRevokeConfirmDialog] = useFlag();
|
||||||
|
|
||||||
|
const previousIsUsernameAvailable = usePrevious(isUsernameAvailable);
|
||||||
|
const renderingIsUsernameAvailable = isUsernameAvailable ?? previousIsUsernameAvailable;
|
||||||
|
|
||||||
const canUpdate = Boolean(
|
const canUpdate = Boolean(
|
||||||
(privacyType === 'public' && username && isUsernameAvailable)
|
(privacyType === 'public' && username && renderingIsUsernameAvailable)
|
||||||
|| (privacyType === 'private' && isPublic),
|
|| (privacyType === 'private' && isPublic),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -175,7 +181,7 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
|||||||
currentUsername={chat.username}
|
currentUsername={chat.username}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isUsernameAvailable={isUsernameAvailable}
|
isUsernameAvailable={isUsernameAvailable}
|
||||||
checkUsername={checkPublicLink}
|
checkedUsername={checkedUsername}
|
||||||
onChange={setUsername}
|
onChange={setUsername}
|
||||||
/>
|
/>
|
||||||
<p className="section-info" dir="auto">
|
<p className="section-info" dir="auto">
|
||||||
@ -219,13 +225,14 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
|||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { chatId }): StateProps => {
|
(global, { chatId }): StateProps => {
|
||||||
const chat = selectChat(global, chatId)!;
|
const chat = selectChat(global, chatId)!;
|
||||||
const { isUsernameAvailable } = selectManagement(global, chatId)!;
|
const { isUsernameAvailable, checkedUsername } = selectManagement(global, chatId)!;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chat,
|
chat,
|
||||||
isChannel: isChatChannel(chat),
|
isChannel: isChatChannel(chat),
|
||||||
progress: global.management.progress,
|
progress: global.management.progress,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
|
checkedUsername,
|
||||||
isProtected: chat?.isProtected,
|
isProtected: chat?.isProtected,
|
||||||
maxPublicLinks: selectCurrentLimit(global, 'channelsPublic'),
|
maxPublicLinks: selectCurrentLimit(global, 'channelsPublic'),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
||||||
|
|
||||||
import { ManagementProgress } from '../../../types';
|
import { ManagementProgress } from '../../../types';
|
||||||
|
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import {
|
import {
|
||||||
addUsers, updateChat, updateManagement, updateManagementProgress,
|
addUsers, updateChat, updateManagement, updateManagementProgress,
|
||||||
@ -22,17 +23,16 @@ addActionHandler('checkPublicLink', async (global, actions, payload) => {
|
|||||||
|
|
||||||
const { username } = payload!;
|
const { username } = payload!;
|
||||||
|
|
||||||
global = updateManagementProgress(global, ManagementProgress.InProgress);
|
global = updateManagement(global, chatId, { isUsernameAvailable: undefined, checkedUsername: undefined });
|
||||||
global = updateManagement(global, chatId, { isUsernameAvailable: undefined });
|
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|
||||||
const isUsernameAvailable = await callApi('checkChatUsername', { username })!;
|
const isUsernameAvailable = (await callApi('checkChatUsername', { username }))!;
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
global = updateManagementProgress(
|
global = updateManagementProgress(
|
||||||
global, isUsernameAvailable ? ManagementProgress.Complete : ManagementProgress.Error,
|
global, isUsernameAvailable ? ManagementProgress.Complete : ManagementProgress.Error,
|
||||||
);
|
);
|
||||||
global = updateManagement(global, chatId, { isUsernameAvailable });
|
global = updateManagement(global, chatId, { isUsernameAvailable, checkedUsername: username });
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|
||||||
if (isUsernameAvailable === undefined) {
|
if (isUsernameAvailable === undefined) {
|
||||||
@ -66,7 +66,7 @@ addActionHandler('updatePublicLink', async (global, actions, payload) => {
|
|||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
global = updateManagementProgress(global, result ? ManagementProgress.Complete : ManagementProgress.Error);
|
global = updateManagementProgress(global, result ? ManagementProgress.Complete : ManagementProgress.Error);
|
||||||
global = updateManagement(global, chatId, { isUsernameAvailable: undefined });
|
global = updateManagement(global, chatId, { isUsernameAvailable: undefined, checkedUsername: undefined });
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -92,6 +92,7 @@ addActionHandler('checkUsername', async (global, actions, payload) => {
|
|||||||
...global,
|
...global,
|
||||||
profileEdit: {
|
profileEdit: {
|
||||||
progress: global.profileEdit ? global.profileEdit.progress : ProfileEditProgress.Idle,
|
progress: global.profileEdit ? global.profileEdit.progress : ProfileEditProgress.Idle,
|
||||||
|
checkedUsername: undefined,
|
||||||
isUsernameAvailable: undefined,
|
isUsernameAvailable: undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -103,6 +104,7 @@ addActionHandler('checkUsername', async (global, actions, payload) => {
|
|||||||
...global,
|
...global,
|
||||||
profileEdit: {
|
profileEdit: {
|
||||||
...global.profileEdit!,
|
...global.profileEdit!,
|
||||||
|
checkedUsername: username,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -511,6 +511,7 @@ export type GlobalState = {
|
|||||||
|
|
||||||
profileEdit?: {
|
profileEdit?: {
|
||||||
progress: ProfileEditProgress;
|
progress: ProfileEditProgress;
|
||||||
|
checkedUsername?: string;
|
||||||
isUsernameAvailable?: boolean;
|
isUsernameAvailable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -313,6 +313,7 @@ export enum ManagementProgress {
|
|||||||
export interface ManagementState {
|
export interface ManagementState {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
nextScreen?: ManagementScreens;
|
nextScreen?: ManagementScreens;
|
||||||
|
checkedUsername?: string;
|
||||||
isUsernameAvailable?: boolean;
|
isUsernameAvailable?: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
invites?: ApiExportedInvite[];
|
invites?: ApiExportedInvite[];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user