Bots: Better forum view handling (#6705)

This commit is contained in:
zubiden 2026-02-22 23:43:55 +01:00 committed by Alexander Zinchuk
parent 48b722939b
commit fc1883f1cc
13 changed files with 136 additions and 111 deletions

View File

@ -112,7 +112,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
const { const {
id, firstName, lastName, fake, scam, support, closeFriend, storiesUnavailable, id, firstName, lastName, fake, scam, support, closeFriend, storiesUnavailable,
bot, botActiveUsers, botVerificationIcon, botInlinePlaceholder, botAttachMenu, botCanEdit, bot, botActiveUsers, botVerificationIcon, botInlinePlaceholder, botAttachMenu, botCanEdit,
sendPaidMessagesStars, profileColor, botForumView, sendPaidMessagesStars, profileColor, botForumView, botForumCanManageTopics,
} = mtpUser; } = mtpUser;
const storiesMaxId = mtpUser.storiesMaxId?.maxId; const storiesMaxId = mtpUser.storiesMaxId?.maxId;
const hasVideoAvatar = mtpUser.photo instanceof GramJs.UserProfilePhoto ? Boolean(mtpUser.photo.hasVideo) : undefined; const hasVideoAvatar = mtpUser.photo instanceof GramJs.UserProfilePhoto ? Boolean(mtpUser.photo.hasVideo) : undefined;
@ -157,6 +157,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
profileColor: profileColor && buildApiPeerColor(profileColor), profileColor: profileColor && buildApiPeerColor(profileColor),
paidMessagesStars: toJSNumber(sendPaidMessagesStars), paidMessagesStars: toJSNumber(sendPaidMessagesStars),
isBotForum: botForumView, isBotForum: botForumView,
canManageBotForumTopics: botForumCanManageTopics,
}; };
} }

View File

@ -48,6 +48,7 @@ export interface ApiUser {
botVerificationIconId?: string; botVerificationIconId?: string;
paidMessagesStars?: number; paidMessagesStars?: number;
isBotForum?: boolean; isBotForum?: boolean;
canManageBotForumTopics?: boolean;
} }
export interface ApiUserFullInfo { export interface ApiUserFullInfo {

View File

@ -1374,6 +1374,7 @@
"ComposerPlaceholderBroadcastSilent" = "Silent Broadcast"; "ComposerPlaceholderBroadcastSilent" = "Silent Broadcast";
"ComposerPlaceholderTopic" = "Message in {topic}"; "ComposerPlaceholderTopic" = "Message in {topic}";
"ComposerPlaceholderTopicGeneral" = "Message in General"; "ComposerPlaceholderTopicGeneral" = "Message in General";
"ComposerPlaceholderBotTopicGeneral" = "Off-thread message";
"ComposerStoryPlaceholderLocked" = "Replies restricted"; "ComposerStoryPlaceholderLocked" = "Replies restricted";
"ComposerPlaceholderNoText" = "Text not allowed"; "ComposerPlaceholderNoText" = "Text not allowed";
"AriaComposerCancelVoice" = "Cancel voice recording"; "AriaComposerCancelVoice" = "Cancel voice recording";

View File

@ -222,96 +222,96 @@ type OwnProps = {
onBlur?: NoneToVoidFunction; onBlur?: NoneToVoidFunction;
}; };
type StateProps = type StateProps = {
{ isOnActiveTab: boolean;
isOnActiveTab: boolean; editingMessage?: ApiMessage;
editingMessage?: ApiMessage; chat?: ApiChat;
chat?: ApiChat; user?: ApiUser;
chatFullInfo?: ApiChatFullInfo; chatFullInfo?: ApiChatFullInfo;
draft?: ApiDraft; draft?: ApiDraft;
replyToTopic?: ApiTopic; replyToTopic?: ApiTopic;
currentMessageList?: MessageList; currentMessageList?: MessageList;
isChatWithBot?: boolean; isChatWithBot?: boolean;
isChatWithSelf?: boolean; isChatWithSelf?: boolean;
isChannel?: boolean; isChannel?: boolean;
isForCurrentMessageList: boolean; isForCurrentMessageList: boolean;
isRightColumnShown?: boolean; isRightColumnShown?: boolean;
isSelectModeActive?: boolean; isSelectModeActive?: boolean;
isReactionPickerOpen?: boolean; isReactionPickerOpen?: boolean;
shouldDisplayGiftsButton?: boolean; shouldDisplayGiftsButton?: boolean;
isForwarding?: boolean; isForwarding?: boolean;
isReplying?: boolean; isReplying?: boolean;
hasSuggestedPost?: boolean; hasSuggestedPost?: boolean;
forwardedMessagesCount?: number; forwardedMessagesCount?: number;
pollModal: TabState['pollModal']; pollModal: TabState['pollModal'];
todoListModal: TabState['todoListModal']; todoListModal: TabState['todoListModal'];
botKeyboardMessageId?: number; botKeyboardMessageId?: number;
botKeyboardPlaceholder?: string; botKeyboardPlaceholder?: string;
withScheduledButton?: boolean; withScheduledButton?: boolean;
isInScheduledList?: boolean; isInScheduledList?: boolean;
canScheduleUntilOnline?: boolean; canScheduleUntilOnline?: boolean;
stickersForEmoji?: ApiSticker[]; stickersForEmoji?: ApiSticker[];
customEmojiForEmoji?: ApiSticker[]; customEmojiForEmoji?: ApiSticker[];
currentUserId?: string; currentUserId?: string;
currentUser?: ApiUser; currentUser?: ApiUser;
recentEmojis: string[]; recentEmojis: string[];
contentToBeScheduled?: TabState['contentToBeScheduled']; contentToBeScheduled?: TabState['contentToBeScheduled'];
shouldSuggestStickers?: boolean; shouldSuggestStickers?: boolean;
shouldSuggestCustomEmoji?: boolean; shouldSuggestCustomEmoji?: boolean;
baseEmojiKeywords?: Record<string, string[]>; baseEmojiKeywords?: Record<string, string[]>;
emojiKeywords?: Record<string, string[]>; emojiKeywords?: Record<string, string[]>;
topInlineBotIds?: string[]; topInlineBotIds?: string[];
isInlineBotLoading: boolean; isInlineBotLoading: boolean;
inlineBots?: Record<string, false | InlineBotSettings>; inlineBots?: Record<string, false | InlineBotSettings>;
botCommands?: ApiBotCommand[] | false; botCommands?: ApiBotCommand[] | false;
botMenuButton?: ApiBotMenuButton; botMenuButton?: ApiBotMenuButton;
sendAsPeer?: ApiPeer; sendAsPeer?: ApiPeer;
sendAsId?: string; sendAsId?: string;
editingDraft?: ApiFormattedText; editingDraft?: ApiFormattedText;
requestedDraft?: ApiFormattedText; requestedDraft?: ApiFormattedText;
requestedDraftFiles?: File[]; requestedDraftFiles?: File[];
attachBots: GlobalState['attachMenu']['bots']; attachBots: GlobalState['attachMenu']['bots'];
attachMenuPeerType?: ApiAttachMenuPeerType; attachMenuPeerType?: ApiAttachMenuPeerType;
theme: ThemeKey; theme: ThemeKey;
fileSizeLimit: number; fileSizeLimit: number;
captionLimit: number; captionLimit: number;
isCurrentUserPremium?: boolean; isCurrentUserPremium?: boolean;
canSendVoiceByPrivacy?: boolean; canSendVoiceByPrivacy?: boolean;
attachmentSettings: GlobalState['attachmentSettings']; attachmentSettings: GlobalState['attachmentSettings'];
slowMode?: ApiChatFullInfo['slowMode']; slowMode?: ApiChatFullInfo['slowMode'];
shouldUpdateStickerSetOrder?: boolean; shouldUpdateStickerSetOrder?: boolean;
availableReactions?: ApiAvailableReaction[]; availableReactions?: ApiAvailableReaction[];
topReactions?: ApiReaction[]; topReactions?: ApiReaction[];
canPlayAnimatedEmojis?: boolean; canPlayAnimatedEmojis?: boolean;
canBuyPremium?: boolean; canBuyPremium?: boolean;
shouldCollectDebugLogs?: boolean; shouldCollectDebugLogs?: boolean;
sentStoryReaction?: ApiReaction; sentStoryReaction?: ApiReaction;
stealthMode?: ApiStealthMode; stealthMode?: ApiStealthMode;
canSendOneTimeMedia?: boolean; canSendOneTimeMedia?: boolean;
quickReplyMessages?: Record<number, ApiMessage>; quickReplyMessages?: Record<number, ApiMessage>;
quickReplies?: Record<number, ApiQuickReply>; quickReplies?: Record<number, ApiQuickReply>;
canSendQuickReplies?: boolean; canSendQuickReplies?: boolean;
webPagePreview?: ApiWebPage; webPagePreview?: ApiWebPage;
noWebPage?: boolean; noWebPage?: boolean;
isContactRequirePremium?: boolean; isContactRequirePremium?: boolean;
paidMessagesStars?: number; paidMessagesStars?: number;
effect?: ApiAvailableEffect; effect?: ApiAvailableEffect;
effectReactions?: ApiReaction[]; effectReactions?: ApiReaction[];
areEffectsSupported?: boolean; areEffectsSupported?: boolean;
canPlayEffect?: boolean; canPlayEffect?: boolean;
shouldPlayEffect?: boolean; shouldPlayEffect?: boolean;
maxMessageLength: number; maxMessageLength: number;
shouldPaidMessageAutoApprove?: boolean; shouldPaidMessageAutoApprove?: boolean;
isSilentPosting?: boolean; isSilentPosting?: boolean;
isPaymentMessageConfirmDialogOpen: boolean; isPaymentMessageConfirmDialogOpen: boolean;
starsBalance: number; starsBalance: number;
isStarsBalanceModalOpen: boolean; isStarsBalanceModalOpen: boolean;
disallowedGifts?: ApiDisallowedGifts; disallowedGifts?: ApiDisallowedGifts;
isAccountFrozen?: boolean; isAccountFrozen?: boolean;
isAppConfigLoaded?: boolean; isAppConfigLoaded?: boolean;
insertingPeerIdMention?: string; insertingPeerIdMention?: string;
pollMaxAnswers?: number; pollMaxAnswers?: number;
}; };
enum MainButtonState { enum MainButtonState {
Send = 'send', Send = 'send',
@ -352,6 +352,7 @@ const Composer: FC<OwnProps & StateProps> = ({
draft, draft,
chat, chat,
chatFullInfo, chatFullInfo,
user,
replyToTopic, replyToTopic,
isForCurrentMessageList, isForCurrentMessageList,
isCurrentUserPremium, isCurrentUserPremium,
@ -1758,6 +1759,10 @@ const Composer: FC<OwnProps & StateProps> = ({
return lang('ComposerPlaceholderAnonymous'); return lang('ComposerPlaceholderAnonymous');
} }
if (chat?.isBotForum && !user?.canManageBotForumTopics && threadId === MAIN_THREAD_ID) {
return lang('ComposerPlaceholderBotTopicGeneral');
}
if (chat?.isForum && !chat.isBotForum && chat.isForumAsMessages && threadId === MAIN_THREAD_ID) { if (chat?.isForum && !chat.isBotForum && chat.isForumAsMessages && threadId === MAIN_THREAD_ID) {
return replyToTopic return replyToTopic
? lang('ComposerPlaceholderTopic', { topic: replyToTopic.title }) ? lang('ComposerPlaceholderTopic', { topic: replyToTopic.title })
@ -1775,7 +1780,7 @@ const Composer: FC<OwnProps & StateProps> = ({
}, [ }, [
activeVoiceRecording, botKeyboardPlaceholder, chat, inputPlaceholder, isChannel, isComposerBlocked, activeVoiceRecording, botKeyboardPlaceholder, chat, inputPlaceholder, isChannel, isComposerBlocked,
isInStoryViewer, isSilentPosting, lang, replyToTopic, isReplying, threadId, windowWidth, paidMessagesStars, isInStoryViewer, isSilentPosting, lang, replyToTopic, isReplying, threadId, windowWidth, paidMessagesStars,
hasSuggestedPost, slowModePlaceholder, stealthMode?.activeUntil, hasSuggestedPost, slowModePlaceholder, stealthMode?.activeUntil, user?.canManageBotForumTopics,
]); ]);
useEffect(() => { useEffect(() => {
@ -2635,6 +2640,7 @@ export default memo(withGlobal<OwnProps>(
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType), editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
draft, draft,
chat, chat,
user,
isChatWithBot, isChatWithBot,
isChatWithSelf, isChatWithSelf,
isForCurrentMessageList, isForCurrentMessageList,

View File

@ -204,6 +204,8 @@ const Chat: FC<OwnProps & StateProps> = ({
const { isForum, isForumAsMessages, isMonoforum } = chat || {}; const { isForum, isForumAsMessages, isMonoforum } = chat || {};
const shouldForceNonForumView = chat?.isBotForum && listedTopicIds && !listedTopicIds.length;
useEnsureMessage(isSavedDialog ? currentUserId : chatId, lastMessageId, lastMessage); useEnsureMessage(isSavedDialog ? currentUserId : chatId, lastMessageId, lastMessage);
const tagFolderIds = useMemo(() => { const tagFolderIds = useMemo(() => {
@ -244,6 +246,7 @@ const Chat: FC<OwnProps & StateProps> = ({
onReorderAnimationEnd, onReorderAnimationEnd,
topicIds: listedTopicIds, topicIds: listedTopicIds,
hasTags: shouldRenderTags, hasTags: shouldRenderTags,
shouldForceNonForumView,
}); });
const getIsForumPanelClosed = useSelectorSignal(selectIsForumPanelClosed); const getIsForumPanelClosed = useSelectorSignal(selectIsForumPanelClosed);
@ -255,7 +258,7 @@ const Chat: FC<OwnProps & StateProps> = ({
return; return;
} }
const noForumTopicPanel = isMobile && isForumAsMessages; const noForumTopicPanel = (isMobile && isForumAsMessages) || shouldForceNonForumView;
if (isMobile) { if (isMobile) {
setShouldCloseRightColumn({ value: true }); setShouldCloseRightColumn({ value: true });
@ -288,7 +291,7 @@ const Chat: FC<OwnProps & StateProps> = ({
openForumPanel({ chatId }, { forceOnHeavyAnimation: true }); openForumPanel({ chatId }, { forceOnHeavyAnimation: true });
} }
if (!isForumAsMessages) return; if (!isForumAsMessages && !shouldForceNonForumView) return;
} }
} }
@ -397,7 +400,7 @@ const Chat: FC<OwnProps & StateProps> = ({
const chatClassName = buildClassName( const chatClassName = buildClassName(
'Chat chat-item-clickable', 'Chat chat-item-clickable',
isUserId(chatId) ? 'private' : 'group', isUserId(chatId) ? 'private' : 'group',
isForum && 'forum', isForum && !shouldForceNonForumView && 'forum',
isSelected && 'selected', isSelected && 'selected',
isSelectedForum && 'selected-forum', isSelectedForum && 'selected-forum',
isPreview && 'standalone', isPreview && 'standalone',

View File

@ -53,6 +53,7 @@ export default function useChatListEntry({
isSavedDialog, isSavedDialog,
isPreview, isPreview,
hasTags, hasTags,
shouldForceNonForumView,
onReorderAnimationEnd, onReorderAnimationEnd,
}: { }: {
chat?: ApiChat; chat?: ApiChat;
@ -74,6 +75,7 @@ export default function useChatListEntry({
orderDiff: number; orderDiff: number;
shiftDiff: number; shiftDiff: number;
withInterfaceAnimations?: boolean; withInterfaceAnimations?: boolean;
shouldForceNonForumView?: boolean;
onReorderAnimationEnd?: NoneToVoidFunction; onReorderAnimationEnd?: NoneToVoidFunction;
}) { }) {
const lang = useLang(); const lang = useLang();
@ -151,7 +153,8 @@ export default function useChatListEntry({
]); ]);
function renderSubtitle() { function renderSubtitle() {
if (chat?.isForum && !isTopic) { const shouldRenderAsForum = chat?.isForum && !isTopic && !shouldForceNonForumView;
if (shouldRenderAsForum) {
return ( return (
<ChatForumLastMessage <ChatForumLastMessage
chat={chat} chat={chat}

View File

@ -114,6 +114,7 @@ type StateProps = {
isMuted?: boolean; isMuted?: boolean;
isTopic?: boolean; isTopic?: boolean;
isForum?: boolean; isForum?: boolean;
isBotForum?: boolean;
isForumAsMessages?: true; isForumAsMessages?: true;
canAddContact?: boolean; canAddContact?: boolean;
canDeleteChat?: boolean; canDeleteChat?: boolean;
@ -148,6 +149,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
withForumActions, withForumActions,
isTopic, isTopic,
isForum, isForum,
isBotForum,
isForumAsMessages, isForumAsMessages,
isChatInfoShown, isChatInfoShown,
canStartBot, canStartBot,
@ -627,7 +629,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
{oldLang('lng_forum_topic_edit')} {oldLang('lng_forum_topic_edit')}
</MenuItem> </MenuItem>
)} )}
{isMobile && !withForumActions && isForum && !isTopic && ( {isMobile && !withForumActions && isForum && !isBotForum && !isTopic && (
<MenuItem <MenuItem
icon="forums" icon="forums"
onClick={handleViewAsTopicsClick} onClick={handleViewAsTopicsClick}
@ -644,7 +646,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
<div className="right-badge">{pendingJoinRequests}</div> <div className="right-badge">{pendingJoinRequests}</div>
</MenuItem> </MenuItem>
)} )}
{withForumActions && !isTopic && !isForumAsMessages && ( {withForumActions && !isTopic && !isBotForum && !isForumAsMessages && (
<MenuItem <MenuItem
icon="message" icon="message"
onClick={handleOpenAsMessages} onClick={handleOpenAsMessages}
@ -883,6 +885,7 @@ export default memo(withGlobal<OwnProps>(
isPrivate, isPrivate,
isTopic: chat?.isForum && !isMainThread, isTopic: chat?.isForum && !isMainThread,
isForum: chat?.isForum, isForum: chat?.isForum,
isBotForum: chat?.isBotForum,
isForumAsMessages: chat?.isForumAsMessages, isForumAsMessages: chat?.isForumAsMessages,
canAddContact, canAddContact,
canDeleteChat: getCanDeleteChat(chat), canDeleteChat: getCanDeleteChat(chat),

View File

@ -45,6 +45,7 @@ import {
selectTabState, selectTabState,
selectTopic, selectTopic,
selectTranslationLanguage, selectTranslationLanguage,
selectUser,
selectUserFullInfo, selectUserFullInfo,
} from '../../global/selectors'; } from '../../global/selectors';
import { selectIsChatRestricted } from '../../global/selectors/chats'; import { selectIsChatRestricted } from '../../global/selectors/chats';
@ -149,7 +150,7 @@ type StateProps = {
translationLanguage?: string; translationLanguage?: string;
shouldAutoTranslate?: boolean; shouldAutoTranslate?: boolean;
isActive?: boolean; isActive?: boolean;
isBotForum?: boolean; canManageBotForumTopics?: boolean;
shouldScrollToBottom?: boolean; shouldScrollToBottom?: boolean;
}; };
@ -199,7 +200,7 @@ const MessageList = ({
canPost, canPost,
isSynced, isSynced,
isActive, isActive,
isBotForum, canManageBotForumTopics,
shouldScrollToBottom, shouldScrollToBottom,
// eslint-disable-next-line @typescript-eslint/no-shadow // eslint-disable-next-line @typescript-eslint/no-shadow
isChatMonoforum, isChatMonoforum,
@ -812,7 +813,7 @@ const MessageList = ({
Content.StarsRequired Content.StarsRequired
) : isContactRequirePremium && !hasMessages ? ( ) : isContactRequirePremium && !hasMessages ? (
Content.PremiumRequired Content.PremiumRequired
) : (isBot || isNonContact) && !hasMessages ? ( ) : (isBot || isNonContact) && !hasMessages && threadId === MAIN_THREAD_ID ? (
Content.AccountInfo Content.AccountInfo
) : shouldRenderGreeting ? ( ) : shouldRenderGreeting ? (
Content.ContactGreeting Content.ContactGreeting
@ -878,7 +879,7 @@ const MessageList = ({
noAppearanceAnimation={!messageGroups || !shouldAnimateAppearanceRef.current} noAppearanceAnimation={!messageGroups || !shouldAnimateAppearanceRef.current}
isQuickPreview={isQuickPreview} isQuickPreview={isQuickPreview}
canPost={canPost} canPost={canPost}
isBotForum={isBotForum} canManageBotForumTopics={canManageBotForumTopics}
shouldScrollToBottom={shouldScrollToBottom} shouldScrollToBottom={shouldScrollToBottom}
onScrollDownToggle={onScrollDownToggle} onScrollDownToggle={onScrollDownToggle}
onNotchToggle={onNotchToggle} onNotchToggle={onNotchToggle}
@ -910,6 +911,7 @@ export default memo(withGlobal<OwnProps>(
const tabState = selectTabState(global); const tabState = selectTabState(global);
const currentUserId = global.currentUserId!; const currentUserId = global.currentUserId!;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const user = selectUser(global, chatId);
const userFullInfo = selectUserFullInfo(global, chatId); const userFullInfo = selectUserFullInfo(global, chatId);
const readState = selectThreadReadState(global, chatId, threadId); const readState = selectThreadReadState(global, chatId, threadId);
if (!chat) { if (!chat) {
@ -1014,7 +1016,7 @@ export default memo(withGlobal<OwnProps>(
canTranslate, canTranslate,
translationLanguage, translationLanguage,
shouldAutoTranslate, shouldAutoTranslate,
isBotForum: chat.isBotForum, canManageBotForumTopics: chat.isBotForum && user?.canManageBotForumTopics,
shouldScrollToBottom, shouldScrollToBottom,
}; };
}, },

View File

@ -60,7 +60,7 @@ interface OwnProps {
withUsers: boolean; withUsers: boolean;
isChannelChat: boolean | undefined; isChannelChat: boolean | undefined;
isChatMonoforum?: boolean; isChatMonoforum?: boolean;
isBotForum?: boolean; canManageBotForumTopics?: boolean;
isEmptyThread?: boolean; isEmptyThread?: boolean;
isComments?: boolean; isComments?: boolean;
noAvatars: boolean; noAvatars: boolean;
@ -101,7 +101,7 @@ const MessageListContent = ({
withUsers, withUsers,
isChannelChat, isChannelChat,
isChatMonoforum, isChatMonoforum,
isBotForum, canManageBotForumTopics,
noAvatars, noAvatars,
containerRef, containerRef,
anchorIdRef, anchorIdRef,
@ -256,7 +256,7 @@ const MessageListContent = ({
}; };
const renderBotForumTopicAction = () => { const renderBotForumTopicAction = () => {
if (!isBotForum || threadId !== MAIN_THREAD_ID) return undefined; if (!canManageBotForumTopics || threadId !== MAIN_THREAD_ID) return undefined;
return ( return (
<div className={buildClassName('local-action-message', actionMessageStyles.root)} key="botforum-new-topic"> <div className={buildClassName('local-action-message', actionMessageStyles.root)} key="botforum-new-topic">
<div className={actionMessageStyles.contentBox}> <div className={actionMessageStyles.contentBox}>

View File

@ -392,6 +392,7 @@ addActionHandler('sendMessage', async (global, actions, payload): Promise<void>
} }
const chat = selectChat(global, chatId!)!; const chat = selectChat(global, chatId!)!;
const user = selectUser(global, chatId!);
const draft = selectDraft(global, chatId!, threadId!); const draft = selectDraft(global, chatId!, threadId!);
const isForwarding = selectTabState(global, tabId).forwardMessages?.messageIds?.length; const isForwarding = selectTabState(global, tabId).forwardMessages?.messageIds?.length;
@ -450,7 +451,8 @@ addActionHandler('sendMessage', async (global, actions, payload): Promise<void>
suggestedMedia = suggestedMessage.content; suggestedMedia = suggestedMessage.content;
} }
if (chat.isBotForum && threadId === MAIN_THREAD_ID && replyInfo?.type === 'message') { if (chat.isBotForum && threadId === MAIN_THREAD_ID && replyInfo?.type === 'message'
&& user?.canManageBotForumTopics) {
const replyMessage = selectChatMessage(global, chatId!, replyInfo.replyToMsgId); const replyMessage = selectChatMessage(global, chatId!, replyInfo.replyToMsgId);
const replyThreadId = replyMessage && selectThreadIdFromMessage(global, replyMessage); const replyThreadId = replyMessage && selectThreadIdFromMessage(global, replyMessage);
actions.openThread({ actions.openThread({
@ -490,7 +492,9 @@ addActionHandler('sendMessage', async (global, actions, payload): Promise<void>
} }
// Create new bot forum topic // Create new bot forum topic
if (chat.isBotForum && threadId === MAIN_THREAD_ID && replyInfo?.type !== 'message') { if (chat.isBotForum && user?.canManageBotForumTopics && threadId === MAIN_THREAD_ID
&& replyInfo?.type !== 'message'
) {
const baseTitle = params.text ?? getTranslationFn()('BotForumTopicTitlePlaceholder'); const baseTitle = params.text ?? getTranslationFn()('BotForumTopicTitlePlaceholder');
const title = baseTitle.length > 12 ? `${baseTitle.slice(0, 12)}...` : baseTitle; const title = baseTitle.length > 12 ? `${baseTitle.slice(0, 12)}...` : baseTitle;
const topic = await callApi('createTopic', { const topic = await callApi('createTopic', {

View File

@ -27,7 +27,7 @@ const SAFE_MIN_PROPERTIES: (keyof ApiTopic)[] = [
export function updateTopicsInfo<T extends GlobalState>( export function updateTopicsInfo<T extends GlobalState>(
global: T, chatId: string, update: Partial<TopicsInfo>, global: T, chatId: string, update: Partial<TopicsInfo>,
) { ) {
const info = global.chats.topicsInfoById[chatId] || {}; const info = global.chats.topicsInfoById[chatId] || { topicsById: {} };
global = { global = {
...global, ...global,

View File

@ -11,7 +11,7 @@ import {
DEBUG, STRICTERDOM_ENABLED, DEBUG, STRICTERDOM_ENABLED,
} from './config'; } from './config';
import { enableStrict, requestMutation } from './lib/fasterdom/fasterdom'; import { enableStrict, requestMutation } from './lib/fasterdom/fasterdom';
import { selectChat, selectChatFullInfo, selectCurrentMessageList, selectTabState } from './global/selectors'; import { selectChat, selectCurrentMessageList, selectPeerFullInfo, selectTabState } from './global/selectors';
import { selectSharedSettings } from './global/selectors/sharedState'; import { selectSharedSettings } from './global/selectors/sharedState';
import { betterView } from './util/betterView'; import { betterView } from './util/betterView';
import { IS_TAURI } from './util/browser/globalEnvironment'; import { IS_TAURI } from './util/browser/globalEnvironment';
@ -115,7 +115,7 @@ async function init() {
console.warn( console.warn(
'CURRENT MESSAGE LIST', 'CURRENT MESSAGE LIST',
selectChat(currentGlobal, currentMessageList.chatId), selectChat(currentGlobal, currentMessageList.chatId),
selectChatFullInfo(currentGlobal, currentMessageList.chatId), selectPeerFullInfo(currentGlobal, currentMessageList.chatId),
currentGlobal.messages.byChatId[currentMessageList.chatId], currentGlobal.messages.byChatId[currentMessageList.chatId],
); );
} }

View File

@ -1170,6 +1170,7 @@ export interface LangPair {
'ComposerPlaceholderBroadcast': undefined; 'ComposerPlaceholderBroadcast': undefined;
'ComposerPlaceholderBroadcastSilent': undefined; 'ComposerPlaceholderBroadcastSilent': undefined;
'ComposerPlaceholderTopicGeneral': undefined; 'ComposerPlaceholderTopicGeneral': undefined;
'ComposerPlaceholderBotTopicGeneral': undefined;
'ComposerStoryPlaceholderLocked': undefined; 'ComposerStoryPlaceholderLocked': undefined;
'ComposerPlaceholderNoText': undefined; 'ComposerPlaceholderNoText': undefined;
'AriaComposerCancelVoice': undefined; 'AriaComposerCancelVoice': undefined;