Support stopping and restarting bots (#1316)
This commit is contained in:
parent
7bd89f47d8
commit
85ed696721
@ -3,7 +3,7 @@ import { ApiUser, ApiUserStatus, ApiUserType } from '../../types';
|
|||||||
|
|
||||||
export function buildApiUserFromFull(mtpUserFull: GramJs.UserFull): ApiUser {
|
export function buildApiUserFromFull(mtpUserFull: GramJs.UserFull): ApiUser {
|
||||||
const {
|
const {
|
||||||
about, commonChatsCount, pinnedMsgId, botInfo,
|
about, commonChatsCount, pinnedMsgId, botInfo, blocked,
|
||||||
} = mtpUserFull;
|
} = mtpUserFull;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -12,6 +12,7 @@ export function buildApiUserFromFull(mtpUserFull: GramJs.UserFull): ApiUser {
|
|||||||
bio: about,
|
bio: about,
|
||||||
commonChatsCount,
|
commonChatsCount,
|
||||||
pinnedMessageId: pinnedMsgId,
|
pinnedMessageId: pinnedMsgId,
|
||||||
|
isBlocked: Boolean(blocked),
|
||||||
...(botInfo && { botDescription: botInfo.description }),
|
...(botInfo && { botDescription: botInfo.description }),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
|
|
||||||
import { buildApiUser } from '../apiBuilders/users';
|
import { buildApiUser } from '../apiBuilders/users';
|
||||||
import { buildApiChatFromPreview, getApiChatIdFromMtpPeer } from '../apiBuilders/chats';
|
import { buildApiChatFromPreview, getApiChatIdFromMtpPeer } from '../apiBuilders/chats';
|
||||||
import { buildInputPrivacyKey, buildInputPeer, buildPeer } from '../gramjsBuilders';
|
import { buildInputPrivacyKey, buildInputPeer } from '../gramjsBuilders';
|
||||||
import { invokeRequest, uploadFile, getClient } from './client';
|
import { invokeRequest, uploadFile, getClient } from './client';
|
||||||
import { omitVirtualClassFields } from '../apiBuilders/helpers';
|
import { omitVirtualClassFields } from '../apiBuilders/helpers';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||||
@ -129,9 +129,9 @@ export async function fetchBlockedContacts() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function blockContact(chatOrUserId: number) {
|
export function blockContact(chatOrUserId: number, accessHash?: string) {
|
||||||
return invokeRequest(new GramJs.contacts.Block({
|
return invokeRequest(new GramJs.contacts.Block({
|
||||||
id: buildPeer(chatOrUserId),
|
id: buildInputPeer(chatOrUserId, accessHash),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export interface ApiUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiUserFullInfo {
|
export interface ApiUserFullInfo {
|
||||||
|
isBlocked?: boolean;
|
||||||
bio?: string;
|
bio?: string;
|
||||||
commonChatsCount?: number;
|
commonChatsCount?: number;
|
||||||
botDescription?: string;
|
botDescription?: string;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { GlobalActions } from '../../global/types';
|
|||||||
import { selectIsChatWithSelf, selectUser } from '../../modules/selectors';
|
import { selectIsChatWithSelf, selectUser } from '../../modules/selectors';
|
||||||
import {
|
import {
|
||||||
isChatPrivate,
|
isChatPrivate,
|
||||||
|
isUserBot,
|
||||||
getUserFirstOrLastName,
|
getUserFirstOrLastName,
|
||||||
getPrivateChatUserId,
|
getPrivateChatUserId,
|
||||||
isChatBasicGroup,
|
isChatBasicGroup,
|
||||||
@ -34,6 +35,7 @@ export type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
isChannel: boolean;
|
isChannel: boolean;
|
||||||
isChatWithSelf?: boolean;
|
isChatWithSelf?: boolean;
|
||||||
|
isBot?: boolean;
|
||||||
isPrivateChat: boolean;
|
isPrivateChat: boolean;
|
||||||
isBasicGroup: boolean;
|
isBasicGroup: boolean;
|
||||||
isSuperGroup: boolean;
|
isSuperGroup: boolean;
|
||||||
@ -42,7 +44,9 @@ type StateProps = {
|
|||||||
contactName?: string;
|
contactName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'leaveChannel' | 'deleteHistory' | 'deleteChannel' | 'deleteChatUser'>;
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
|
'leaveChannel' | 'deleteHistory' | 'deleteChannel' | 'deleteChatUser' | 'blockContact'
|
||||||
|
)>;
|
||||||
|
|
||||||
const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
@ -50,6 +54,7 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
isChannel,
|
isChannel,
|
||||||
isPrivateChat,
|
isPrivateChat,
|
||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
|
isBot,
|
||||||
isBasicGroup,
|
isBasicGroup,
|
||||||
isSuperGroup,
|
isSuperGroup,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
@ -61,6 +66,7 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
deleteHistory,
|
deleteHistory,
|
||||||
deleteChannel,
|
deleteChannel,
|
||||||
deleteChatUser,
|
deleteChatUser,
|
||||||
|
blockContact,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const chatTitle = getChatTitle(lang, chat);
|
const chatTitle = getChatTitle(lang, chat);
|
||||||
@ -71,6 +77,13 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onClose();
|
onClose();
|
||||||
}, [deleteHistory, chat.id, onClose]);
|
}, [deleteHistory, chat.id, onClose]);
|
||||||
|
|
||||||
|
const handleDeleteAndStop = useCallback(() => {
|
||||||
|
deleteHistory({ chatId: chat.id, shouldDeleteForAll: true });
|
||||||
|
blockContact({ contactId: chat.id, accessHash: chat.accessHash });
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
}, [deleteHistory, chat.id, chat.accessHash, blockContact, onClose]);
|
||||||
|
|
||||||
const handleDeleteChat = useCallback(() => {
|
const handleDeleteChat = useCallback(() => {
|
||||||
if (isPrivateChat) {
|
if (isPrivateChat) {
|
||||||
deleteHistory({ chatId: chat.id, shouldDeleteForAll: false });
|
deleteHistory({ chatId: chat.id, shouldDeleteForAll: false });
|
||||||
@ -163,6 +176,11 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onCloseAnimationEnd={onCloseAnimationEnd}
|
onCloseAnimationEnd={onCloseAnimationEnd}
|
||||||
>
|
>
|
||||||
{renderMessage()}
|
{renderMessage()}
|
||||||
|
{isBot && (
|
||||||
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteAndStop}>
|
||||||
|
{lang('DeleteAndStop')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{canDeleteForAll && (
|
{canDeleteForAll && (
|
||||||
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
|
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
|
||||||
{contactName ? renderText(lang('ChatList.DeleteForEveryone', contactName)) : lang('DeleteForAll')}
|
{contactName ? renderText(lang('ChatList.DeleteForEveryone', contactName)) : lang('DeleteForAll')}
|
||||||
@ -180,14 +198,17 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(global, { chat }): StateProps => {
|
(global, { chat }): StateProps => {
|
||||||
const isPrivateChat = isChatPrivate(chat.id);
|
const isPrivateChat = isChatPrivate(chat.id);
|
||||||
const isChatWithSelf = selectIsChatWithSelf(global, chat.id);
|
const isChatWithSelf = selectIsChatWithSelf(global, chat.id);
|
||||||
const canDeleteForAll = (isPrivateChat && !isChatWithSelf);
|
const user = isPrivateChat && selectUser(global, getPrivateChatUserId(chat)!);
|
||||||
const contactName = chat && isChatPrivate(chat.id)
|
const isBot = user && isUserBot(user) && !chat.isSupport;
|
||||||
|
const canDeleteForAll = (isPrivateChat && !isChatWithSelf && !isBot);
|
||||||
|
const contactName = isPrivateChat
|
||||||
? getUserFirstOrLastName(selectUser(global, getPrivateChatUserId(chat)!))
|
? getUserFirstOrLastName(selectUser(global, getPrivateChatUserId(chat)!))
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isPrivateChat,
|
isPrivateChat,
|
||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
|
isBot,
|
||||||
isChannel: isChatChannel(chat),
|
isChannel: isChatChannel(chat),
|
||||||
isBasicGroup: isChatBasicGroup(chat),
|
isBasicGroup: isChatBasicGroup(chat),
|
||||||
isSuperGroup: isChatSuperGroup(chat),
|
isSuperGroup: isChatSuperGroup(chat),
|
||||||
@ -197,5 +218,5 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions,
|
(setGlobal, actions): DispatchProps => pick(actions,
|
||||||
['leaveChannel', 'deleteHistory', 'deleteChannel', 'deleteChatUser']),
|
['leaveChannel', 'deleteHistory', 'deleteChannel', 'deleteChatUser', 'blockContact']),
|
||||||
)(DeleteChatModal));
|
)(DeleteChatModal));
|
||||||
|
|||||||
@ -16,7 +16,10 @@ import { pick } from '../../util/iteratees';
|
|||||||
import { isChatChannel, isChatSuperGroup } from '../../modules/helpers';
|
import { isChatChannel, isChatSuperGroup } from '../../modules/helpers';
|
||||||
import {
|
import {
|
||||||
selectChat,
|
selectChat,
|
||||||
selectIsChatBotNotStarted, selectIsChatWithSelf,
|
selectChatBot,
|
||||||
|
selectIsUserBlocked,
|
||||||
|
selectIsChatBotNotStarted,
|
||||||
|
selectIsChatWithSelf,
|
||||||
selectIsInSelectMode,
|
selectIsInSelectMode,
|
||||||
selectIsRightColumnShown,
|
selectIsRightColumnShown,
|
||||||
} from '../../modules/selectors';
|
} from '../../modules/selectors';
|
||||||
@ -36,6 +39,7 @@ interface StateProps {
|
|||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
isRightColumnShown?: boolean;
|
isRightColumnShown?: boolean;
|
||||||
canStartBot?: boolean;
|
canStartBot?: boolean;
|
||||||
|
canRestartBot?: boolean;
|
||||||
canSubscribe?: boolean;
|
canSubscribe?: boolean;
|
||||||
canSearch?: boolean;
|
canSearch?: boolean;
|
||||||
canMute?: boolean;
|
canMute?: boolean;
|
||||||
@ -43,7 +47,7 @@ interface StateProps {
|
|||||||
canLeave?: boolean;
|
canLeave?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'joinChannel' | 'sendBotCommand' | 'openLocalTextSearch'>;
|
type DispatchProps = Pick<GlobalActions, 'joinChannel' | 'sendBotCommand' | 'openLocalTextSearch' | 'restartBot'>;
|
||||||
|
|
||||||
// Chrome breaks layout when focusing input during transition
|
// Chrome breaks layout when focusing input during transition
|
||||||
const SEARCH_FOCUS_DELAY_MS = 400;
|
const SEARCH_FOCUS_DELAY_MS = 400;
|
||||||
@ -54,6 +58,7 @@ const HeaderActions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
noMenu,
|
noMenu,
|
||||||
isChannel,
|
isChannel,
|
||||||
canStartBot,
|
canStartBot,
|
||||||
|
canRestartBot,
|
||||||
canSubscribe,
|
canSubscribe,
|
||||||
canSearch,
|
canSearch,
|
||||||
canMute,
|
canMute,
|
||||||
@ -63,6 +68,7 @@ const HeaderActions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
joinChannel,
|
joinChannel,
|
||||||
sendBotCommand,
|
sendBotCommand,
|
||||||
openLocalTextSearch,
|
openLocalTextSearch,
|
||||||
|
restartBot,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
@ -91,6 +97,10 @@ const HeaderActions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
sendBotCommand({ command: '/start' });
|
sendBotCommand({ command: '/start' });
|
||||||
}, [sendBotCommand]);
|
}, [sendBotCommand]);
|
||||||
|
|
||||||
|
const handleRestartBot = useCallback(() => {
|
||||||
|
restartBot({ chatId });
|
||||||
|
}, [chatId, restartBot]);
|
||||||
|
|
||||||
const handleSearchClick = useCallback(() => {
|
const handleSearchClick = useCallback(() => {
|
||||||
openLocalTextSearch();
|
openLocalTextSearch();
|
||||||
|
|
||||||
@ -132,6 +142,16 @@ const HeaderActions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
{lang('BotStart')}
|
{lang('BotStart')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{!IS_SINGLE_COLUMN_LAYOUT && canRestartBot && (
|
||||||
|
<Button
|
||||||
|
size="tiny"
|
||||||
|
ripple
|
||||||
|
fluid
|
||||||
|
onClick={handleRestartBot}
|
||||||
|
>
|
||||||
|
{lang('BotRestart')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{!IS_SINGLE_COLUMN_LAYOUT && canSearch && (
|
{!IS_SINGLE_COLUMN_LAYOUT && canSearch && (
|
||||||
<Button
|
<Button
|
||||||
round
|
round
|
||||||
@ -166,6 +186,8 @@ const HeaderActions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
isOpen={isMenuOpen}
|
isOpen={isMenuOpen}
|
||||||
anchor={menuPosition}
|
anchor={menuPosition}
|
||||||
isChannel={isChannel}
|
isChannel={isChannel}
|
||||||
|
canStartBot={canStartBot}
|
||||||
|
canRestartBot={canRestartBot}
|
||||||
canSubscribe={canSubscribe}
|
canSubscribe={canSubscribe}
|
||||||
canSearch={canSearch}
|
canSearch={canSearch}
|
||||||
canMute={canMute}
|
canMute={canMute}
|
||||||
@ -192,12 +214,14 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bot = selectChatBot(global, chatId);
|
||||||
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
||||||
const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID;
|
const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID;
|
||||||
const isDiscussionThread = messageListType === 'thread' && threadId !== MAIN_THREAD_ID;
|
const isDiscussionThread = messageListType === 'thread' && threadId !== MAIN_THREAD_ID;
|
||||||
const isRightColumnShown = selectIsRightColumnShown(global);
|
const isRightColumnShown = selectIsRightColumnShown(global);
|
||||||
|
|
||||||
const canStartBot = Boolean(selectIsChatBotNotStarted(global, chatId));
|
const canRestartBot = Boolean(bot && selectIsUserBlocked(global, bot.id));
|
||||||
|
const canStartBot = !canRestartBot && Boolean(selectIsChatBotNotStarted(global, chatId));
|
||||||
const canSubscribe = Boolean(
|
const canSubscribe = Boolean(
|
||||||
isMainThread && chat && (isChannel || isChatSuperGroup(chat)) && chat.isNotJoined,
|
isMainThread && chat && (isChannel || isChatSuperGroup(chat)) && chat.isNotJoined,
|
||||||
);
|
);
|
||||||
@ -219,6 +243,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isChannel,
|
isChannel,
|
||||||
isRightColumnShown,
|
isRightColumnShown,
|
||||||
canStartBot,
|
canStartBot,
|
||||||
|
canRestartBot,
|
||||||
canSubscribe,
|
canSubscribe,
|
||||||
canSearch,
|
canSearch,
|
||||||
canMute,
|
canMute,
|
||||||
@ -227,6 +252,6 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, [
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
'joinChannel', 'sendBotCommand', 'openLocalTextSearch',
|
'joinChannel', 'sendBotCommand', 'openLocalTextSearch', 'restartBot',
|
||||||
]),
|
]),
|
||||||
)(HeaderActions));
|
)(HeaderActions));
|
||||||
|
|||||||
@ -22,7 +22,9 @@ import DeleteChatModal from '../common/DeleteChatModal';
|
|||||||
|
|
||||||
import './HeaderMenuContainer.scss';
|
import './HeaderMenuContainer.scss';
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'updateChatMutedState' | 'enterMessageSelectMode'>;
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
|
'updateChatMutedState' | 'enterMessageSelectMode' | 'sendBotCommand' | 'restartBot'
|
||||||
|
)>;
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
@ -30,6 +32,8 @@ export type OwnProps = {
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
anchor: IAnchorPosition;
|
anchor: IAnchorPosition;
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
|
canStartBot?: boolean;
|
||||||
|
canRestartBot?: boolean;
|
||||||
canSubscribe?: boolean;
|
canSubscribe?: boolean;
|
||||||
canSearch?: boolean;
|
canSearch?: boolean;
|
||||||
canMute?: boolean;
|
canMute?: boolean;
|
||||||
@ -53,6 +57,8 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
anchor,
|
anchor,
|
||||||
isChannel,
|
isChannel,
|
||||||
|
canStartBot,
|
||||||
|
canRestartBot,
|
||||||
canSubscribe,
|
canSubscribe,
|
||||||
canSearch,
|
canSearch,
|
||||||
canMute,
|
canMute,
|
||||||
@ -68,6 +74,8 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onCloseAnimationEnd,
|
onCloseAnimationEnd,
|
||||||
updateChatMutedState,
|
updateChatMutedState,
|
||||||
enterMessageSelectMode,
|
enterMessageSelectMode,
|
||||||
|
sendBotCommand,
|
||||||
|
restartBot,
|
||||||
}) => {
|
}) => {
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(true);
|
const [isMenuOpen, setIsMenuOpen] = useState(true);
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@ -90,6 +98,14 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onClose();
|
onClose();
|
||||||
}, [onClose]);
|
}, [onClose]);
|
||||||
|
|
||||||
|
const handleStartBot = useCallback(() => {
|
||||||
|
sendBotCommand({ command: '/start' });
|
||||||
|
}, [sendBotCommand]);
|
||||||
|
|
||||||
|
const handleRestartBot = useCallback(() => {
|
||||||
|
restartBot({ chatId });
|
||||||
|
}, [chatId, restartBot]);
|
||||||
|
|
||||||
const handleToggleMuteClick = useCallback(() => {
|
const handleToggleMuteClick = useCallback(() => {
|
||||||
updateChatMutedState({ chatId, isMuted: !isMuted });
|
updateChatMutedState({ chatId, isMuted: !isMuted });
|
||||||
closeMenu();
|
closeMenu();
|
||||||
@ -127,6 +143,22 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
style={`left: ${x}px;top: ${y}px;`}
|
style={`left: ${x}px;top: ${y}px;`}
|
||||||
onClose={closeMenu}
|
onClose={closeMenu}
|
||||||
>
|
>
|
||||||
|
{IS_SINGLE_COLUMN_LAYOUT && canStartBot && (
|
||||||
|
<MenuItem
|
||||||
|
icon="bots"
|
||||||
|
onClick={handleStartBot}
|
||||||
|
>
|
||||||
|
{lang('BotStart')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{IS_SINGLE_COLUMN_LAYOUT && canRestartBot && (
|
||||||
|
<MenuItem
|
||||||
|
icon="bots"
|
||||||
|
onClick={handleRestartBot}
|
||||||
|
>
|
||||||
|
{lang('BotRestart')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
{IS_SINGLE_COLUMN_LAYOUT && canSubscribe && (
|
{IS_SINGLE_COLUMN_LAYOUT && canSubscribe && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={isChannel ? 'channel' : 'group'}
|
icon={isChannel ? 'channel' : 'group'}
|
||||||
@ -200,5 +232,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(setGlobal, actions): DispatchProps => pick(actions, [
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
'updateChatMutedState',
|
'updateChatMutedState',
|
||||||
'enterMessageSelectMode',
|
'enterMessageSelectMode',
|
||||||
|
'sendBotCommand',
|
||||||
|
'restartBot',
|
||||||
]),
|
]),
|
||||||
)(HeaderMenuContainer));
|
)(HeaderMenuContainer));
|
||||||
|
|||||||
@ -446,7 +446,7 @@ export default memo(withGlobal(
|
|||||||
messageListType,
|
messageListType,
|
||||||
originChatId: originChat ? originChat.id : chatId,
|
originChatId: originChat ? originChat.id : chatId,
|
||||||
isPrivate: isChatPrivate(chatId),
|
isPrivate: isChatPrivate(chatId),
|
||||||
canPost: !isPinnedMessageList && (!chat || canPost) && (!isBotNotStarted || IS_SINGLE_COLUMN_LAYOUT),
|
canPost: !isPinnedMessageList && (!chat || canPost) && !isBotNotStarted,
|
||||||
isPinnedMessageList,
|
isPinnedMessageList,
|
||||||
isScheduledMessageList,
|
isScheduledMessageList,
|
||||||
currentUserBannedRights: chat && chat.currentUserBannedRights,
|
currentUserBannedRights: chat && chat.currentUserBannedRights,
|
||||||
|
|||||||
@ -487,7 +487,7 @@ export type ActionTypes = (
|
|||||||
'loadStickersForEmoji' | 'clearStickersForEmoji' | 'loadEmojiKeywords' |
|
'loadStickersForEmoji' | 'clearStickersForEmoji' | 'loadEmojiKeywords' |
|
||||||
// bots
|
// bots
|
||||||
'clickInlineButton' | 'sendBotCommand' | 'loadTopInlineBots' | 'queryInlineBot' | 'sendInlineBotResult' |
|
'clickInlineButton' | 'sendBotCommand' | 'loadTopInlineBots' | 'queryInlineBot' | 'sendInlineBotResult' |
|
||||||
'resetInlineBot' |
|
'resetInlineBot' | 'restartBot' |
|
||||||
// misc
|
// misc
|
||||||
'openMediaViewer' | 'closeMediaViewer' | 'openAudioPlayer' | 'closeAudioPlayer' | 'openPollModal' | 'closePollModal' |
|
'openMediaViewer' | 'closeMediaViewer' | 'openAudioPlayer' | 'closeAudioPlayer' | 'openPollModal' | 'closePollModal' |
|
||||||
'loadWebPagePreview' | 'clearWebPagePreview' | 'loadWallpapers' | 'uploadWallpaper' | 'setDeviceToken' |
|
'loadWebPagePreview' | 'clearWebPagePreview' | 'loadWallpapers' | 'uploadWallpaper' | 'setDeviceToken' |
|
||||||
|
|||||||
@ -8,9 +8,10 @@ import { InlineBotSettings } from '../../../types';
|
|||||||
import { RE_TME_INVITE_LINK, RE_TME_LINK } from '../../../config';
|
import { RE_TME_INVITE_LINK, RE_TME_LINK } from '../../../config';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import {
|
import {
|
||||||
selectChat, selectChatMessage, selectCurrentChat, selectCurrentMessageList, selectReplyingToId, selectUser,
|
selectChat, selectChatBot, selectChatMessage, selectCurrentChat, selectCurrentMessageList,
|
||||||
|
selectReplyingToId, selectUser,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { addChats, addUsers } from '../../reducers';
|
import { addChats, addUsers, removeBlockedContact } from '../../reducers';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||||
import { debounce } from '../../../util/schedulers';
|
import { debounce } from '../../../util/schedulers';
|
||||||
import { replaceInlineBotSettings, replaceInlineBotsIsLoading } from '../../reducers/bots';
|
import { replaceInlineBotSettings, replaceInlineBotsIsLoading } from '../../reducers/bots';
|
||||||
@ -74,6 +75,26 @@ addReducer('sendBotCommand', (global, actions, payload) => {
|
|||||||
void sendBotCommand(chat, currentUserId, command);
|
void sendBotCommand(chat, currentUserId, command);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addReducer('restartBot', (global, actions, payload) => {
|
||||||
|
const { chatId } = payload;
|
||||||
|
const { currentUserId } = global;
|
||||||
|
const chat = selectCurrentChat(global);
|
||||||
|
const bot = currentUserId && selectChatBot(global, chatId);
|
||||||
|
if (!currentUserId || !chat || !bot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const result = await callApi('unblockContact', bot.id, bot.accessHash);
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setGlobal(removeBlockedContact(getGlobal(), bot.id));
|
||||||
|
void sendBotCommand(chat, currentUserId, '/start');
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
addReducer('loadTopInlineBots', (global) => {
|
addReducer('loadTopInlineBots', (global) => {
|
||||||
const { serverTimeOffset } = global;
|
const { serverTimeOffset } = global;
|
||||||
const { hash, lastRequestedAt } = global.topInlineBots;
|
const { hash, lastRequestedAt } = global.topInlineBots;
|
||||||
|
|||||||
@ -217,10 +217,10 @@ addReducer('loadBlockedContacts', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addReducer('blockContact', (global, actions, payload) => {
|
addReducer('blockContact', (global, actions, payload) => {
|
||||||
const { contactId } = payload!;
|
const { contactId, accessHash } = payload!;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const result = await callApi('blockContact', contactId);
|
const result = await callApi('blockContact', contactId, accessHash);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {
|
|||||||
ISettings, IThemeSettings, ThemeKey, NotifyException,
|
ISettings, IThemeSettings, ThemeKey, NotifyException,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import { ApiNotifyException } from '../../api/types';
|
import { ApiNotifyException } from '../../api/types';
|
||||||
|
import { updateUserBlockedState } from './users';
|
||||||
|
|
||||||
export function replaceSettings(global: GlobalState, newSettings?: Partial<ISettings>): GlobalState {
|
export function replaceSettings(global: GlobalState, newSettings?: Partial<ISettings>): GlobalState {
|
||||||
return {
|
return {
|
||||||
@ -87,6 +88,8 @@ export function updateNotifySettings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function addBlockedContact(global: GlobalState, contactId: number): GlobalState {
|
export function addBlockedContact(global: GlobalState, contactId: number): GlobalState {
|
||||||
|
global = updateUserBlockedState(global, contactId, true);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...global,
|
...global,
|
||||||
blocked: {
|
blocked: {
|
||||||
@ -98,6 +101,8 @@ export function addBlockedContact(global: GlobalState, contactId: number): Globa
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function removeBlockedContact(global: GlobalState, contactId: number): GlobalState {
|
export function removeBlockedContact(global: GlobalState, contactId: number): GlobalState {
|
||||||
|
global = updateUserBlockedState(global, contactId, false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...global,
|
...global,
|
||||||
blocked: {
|
blocked: {
|
||||||
|
|||||||
@ -169,3 +169,19 @@ export function updateUserSearchFetchingStatus(
|
|||||||
fetchingStatus: newState,
|
fetchingStatus: newState,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateUserBlockedState(global: GlobalState, userId: number, isBlocked: boolean) {
|
||||||
|
const { byId } = global.users;
|
||||||
|
const user = byId[userId];
|
||||||
|
if (!user || !user.fullInfo) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateUser(global, userId, {
|
||||||
|
...user,
|
||||||
|
fullInfo: {
|
||||||
|
...user.fullInfo,
|
||||||
|
isBlocked,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,12 @@ export function selectUser(global: GlobalState, userId: number): ApiUser | undef
|
|||||||
return global.users.byId[userId];
|
return global.users.byId[userId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectIsUserBlocked(global: GlobalState, userId: number) {
|
||||||
|
const user = selectUser(global, userId);
|
||||||
|
|
||||||
|
return user && user.fullInfo && user.fullInfo.isBlocked;
|
||||||
|
}
|
||||||
|
|
||||||
// Slow, not to be used in `withGlobal`
|
// Slow, not to be used in `withGlobal`
|
||||||
export function selectUserByUsername(global: GlobalState, username: string) {
|
export function selectUserByUsername(global: GlobalState, username: string) {
|
||||||
const usernameLowered = username.toLowerCase();
|
const usernameLowered = username.toLowerCase();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user