Management: Allow to edit bot info (#4178)
Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>
This commit is contained in:
parent
13846c0d42
commit
21aefeffef
@ -71,6 +71,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
|
||||
type: userType,
|
||||
firstName,
|
||||
lastName,
|
||||
canEditBot: Boolean(mtpUser.botCanEdit),
|
||||
...(userType === 'userTypeBot' && { canBeInvitedToGroup: !mtpUser.botNochats }),
|
||||
...(usernames && { usernames }),
|
||||
phoneNumber: mtpUser.phone || '',
|
||||
|
||||
@ -564,3 +564,27 @@ function addPhotoToLocalDb(photo: GramJs.Photo) {
|
||||
function addWebDocumentToLocalDb(webDocument: GramJs.TypeWebDocument) {
|
||||
localDb.webDocuments[webDocument.url] = webDocument;
|
||||
}
|
||||
|
||||
export function setBotInfo({
|
||||
bot,
|
||||
langCode,
|
||||
name,
|
||||
about,
|
||||
description,
|
||||
}: {
|
||||
bot: ApiUser;
|
||||
langCode: string;
|
||||
name?: string;
|
||||
about?: string;
|
||||
description?: string;
|
||||
}) {
|
||||
return invokeRequest(new GramJs.bots.SetBotInfo({
|
||||
bot: buildInputPeer(bot.id, bot.accessHash),
|
||||
langCode,
|
||||
name: name || '',
|
||||
about: about || '',
|
||||
description: description || '',
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -75,7 +75,8 @@ export {
|
||||
} from './twoFaSettings';
|
||||
|
||||
export {
|
||||
answerCallbackButton, fetchTopInlineBots, fetchInlineBot, fetchInlineBotResults, sendInlineBotResult, startBot,
|
||||
answerCallbackButton, setBotInfo, fetchTopInlineBots, fetchInlineBot, fetchInlineBotResults,
|
||||
sendInlineBotResult, startBot,
|
||||
requestWebView, requestSimpleWebView, sendWebViewData, prolongWebView, loadAttachBots, toggleAttachBot, fetchBotApp,
|
||||
requestBotUrlAuth, requestLinkUrlAuth, acceptBotUrlAuth, acceptLinkUrlAuth, loadAttachBot, requestAppWebView,
|
||||
allowBotSendMessages, fetchBotCanSendMessage, invokeWebViewCustomMethod,
|
||||
|
||||
@ -109,9 +109,12 @@ export async function updateProfilePhoto(photo?: ApiPhoto, isFallback?: boolean)
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function uploadProfilePhoto(file: File, isFallback?: boolean, isVideo = false, videoTs = 0) {
|
||||
export async function uploadProfilePhoto(
|
||||
file: File, isFallback?: boolean, isVideo = false, videoTs = 0, bot?: ApiUser,
|
||||
) {
|
||||
const inputFile = await uploadFile(file);
|
||||
const result = await invokeRequest(new GramJs.photos.UploadProfilePhoto({
|
||||
...(bot ? { bot: buildInputPeer(bot.id, bot.accessHash) } : undefined),
|
||||
...(isVideo ? { video: inputFile, videoStartTs: videoTs } : { file: inputFile }),
|
||||
...(isFallback ? { fallback: true } : undefined),
|
||||
}));
|
||||
|
||||
@ -37,6 +37,7 @@ export interface ApiUser {
|
||||
hasUnreadStories?: boolean;
|
||||
maxStoryId?: number;
|
||||
color?: ApiPeerColor;
|
||||
canEditBot?: boolean;
|
||||
}
|
||||
|
||||
export interface ApiUserFullInfo {
|
||||
|
||||
@ -31,6 +31,7 @@ import useFlag from '../../hooks/useFlag';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
|
||||
import Icon from '../common/Icon';
|
||||
import Button from '../ui/Button';
|
||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||
import SearchInput from '../ui/SearchInput';
|
||||
@ -75,6 +76,7 @@ type StateProps = {
|
||||
currentInviteInfo?: ApiExportedInvite;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
isBot?: boolean;
|
||||
canEditBot?: boolean;
|
||||
isInsideTopic?: boolean;
|
||||
canEditTopic?: boolean;
|
||||
};
|
||||
@ -157,6 +159,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
canEditTopic,
|
||||
onClose,
|
||||
onScreenSelect,
|
||||
canEditBot,
|
||||
}) => {
|
||||
const {
|
||||
setLocalTextSearchQuery,
|
||||
@ -494,6 +497,17 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
<i className="icon icon-edit" />
|
||||
</Button>
|
||||
)}
|
||||
{canEditBot && (
|
||||
<Button
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Edit')}
|
||||
onClick={handleToggleManagement}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
</Button>
|
||||
)}
|
||||
{canEditTopic && (
|
||||
<Button
|
||||
round
|
||||
@ -580,6 +594,7 @@ export default withGlobal<OwnProps>(
|
||||
const topic = isInsideTopic ? chat.topics?.[threadId!] : undefined;
|
||||
const canEditTopic = isInsideTopic && topic && getCanManageTopic(chat, topic);
|
||||
const isBot = user && isUserBot(user);
|
||||
const canEditBot = isBot && user?.canEditBot;
|
||||
|
||||
const canAddContact = user && getCanAddContact(user);
|
||||
const canManage = Boolean(!isManagement && isProfile && chatId && selectCanManage(global, chatId));
|
||||
@ -607,6 +622,7 @@ export default withGlobal<OwnProps>(
|
||||
isEditingInvite,
|
||||
currentInviteInfo,
|
||||
shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations,
|
||||
canEditBot,
|
||||
};
|
||||
},
|
||||
)(RightHeader);
|
||||
|
||||
267
src/components/right/management/ManageBot.tsx
Normal file
267
src/components/right/management/ManageBot.tsx
Normal file
@ -0,0 +1,267 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useMemo, useRef, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiBotInfo, ApiUser } from '../../../api/types';
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
import { ManagementProgress } from '../../../types';
|
||||
|
||||
import {
|
||||
getChatAvatarHash, getMainUsername, getUserFirstOrLastName,
|
||||
} from '../../../global/helpers';
|
||||
import {
|
||||
selectBot,
|
||||
selectTabState,
|
||||
selectUserFullInfo,
|
||||
} from '../../../global/selectors';
|
||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
|
||||
import Icon from '../../common/Icon';
|
||||
import AvatarEditable from '../../ui/AvatarEditable';
|
||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||
import InputText from '../../ui/InputText';
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import SelectAvatar from '../../ui/SelectAvatar';
|
||||
import Spinner from '../../ui/Spinner';
|
||||
import TextArea from '../../ui/TextArea';
|
||||
|
||||
import './Management.scss';
|
||||
|
||||
type OwnProps = {
|
||||
userId: string;
|
||||
onClose: NoneToVoidFunction;
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
userId?: string;
|
||||
user?: ApiUser;
|
||||
chatBot?: ApiBotInfo;
|
||||
currentBio?: string;
|
||||
progress?: ManagementProgress;
|
||||
isMuted?: boolean;
|
||||
maxBioLength: number;
|
||||
};
|
||||
|
||||
const ERROR_NAME_MISSING = 'Please provide name';
|
||||
|
||||
const ManageBot: FC<OwnProps & StateProps> = ({
|
||||
userId,
|
||||
user,
|
||||
progress,
|
||||
onClose,
|
||||
currentBio,
|
||||
isActive,
|
||||
maxBioLength,
|
||||
}) => {
|
||||
const {
|
||||
setBotInfo,
|
||||
uploadProfilePhoto,
|
||||
uploadContactProfilePhoto,
|
||||
startBotFatherConversation,
|
||||
} = getActions();
|
||||
|
||||
const [isFieldTouched, markFieldTouched, unmarkProfileTouched] = useFlag(false);
|
||||
const [isAvatarTouched, markAvatarTouched, unmarkAvatarTouched] = useFlag(false);
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
const lang = useLang();
|
||||
|
||||
const username = useMemo(() => (user ? getMainUsername(user) : undefined), [user]);
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
onBack: onClose,
|
||||
});
|
||||
|
||||
const currentName = user ? getUserFirstOrLastName(user) : '';
|
||||
|
||||
const [photo, setPhoto] = useState<File | undefined>();
|
||||
const [name, setName] = useState(currentName || '');
|
||||
const [bio, setBio] = useState(currentBio || '');
|
||||
|
||||
const currentAvatarHash = user && getChatAvatarHash(user);
|
||||
const currentAvatarBlobUrl = useMedia(currentAvatarHash, false, ApiMediaFormat.BlobUrl);
|
||||
|
||||
useEffect(() => {
|
||||
unmarkProfileTouched();
|
||||
unmarkAvatarTouched();
|
||||
}, [userId]);
|
||||
|
||||
useEffect(() => {
|
||||
setName(currentName || '');
|
||||
setBio(currentBio || '');
|
||||
}, [currentName, currentBio, user]);
|
||||
|
||||
useEffect(() => {
|
||||
setPhoto(undefined);
|
||||
}, [currentAvatarBlobUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (progress === ManagementProgress.Complete) {
|
||||
unmarkProfileTouched();
|
||||
unmarkAvatarTouched();
|
||||
setError(undefined);
|
||||
}
|
||||
}, [progress]);
|
||||
|
||||
const handleNameChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setName(e.target.value);
|
||||
markFieldTouched();
|
||||
|
||||
if (error === ERROR_NAME_MISSING) {
|
||||
setError(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const handleBioChange = useLastCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setBio(e.target.value);
|
||||
markFieldTouched();
|
||||
});
|
||||
|
||||
const handlePhotoChange = useLastCallback((newPhoto: File) => {
|
||||
setPhoto(newPhoto);
|
||||
markAvatarTouched();
|
||||
});
|
||||
|
||||
const handleProfileSave = useLastCallback(() => {
|
||||
const trimmedName = name.trim();
|
||||
const trimmedBio = bio.trim();
|
||||
|
||||
if (!trimmedName.length) {
|
||||
setError(ERROR_NAME_MISSING);
|
||||
return;
|
||||
}
|
||||
|
||||
setBotInfo({
|
||||
...(isFieldTouched && {
|
||||
bot: user,
|
||||
name: trimmedName,
|
||||
description: trimmedBio,
|
||||
}),
|
||||
});
|
||||
|
||||
if (photo) {
|
||||
uploadProfilePhoto({
|
||||
file: photo,
|
||||
...(isAvatarTouched && { bot: user }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleChangeEditIntro = useLastCallback(() => {
|
||||
startBotFatherConversation({ param: `${username}-intro` });
|
||||
});
|
||||
|
||||
const handleChangeEditCommands = useLastCallback(() => {
|
||||
startBotFatherConversation({ param: `${username}-commands` });
|
||||
});
|
||||
|
||||
const handleChangeSettings = useLastCallback(() => {
|
||||
startBotFatherConversation({ param: `${username}` });
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const isSuggestRef = useRef(false);
|
||||
|
||||
const handleSelectAvatar = useLastCallback((file: File) => {
|
||||
markAvatarTouched();
|
||||
uploadContactProfilePhoto({ userId, file, isSuggest: isSuggestRef.current });
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isLoading = progress === ManagementProgress.InProgress;
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="custom-scroll">
|
||||
<div className="section">
|
||||
<AvatarEditable
|
||||
currentAvatarBlobUrl={currentAvatarBlobUrl}
|
||||
onChange={handlePhotoChange}
|
||||
title={lang('ChatSetPhotoOrVideo')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<InputText
|
||||
id="user-name"
|
||||
label={lang('PaymentCheckoutName')}
|
||||
onChange={handleNameChange}
|
||||
value={name}
|
||||
error={error === ERROR_NAME_MISSING ? error : undefined}
|
||||
teactExperimentControlled
|
||||
/>
|
||||
<TextArea
|
||||
value={bio}
|
||||
onChange={handleBioChange}
|
||||
label={lang('DescriptionPlaceholder')}
|
||||
disabled={isLoading}
|
||||
maxLength={maxBioLength}
|
||||
maxLengthIndicator={maxBioLength ? (maxBioLength - bio.length).toString() : undefined}
|
||||
/>
|
||||
</div>
|
||||
<div className="section">
|
||||
<div className="dialog-buttons">
|
||||
<ListItem icon="bot-commands-filled" ripple onClick={handleChangeEditIntro}>
|
||||
<span>{lang('BotEditIntro')}</span>
|
||||
</ListItem>
|
||||
<ListItem icon="bot-command" ripple onClick={handleChangeEditCommands}>
|
||||
<span>{lang('BotEditCommands')}</span>
|
||||
</ListItem>
|
||||
<ListItem icon="bots" ripple onClick={handleChangeSettings}>
|
||||
<span>{lang('BotChangeSettings')}</span>
|
||||
</ListItem>
|
||||
<div className="section-info section-info_push">
|
||||
{renderText(lang('BotManageInfo'), ['links'])}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FloatingActionButton
|
||||
isShown={isFieldTouched || isAvatarTouched}
|
||||
onClick={handleProfileSave}
|
||||
disabled={isLoading}
|
||||
ariaLabel={lang('Save')}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Spinner color="white" />
|
||||
) : (
|
||||
<Icon name="check" />
|
||||
)}
|
||||
</FloatingActionButton>
|
||||
<SelectAvatar
|
||||
onChange={handleSelectAvatar}
|
||||
inputRef={inputRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { userId }): StateProps => {
|
||||
const user = selectBot(global, userId);
|
||||
const userFullInfo = selectUserFullInfo(global, userId);
|
||||
const { progress } = selectTabState(global).management;
|
||||
const maxBioLength = selectCurrentLimit(global, 'aboutLength');
|
||||
|
||||
return {
|
||||
userId,
|
||||
user,
|
||||
progress,
|
||||
currentBio: userFullInfo?.bio,
|
||||
maxBioLength,
|
||||
};
|
||||
},
|
||||
)(ManageBot));
|
||||
@ -161,6 +161,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.button-position {
|
||||
justify-content: initial;
|
||||
}
|
||||
|
||||
&__filter {
|
||||
padding: 0 1rem 0.25rem 0.75rem;
|
||||
margin-bottom: 0.625rem;
|
||||
|
||||
@ -7,6 +7,7 @@ import { ManagementScreens } from '../../../types';
|
||||
|
||||
import { selectCurrentManagementType } from '../../../global/selectors';
|
||||
|
||||
import ManageBot from './ManageBot';
|
||||
import ManageChannel from './ManageChannel';
|
||||
import ManageChatAdministrators from './ManageChatAdministrators';
|
||||
import ManageChatPrivacyType from './ManageChatPrivacyType';
|
||||
@ -54,6 +55,15 @@ const Management: FC<OwnProps & StateProps> = ({
|
||||
switch (currentScreen) {
|
||||
case ManagementScreens.Initial: {
|
||||
switch (managementType) {
|
||||
case 'bot':
|
||||
return (
|
||||
<ManageBot
|
||||
key={chatId}
|
||||
userId={chatId}
|
||||
onClose={onClose}
|
||||
isActive={isActive}
|
||||
/>
|
||||
);
|
||||
case 'user':
|
||||
return (
|
||||
<ManageUser
|
||||
|
||||
@ -272,6 +272,7 @@ export const RE_TG_LINK = /^tg:(\/\/)?/i;
|
||||
export const RE_TME_LINK = /^(https?:\/\/)?([-a-zA-Z0-9@:%_+~#=]{1,32}\.)?t\.me/i;
|
||||
export const RE_TELEGRAM_LINK = /^(https?:\/\/)?telegram\.org\//i;
|
||||
export const TME_LINK_PREFIX = 'https://t.me/';
|
||||
export const BOT_FATHER_USERNAME = 'botfather';
|
||||
export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE';
|
||||
export const PURCHASE_USERNAME = 'auction';
|
||||
export const TME_WEB_DOMAINS = new Set(['t.me', 'web.t.me', 'a.t.me', 'k.t.me', 'z.t.me']);
|
||||
|
||||
@ -5,8 +5,9 @@ import {
|
||||
type ApiChat, type ApiChatType, type ApiContact, type ApiInputMessageReplyInfo, type ApiPeer, type ApiUrlAuthResult,
|
||||
MAIN_THREAD_ID,
|
||||
} from '../../../api/types';
|
||||
import { ManagementProgress } from '../../../types';
|
||||
|
||||
import { GENERAL_REFETCH_INTERVAL } from '../../../config';
|
||||
import { BOT_FATHER_USERNAME, GENERAL_REFETCH_INTERVAL } from '../../../config';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { translate } from '../../../util/langProvider';
|
||||
@ -19,17 +20,21 @@ import { callApi } from '../../../api/gramjs';
|
||||
import {
|
||||
addActionHandler, getGlobal, setGlobal,
|
||||
} from '../../index';
|
||||
import { addChats, addUsers, removeBlockedUser } from '../../reducers';
|
||||
import {
|
||||
addChats, addUsers, removeBlockedUser, updateManagementProgress, updateUser, updateUserFullInfo,
|
||||
} from '../../reducers';
|
||||
import { replaceInlineBotSettings, replaceInlineBotsIsLoading } from '../../reducers/bots';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import {
|
||||
selectBot, selectChat, selectChatMessage, selectCurrentChat, selectCurrentMessageList, selectDraft,
|
||||
selectIsTrustedBot, selectMessageReplyInfo, selectSendAs, selectTabState, selectUser, selectUserFullInfo,
|
||||
} from '../../selectors';
|
||||
import { fetchChatByUsername } from './chats';
|
||||
|
||||
const GAMEE_URL = 'https://prizes.gamee.com/';
|
||||
const TOP_PEERS_REQUEST_COOLDOWN = 60; // 1 min
|
||||
const runDebouncedForSearch = debounce((cb) => cb(), 500, false);
|
||||
let botFatherId: string | null;
|
||||
|
||||
addActionHandler('clickBotInlineButton', (global, actions, payload): ActionReturnType => {
|
||||
const { messageId, button, tabId = getCurrentTabId() } = payload;
|
||||
@ -1128,3 +1133,66 @@ async function answerCallbackButton<T extends GlobalState>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addActionHandler('setBotInfo', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
bot, name, description: about,
|
||||
tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
let { langCode } = payload;
|
||||
if (!langCode) langCode = global.settings.byKey.language;
|
||||
|
||||
const { currentUserId } = global;
|
||||
if (!currentUserId || !bot) {
|
||||
return;
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
global = updateManagementProgress(global, ManagementProgress.InProgress, tabId);
|
||||
setGlobal(global);
|
||||
|
||||
if (name || about) {
|
||||
const result = await callApi('setBotInfo', {
|
||||
bot, langCode, name, about,
|
||||
});
|
||||
|
||||
if (result) {
|
||||
global = getGlobal();
|
||||
global = updateUser(
|
||||
global,
|
||||
bot.id,
|
||||
{
|
||||
firstName: name,
|
||||
},
|
||||
);
|
||||
global = updateUserFullInfo(global, bot.id, { bio: about });
|
||||
setGlobal(global);
|
||||
}
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
global = updateManagementProgress(global, ManagementProgress.Complete, tabId);
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('startBotFatherConversation', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
param,
|
||||
tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
if (!botFatherId) {
|
||||
const chat = await fetchChatByUsername(global, BOT_FATHER_USERNAME);
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
botFatherId = chat.id;
|
||||
}
|
||||
|
||||
if (param) {
|
||||
actions.startBot({ botId: botFatherId, param });
|
||||
}
|
||||
|
||||
actions.openChat({ id: botFatherId, tabId });
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { ActionReturnType } from '../../types';
|
||||
import { ManagementProgress } from '../../../types';
|
||||
|
||||
import {
|
||||
CUSTOM_BG_CACHE_NAME,
|
||||
@ -34,7 +35,9 @@ import { serializeGlobal } from '../../cache';
|
||||
import {
|
||||
addActionHandler, getGlobal, setGlobal,
|
||||
} from '../../index';
|
||||
import { addUsers, clearGlobalForLockScreen, updatePasscodeSettings } from '../../reducers';
|
||||
import {
|
||||
addUsers, clearGlobalForLockScreen, updateManagementProgress, updatePasscodeSettings,
|
||||
} from '../../reducers';
|
||||
|
||||
addActionHandler('initApi', async (global, actions): Promise<void> => {
|
||||
if (!IS_TEST) {
|
||||
@ -100,14 +103,19 @@ addActionHandler('setAuthPassword', (global, actions, payload): ActionReturnType
|
||||
|
||||
addActionHandler('uploadProfilePhoto', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
file, isFallback, isVideo, videoTs,
|
||||
file, isFallback, isVideo, videoTs, bot,
|
||||
tabId = getCurrentTabId(),
|
||||
} = payload!;
|
||||
|
||||
const result = await callApi('uploadProfilePhoto', file, isFallback, isVideo, videoTs);
|
||||
global = updateManagementProgress(global, ManagementProgress.InProgress, tabId);
|
||||
setGlobal(global);
|
||||
|
||||
const result = await callApi('uploadProfilePhoto', file, isFallback, isVideo, videoTs, bot);
|
||||
if (!result) return;
|
||||
|
||||
global = getGlobal();
|
||||
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = updateManagementProgress(global, ManagementProgress.Complete, tabId);
|
||||
setGlobal(global);
|
||||
|
||||
actions.loadFullUser({ userId: global.currentUserId! });
|
||||
|
||||
@ -15,6 +15,7 @@ export function getUserFirstOrLastName(user?: ApiUser) {
|
||||
|
||||
switch (user.type) {
|
||||
case 'userTypeBot':
|
||||
return user.firstName;
|
||||
case 'userTypeRegular': {
|
||||
return user.firstName || user.lastName;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
import { selectChat, selectIsChatWithSelf } from './chats';
|
||||
import { selectCurrentMessageList } from './messages';
|
||||
import { selectTabState } from './tabs';
|
||||
import { selectUser } from './users';
|
||||
import { selectBot, selectUser } from './users';
|
||||
|
||||
export function selectManagement<T extends GlobalState>(
|
||||
global: T, chatId: string,
|
||||
@ -39,10 +39,16 @@ export function selectCurrentManagementType<T extends GlobalState>(
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
) {
|
||||
const { chatId, threadId } = selectCurrentMessageList(global, tabId) || {};
|
||||
|
||||
if (!chatId || !threadId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const chatBot = selectBot(global, chatId);
|
||||
if (chatBot) {
|
||||
return 'bot';
|
||||
}
|
||||
|
||||
if (isUserId(chatId)) {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
@ -1020,6 +1020,8 @@ export interface ActionPayloads {
|
||||
isFallback?: boolean;
|
||||
videoTs?: number;
|
||||
isVideo?: boolean;
|
||||
bot?: ApiUser;
|
||||
tabId?: number;
|
||||
};
|
||||
goToAuthQrCode: undefined;
|
||||
|
||||
@ -2274,6 +2276,22 @@ export interface ActionPayloads {
|
||||
bio?: string;
|
||||
username?: string;
|
||||
} & WithTabId;
|
||||
updateBotProfile: {
|
||||
photo?: File;
|
||||
firstName?: string;
|
||||
bio?: string;
|
||||
} & WithTabId;
|
||||
setBotInfo: {
|
||||
bot?: ApiUser | undefined;
|
||||
langCode?: string;
|
||||
name?: string | undefined;
|
||||
about?: string | undefined;
|
||||
description?: string | undefined;
|
||||
isMuted?: boolean;
|
||||
} & WithTabId;
|
||||
startBotFatherConversation: {
|
||||
param: string;
|
||||
} & WithTabId;
|
||||
checkUsername: {
|
||||
username: string;
|
||||
} & WithTabId;
|
||||
|
||||
@ -1464,6 +1464,7 @@ channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = U
|
||||
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
|
||||
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.getChannelRecommendations#83b70d97 channel:InputChannel = messages.Chats;
|
||||
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
|
||||
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
|
||||
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
|
||||
bots.invokeWebViewCustomMethod#87fc5e7 bot:InputUser custom_method:string params:DataJSON = DataJSON;
|
||||
|
||||
@ -218,6 +218,7 @@
|
||||
"bots.canSendMessage",
|
||||
"bots.allowSendMessage",
|
||||
"bots.invokeWebViewCustomMethod",
|
||||
"bots.setBotInfo",
|
||||
"payments.getPaymentForm",
|
||||
"payments.getPaymentReceipt",
|
||||
"payments.validateRequestedInfo",
|
||||
|
||||
@ -414,7 +414,7 @@ export enum ManagementScreens {
|
||||
JoinRequests,
|
||||
}
|
||||
|
||||
export type ManagementType = 'user' | 'group' | 'channel';
|
||||
export type ManagementType = 'user' | 'group' | 'channel' | 'bot';
|
||||
|
||||
export type NotifyException = {
|
||||
isMuted: boolean;
|
||||
|
||||
@ -139,6 +139,8 @@ export default {
|
||||
PrivacySettings: 'Privacy and Security',
|
||||
Language: 'Language',
|
||||
FirstName: 'First name (required)',
|
||||
PaymentCheckoutName: 'Name',
|
||||
ChatSetPhotoOrVideo: 'Set Photo',
|
||||
LastName: 'Last name (optional)',
|
||||
UserBio: 'Bio',
|
||||
lng_settings_about_bio: 'Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco',
|
||||
@ -176,6 +178,9 @@ export default {
|
||||
AccDescrChannel: 'Channel',
|
||||
AccDescrGroup: 'Group',
|
||||
Bot: 'bot',
|
||||
BotChangeSettings: 'Change Bot Settings',
|
||||
BotEditCommands: 'Edit Commands',
|
||||
BotEditIntro: 'Edit Intro',
|
||||
ServiceNotifications: 'Service notifications',
|
||||
'LastSeen.TodayAt': 'last seen today at %@',
|
||||
ALongTimeAgo: 'last seen a long time ago',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user