diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 985a57bb8b..179518600c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -459,6 +459,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_contacts_not_found" = "No contacts found"; "lng_topics_not_found" = "No topics found."; "lng_forum_all_messages" = "All Messages"; +"lng_forum_create_new_topic" = "Create New Thread"; "lng_dlg_search_for_messages" = "Search for messages"; "lng_update_telegram" = "Update Telegram"; "lng_dlg_search_in" = "Search messages in"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 228e726e25..e0453428aa 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3529,7 +3529,6 @@ void ApiWrap::forwardMessages( if (shared) { ++shared->requestsLeft; } - const auto requestType = Data::Histories::RequestType::Send; const auto idsCopy = localIds; const auto scheduled = action.options.scheduled; const auto starsPaid = std::min( @@ -3540,57 +3539,80 @@ void ApiWrap::forwardMessages( action.options.starsApproved -= starsPaid; oneFlags |= SendFlag::f_allow_paid_stars; } - histories.sendRequest(history, requestType, [=](Fn finish) { - history->sendRequestId = request(MTPmessages_ForwardMessages( - MTP_flags(oneFlags), + auto buildMessage = [=]( + not_null history, + FullReplyTo replyTo) + -> Data::Histories::PreparedMessage { + const auto kGeneralId = Data::ForumTopic::kGeneralId; + const auto realTopMsgId = (replyTo.topicRootId == kGeneralId) + ? MsgId(0) + : replyTo.topicRootId; + auto flags = oneFlags; + if (realTopMsgId) { + flags |= SendFlag::f_top_msg_id; + } else { + flags &= ~SendFlag::f_top_msg_id; + } + return MTPmessages_ForwardMessages( + MTP_flags(flags), forwardFrom->input(), MTP_vector(ids), MTP_vector(randomIds), - peer->input(), - MTP_int(topMsgId), + history->peer->input(), + MTP_int(realTopMsgId), (action.options.suggest - ? ReplyToForMTP(history, action.replyTo) + ? ReplyToForMTP(history, replyTo) : monoforumPeer - ? MTP_inputReplyToMonoForum(monoforumPeer->input()) + ? MTP_inputReplyToMonoForum( + monoforumPeer->input()) : MTPInputReplyTo()), MTP_int(action.options.scheduled), MTP_int(action.options.scheduleRepeatPeriod), - (sendAs ? sendAs->input() : MTP_inputPeerEmpty()), - Data::ShortcutIdToMTP(_session, action.options.shortcutId), + (sendAs + ? sendAs->input() + : MTP_inputPeerEmpty()), + Data::ShortcutIdToMTP( + &history->session(), + action.options.shortcutId), MTP_long(action.options.effectId), - MTPint(), // video_timestamp + MTPint(), MTP_long(starsPaid), - Api::SuggestToMTP(action.options.suggest) - )).done([=](const MTPUpdates &result) { + Api::SuggestToMTP(action.options.suggest)); + }; + histories.sendPreparedMessage( + history, + FullReplyTo{ .topicRootId = topicRootId }, + uint64(0), + std::move(buildMessage), + [=](const MTPUpdates &result, const MTP::Response &) { if (!scheduled) { - this->updates().checkForSentToScheduled(result); + _session->api().updates().checkForSentToScheduled( + result); } - applyUpdates(result); if (shared && !--shared->requestsLeft) { shared->callback(); } - finish(); - if (peer->isSelf() && session().premium()) { + if (peer->isSelf() && _session->premium()) { ProcessRecentSelfForwards( _session, result, peer->id, forwardFrom->id); } - }).fail([=](const MTP::Error &error) { + }, + [=](const MTP::Error &error, const MTP::Response &) { if (idsCopy) { for (const auto &[randomId, itemId] : *idsCopy) { - sendMessageFail(error, peer, randomId, itemId); + _session->api().sendMessageFail( + error, + peer, + randomId, + itemId); } } else { - sendMessageFail(error, peer); + _session->api().sendMessageFail(error, peer); } - finish(); - }).afterRequest( - history->sendRequestId - ).send(); - return history->sendRequestId; - }); + }); ids.resize(0); randomIds.resize(0); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 6979595d4e..a83af75867 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -1096,10 +1096,16 @@ auto ChooseTopicBoxController::Row::generateNameWords() const return _topic->chatListNameWords(); } -ChooseTopicBoxController::AllMessagesRow::AllMessagesRow() -: PeerListRow(PeerListRowId(0)) { - const auto name = tr::lng_forum_all_messages(tr::now); - const auto words = TextUtilities::PrepareSearchWords(name); +QString ChooseTopicBoxController::AllMessagesRow::name() const { + return _userCreatesTopics + ? tr::lng_forum_create_new_topic(tr::now) + : tr::lng_forum_all_messages(tr::now); +} + +ChooseTopicBoxController::AllMessagesRow::AllMessagesRow(bool userCreatesTopics) +: PeerListRow(PeerListRowId(0)) +, _userCreatesTopics(userCreatesTopics) { + const auto words = TextUtilities::PrepareSearchWords(name()); for (const auto &word : words) { _nameWords.emplace(word); _nameFirstLetters.emplace(word[0]); @@ -1107,23 +1113,26 @@ ChooseTopicBoxController::AllMessagesRow::AllMessagesRow() } QString ChooseTopicBoxController::AllMessagesRow::generateName() { - return tr::lng_forum_all_messages(tr::now); + return name(); } QString ChooseTopicBoxController::AllMessagesRow::generateShortName() { - return tr::lng_forum_all_messages(tr::now); + return name(); } auto ChooseTopicBoxController::AllMessagesRow::generatePaintUserpicCallback( bool forceRound) -> PaintRoundImageCallback { - return []( + return [userCreatesTopics = _userCreatesTopics]( Painter &p, int x, int y, int outerWidth, int size) { - st::menuIconChats.paintInCenter( + const auto &icon = userCreatesTopics + ? st::menuIconDiscussion + : st::menuIconChats; + icon.paintInCenter( p, QRect(x, y - st::lineWidth, size, size)); }; @@ -1206,8 +1215,10 @@ void ChooseTopicBoxController::refreshRows(bool initial) { if (_forum->bot() && !delegate()->peerListFindRow(PeerListRowId(0)) && (!_filter || _filter(_forum->history()))) { + const auto userCreatesTopics = Data::IsBotUserCreatesTopics( + _forum->peer()); delegate()->peerListAppendRow( - std::make_unique()); + std::make_unique(userCreatesTopics)); added = true; } for (const auto &row : _forum->topicsList()->indexed()->all()) { diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index 73defcdb4c..6eb90a0009 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -393,7 +393,7 @@ private: class AllMessagesRow final : public PeerListRow { public: - AllMessagesRow(); + explicit AllMessagesRow(bool userCreatesTopics); QString generateName() override; QString generateShortName() override; @@ -406,8 +406,11 @@ private: -> const base::flat_set & override; private: + [[nodiscard]] QString name() const; + base::flat_set _nameFirstLetters; base::flat_set _nameWords; + bool _userCreatesTopics = false; }; diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 9f4deabc9d..417320ba81 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -1683,6 +1683,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback( std::optional videoTimestamp) { struct State final { base::flat_set requests; + mtpRequestId nextRequestKey = 0; }; const auto state = std::make_shared(); return [=]( @@ -1732,13 +1733,6 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback( for (const auto &fullId : existingIds) { mtpMsgIds.push_back(MTP_int(fullId.msg)); } - const auto generateRandom = [&] { - auto result = QVector(existingIds.size()); - for (auto &value : result) { - value = base::RandomValue(); - } - return result; - }; auto &api = history->session().api(); auto &histories = history->owner().histories(); const auto donePhraseArgs = CreateForwardedMessagePhraseArgs( @@ -1747,103 +1741,170 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback( const auto showRecentForwardsToSelf = result.size() == 1 && result.front()->peer()->isSelf() && history->session().premium(); - const auto requestType = Data::Histories::RequestType::Send; for (const auto thread : result) { + const auto peer = thread->peer(); + const auto threadHistory = thread->owningHistory(); + const auto forum = threadHistory->asForum(); + const auto needNewTopic = forum + && forum->bot() + && Data::IsBotUserCreatesTopics(peer) + && !thread->asTopic(); + const auto effectiveThread = [&]() -> not_null { + if (needNewTopic) { + const auto topic = forum->reserveNewBotTopic(); + Assert(topic != nullptr); + return topic; + } + return thread; + }(); + if (!comment.text.isEmpty()) { auto message = Api::MessageToSend( - Api::SendAction(thread, options)); + Api::SendAction(effectiveThread, options)); message.textWithTags = comment; message.action.clearDraft = false; api.sendMessage(std::move(message)); } - const auto topicRootId = thread->topicRootId(); - const auto sublistPeer = thread->maybeSublistPeer(); - const auto kGeneralId = Data::ForumTopic::kGeneralId; - const auto topMsgId = (topicRootId == kGeneralId) - ? MsgId(0) - : topicRootId; - const auto peer = thread->peer(); - const auto threadHistory = thread->owningHistory(); + + const auto topicRootId = effectiveThread->topicRootId(); + const auto sublistPeer = needNewTopic + ? nullptr + : thread->maybeSublistPeer(); + const auto fromPeer = history->peer; + const auto msgCount = int(existingIds.size()); const auto starsPaid = std::min( peer->starsPerMessageChecked(), options.starsApproved); if (starsPaid) { options.starsApproved -= starsPaid; } - histories.sendRequest(threadHistory, requestType, [=]( - Fn finish) { - const auto session = &threadHistory->session(); - auto &api = session->api(); - const auto sendFlags = commonSendFlags - | (topMsgId ? Flag::f_top_msg_id : Flag(0)) - | (ShouldSendSilent(peer, options) - ? Flag::f_silent - : Flag(0)) - | (options.shortcutId - ? Flag::f_quick_reply_shortcut - : Flag(0)) - | (starsPaid ? Flag::f_allow_paid_stars : Flag()) - | (sublistPeer ? Flag::f_reply_to : Flag()) - | (options.suggest ? Flag::f_suggested_post : Flag()) - | (options.effectId ? Flag::f_effect : Flag()); - threadHistory->sendRequestId = api.request( - MTPmessages_ForwardMessages( - MTP_flags(sendFlags), - history->peer->input(), - MTP_vector(mtpMsgIds), - MTP_vector(generateRandom()), - peer->input(), - MTP_int(topMsgId), - (sublistPeer - ? MTP_inputReplyToMonoForum(sublistPeer->input()) - : MTPInputReplyTo()), - MTP_int(options.scheduled), - MTP_int(options.scheduleRepeatPeriod), - MTP_inputPeerEmpty(), // send_as - Data::ShortcutIdToMTP(session, options.shortcutId), - MTP_long(options.effectId), - MTP_int(videoTimestamp.value_or(0)), - MTP_long(starsPaid), - Api::SuggestToMTP(options.suggest) - )).done([=](const MTPUpdates &updates, mtpRequestId reqId) { - threadHistory->session().api().applyUpdates(updates); - if (showRecentForwardsToSelf) { - ApiWrap::ProcessRecentSelfForwards( - &threadHistory->session(), - updates, - peer->id, - history->peer->id); - } - state->requests.remove(reqId); - if (state->requests.empty()) { - if (show->valid()) { - auto phrase = rpl::variable( - ChatHelpers::ForwardedMessagePhrase( - donePhraseArgs)).current(); - if (!phrase.empty()) { - show->showToast(std::move(phrase)); - } - show->hideLayer(); + const auto sendFlags = commonSendFlags + | (ShouldSendSilent(peer, options) + ? Flag::f_silent + : Flag(0)) + | (options.shortcutId + ? Flag::f_quick_reply_shortcut + : Flag(0)) + | (starsPaid ? Flag::f_allow_paid_stars : Flag()) + | (sublistPeer ? Flag::f_reply_to : Flag()) + | (options.suggest ? Flag::f_suggested_post : Flag()) + | (options.effectId ? Flag::f_effect : Flag()); + auto buildMessage = [=]( + not_null history, + FullReplyTo replyTo) + -> Data::Histories::PreparedMessage { + const auto kGeneralId + = Data::ForumTopic::kGeneralId; + const auto realTopMsgId + = (replyTo.topicRootId == kGeneralId) + ? MsgId(0) + : replyTo.topicRootId; + auto flags = sendFlags; + if (realTopMsgId) { + flags |= Flag::f_top_msg_id; + } else { + flags &= ~Flag::f_top_msg_id; + } + auto randoms = QVector(msgCount); + for (auto &value : randoms) { + value = base::RandomValue(); + } + return MTPmessages_ForwardMessages( + MTP_flags(flags), + fromPeer->input(), + MTP_vector(mtpMsgIds), + MTP_vector(randoms), + history->peer->input(), + MTP_int(realTopMsgId), + (sublistPeer + ? MTP_inputReplyToMonoForum( + sublistPeer->input()) + : MTPInputReplyTo()), + MTP_int(options.scheduled), + MTP_int(options.scheduleRepeatPeriod), + MTP_inputPeerEmpty(), + Data::ShortcutIdToMTP( + &history->session(), + options.shortcutId), + MTP_long(options.effectId), + MTP_int(videoTimestamp.value_or(0)), + MTP_long(starsPaid), + Api::SuggestToMTP(options.suggest)); + }; + const auto requestDone = [=]( + const MTPUpdates &updates, + mtpRequestId requestKey) { + if (showRecentForwardsToSelf) { + ApiWrap::ProcessRecentSelfForwards( + &threadHistory->session(), + updates, + peer->id, + history->peer->id); + } + state->requests.remove(requestKey); + if (state->requests.empty()) { + if (show->valid()) { + auto phrase = rpl::variable< + TextWithEntities>( + ChatHelpers::ForwardedMessagePhrase( + donePhraseArgs)).current(); + if (!phrase.empty()) { + show->showToast(std::move(phrase)); } + show->hideLayer(); } - finish(); - }).fail([=](const MTP::Error &error) { - const auto type = error.type(); - if (type.startsWith(u"ALLOW_PAYMENT_REQUIRED_"_q)) { - show->showToast(u"Payment requirements changed. " - "Please, try again."_q); - } else if (type == u"VOICE_MESSAGES_FORBIDDEN"_q) { - show->showToast( - tr::lng_restricted_send_voice_messages( - tr::now, - lt_user, - peer->name())); + } + }; + const auto requestFail = [=]( + const MTP::Error &error, + mtpRequestId requestKey) { + const auto type = error.type(); + if (type.startsWith( + u"ALLOW_PAYMENT_REQUIRED_"_q)) { + show->showToast( + u"Payment requirements changed. " + "Please, try again."_q); + } else if (type + == u"VOICE_MESSAGES_FORBIDDEN"_q) { + show->showToast( + tr::lng_restricted_send_voice_messages( + tr::now, + lt_user, + peer->name())); + } + state->requests.remove(requestKey); + if (state->requests.empty()) { + if (show->valid()) { + show->hideLayer(); } - finish(); - }).afterRequest(threadHistory->sendRequestId).send(); - return threadHistory->sendRequestId; - }); - state->requests.insert(threadHistory->sendRequestId); + } + }; + const auto requestKey = ++state->nextRequestKey; + state->requests.insert(requestKey); + histories.sendPreparedMessage( + threadHistory, + FullReplyTo{ .topicRootId = topicRootId }, + uint64(0), + std::move(buildMessage), + [=](const MTPUpdates &updates, + const MTP::Response &) { + requestDone(updates, requestKey); + }, + [=](const MTP::Error &error, + const MTP::Response &) { + requestFail(error, requestKey); + }); + } + if (state->requests.empty()) { + if (show->valid()) { + auto phrase = rpl::variable( + ChatHelpers::ForwardedMessagePhrase( + donePhraseArgs)).current(); + if (!phrase.empty()) { + show->showToast(std::move(phrase)); + } + show->hideLayer(); + } } }; } diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index 2ce2861839..0c059f9490 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -527,6 +527,15 @@ MsgId Forum::reserveCreatingId( return result; } +ForumTopic *Forum::reserveNewBotTopic() { + const auto &colors = ForumTopicColorIds(); + const auto colorId = colors[base::RandomIndex(colors.size())]; + return topicFor(reserveCreatingId( + tr::lng_bot_new_chat(tr::now), + colorId, + DocumentId())); +} + void Forum::discardCreatingId(MsgId rootId) { Expects(creating(rootId)); diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h index 0b38ca6232..96f4fcb49f 100644 --- a/Telegram/SourceFiles/data/data_forum.h +++ b/Telegram/SourceFiles/data/data_forum.h @@ -87,6 +87,7 @@ public: void discardCreatingId(MsgId rootId); [[nodiscard]] bool creating(MsgId rootId) const; void created(MsgId rootId, MsgId realId); + [[nodiscard]] ForumTopic *reserveNewBotTopic(); void clearAllUnreadMentions(); void clearAllUnreadReactions(); diff --git a/Telegram/SourceFiles/data/data_histories.h b/Telegram/SourceFiles/data/data_histories.h index fd6ee38108..773dec16c1 100644 --- a/Telegram/SourceFiles/data/data_histories.h +++ b/Telegram/SourceFiles/data/data_histories.h @@ -112,7 +112,8 @@ public: MTPmessages_SendMessage, MTPmessages_SendMedia, MTPmessages_SendInlineBotResult, - MTPmessages_SendMultiMedia>; + MTPmessages_SendMultiMedia, + MTPmessages_ForwardMessages>; int sendPreparedMessage( not_null history, FullReplyTo replyTo, diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index d2a12f5695..bd5c49c0f7 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -2231,9 +2231,9 @@ std::optional ColorIndexFromColor(const MTPPeerColor *color) { }); } -bool IsBotCanManageTopics(not_null peer) { +bool IsBotUserCreatesTopics(not_null peer) { if (const auto user = peer->asUser()) { - return user->botInfo && user->botInfo->canManageTopics; + return user->botInfo && user->botInfo->userCreatesTopics; } return false; } diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 5f73007840..c907e1778b 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -678,6 +678,6 @@ void SetTopPinnedMessageId( [[nodiscard]] uint64 BackgroundEmojiIdFromColor(const MTPPeerColor *color); [[nodiscard]] std::optional ColorIndexFromColor(const MTPPeerColor *); -[[nodiscard]] bool IsBotCanManageTopics(not_null); +[[nodiscard]] bool IsBotUserCreatesTopics(not_null); } // namespace Data diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index b34e7770da..a4723c828a 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -564,6 +564,28 @@ not_null Session::processUser(const MTPUser &data) { result->setStarsPerMessage(0); } + if (!minimal) { + result->setBotInfoVersion(data.vbot_info_version().value_or(-1)); + if (const auto info = result->botInfo.get()) { + info->readsAllHistory = data.is_bot_chat_history(); + if (info->cantJoinGroups != data.is_bot_nochats()) { + info->cantJoinGroups = data.is_bot_nochats(); + flags |= UpdateFlag::BotCanBeInvited; + } + if (const auto value = data.vbot_inline_placeholder()) { + info->inlinePlaceholder = '_' + qs(*value); + } else { + info->inlinePlaceholder = QString(); + } + info->supportsAttachMenu = data.is_bot_attach_menu(); + info->supportsBusiness = data.is_bot_business(); + info->canEditInformation = data.is_bot_can_edit(); + info->activeUsers = data.vbot_active_users().value_or_empty(); + info->hasMainApp = data.is_bot_has_main_app(); + info->userCreatesTopics = data.is_bot_forum_can_manage_topics(); + } + } + using Flag = UserDataFlag; const auto flagsMask = Flag::Deleted | Flag::Verified @@ -739,28 +761,6 @@ not_null Session::processUser(const MTPUser &data) { result->setEmojiStatus(EmojiStatusId()); } if (!minimal) { - if (const auto botInfoVersion = data.vbot_info_version()) { - result->setBotInfoVersion(botInfoVersion->v); - result->botInfo->readsAllHistory = data.is_bot_chat_history(); - if (result->botInfo->cantJoinGroups != data.is_bot_nochats()) { - result->botInfo->cantJoinGroups = data.is_bot_nochats(); - flags |= UpdateFlag::BotCanBeInvited; - } - if (const auto placeholder = data.vbot_inline_placeholder()) { - result->botInfo->inlinePlaceholder = '_' + qs(*placeholder); - } else { - result->botInfo->inlinePlaceholder = QString(); - } - result->botInfo->supportsAttachMenu = data.is_bot_attach_menu(); - result->botInfo->supportsBusiness = data.is_bot_business(); - result->botInfo->canEditInformation = data.is_bot_can_edit(); - result->botInfo->activeUsers = data.vbot_active_users().value_or_empty(); - result->botInfo->hasMainApp = data.is_bot_has_main_app(); - result->botInfo->canManageTopics - = data.is_bot_forum_can_manage_topics(); - } else { - result->setBotInfoVersion(-1); - } result->setIsContact(data.is_contact() || data.is_mutual_contact()); } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 4a8d21bcfd..e969efc4b6 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -98,7 +98,7 @@ struct BotInfo { bool canManageEmojiStatus : 1 = false; bool supportsBusiness : 1 = false; bool hasMainApp : 1 = false; - bool canManageTopics : 1 = false; + bool userCreatesTopics : 1 = false; private: std::unique_ptr _forum; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 1a2b4368ac..b977975360 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4719,12 +4719,12 @@ void HistoryWidget::hideSelectorControlsAnimated() { } Api::SendAction HistoryWidget::prepareSendAction( - Api::SendOptions options) const { + Api::SendOptions options) { auto result = Api::SendAction(_history, options); result.replyTo = replyTo(); if (const auto forum = _history->asForum()) { - if (Data::IsBotCanManageTopics(_history->peer)) { + if (forum->bot() && Data::IsBotUserCreatesTopics(_history->peer)) { const auto readyRootId = [&]() -> MsgId { if (const auto id = result.replyTo.messageId) { if (const auto item = session().data().message(id)) { @@ -4737,14 +4737,15 @@ Api::SendAction HistoryWidget::prepareSendAction( result.replyTo.topicRootId = readyRootId; } else { if (!_creatingBotTopic) { - const auto &colors = Data::ForumTopicColorIds(); - const auto colorId - = colors[base::RandomIndex(colors.size())]; - _creatingBotTopic = forum->topicFor( - forum->reserveCreatingId( - tr::lng_bot_new_chat(tr::now), - colorId, - DocumentId())); + _creatingBotTopic = forum->reserveNewBotTopic(); + auto draft = _history->forwardDraft(MsgId(0), PeerId()); + if (!draft.ids.empty()) { + _history->setForwardDraft(MsgId(0), PeerId(), {}); + _history->setForwardDraft( + _creatingBotTopic->rootId(), + PeerId(), + std::move(draft)); + } } result = Api::SendAction(_creatingBotTopic, options); result.replyTo.topicRootId = _creatingBotTopic->rootId(); @@ -6302,7 +6303,7 @@ void HistoryWidget::updateFieldPlaceholder() { } } else if (const auto user = peer->asUser()) { if (const auto &info = user->botInfo) { - if (info->forum() && !info->canManageTopics) { + if (info->forum() && !info->userCreatesTopics) { return tr::lng_bot_off_thread_ph(); } } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 214de7054d..e2a187c124 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -422,7 +422,7 @@ private: void messageDataReceived(not_null peer, MsgId msgId); [[nodiscard]] Api::SendAction prepareSendAction( - Api::SendOptions options) const; + Api::SendOptions options); void sendVoice(const VoiceToSend &data); void send(Api::SendOptions options); void sendWithModifiers(Qt::KeyboardModifiers modifiers); diff --git a/Telegram/SourceFiles/history/view/history_view_about_view.cpp b/Telegram/SourceFiles/history/view/history_view_about_view.cpp index 692abd8b15..0424dd26de 100644 --- a/Telegram/SourceFiles/history/view/history_view_about_view.cpp +++ b/Telegram/SourceFiles/history/view/history_view_about_view.cpp @@ -636,7 +636,7 @@ bool AboutView::aboveHistory() const { return true; } const auto info = _history->peer->asUser()->botInfo.get(); - return !(info->canManageTopics + return !(info->userCreatesTopics && info->startToken.isEmpty() && (!_history->isEmpty() || _history->lastMessage())); } @@ -696,7 +696,7 @@ bool AboutView::refresh() { _version = 0; return false; } else if (_history->peer->isForum() - && info->canManageTopics + && info->userCreatesTopics && info->startToken.isEmpty() && (!_history->isEmpty() || _history->lastMessage())) { if (_item) { diff --git a/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp b/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp index 195c7aa205..5c4049307a 100644 --- a/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp +++ b/Telegram/SourceFiles/history/view/history_view_subsection_tabs.cpp @@ -453,7 +453,7 @@ void SubsectionTabs::startFillingSlider( ).append(' ').append(peer->shortName()), }); } - // } else if (Data::IsBotCanManageTopics(item.thread->peer())) { + // } else if (Data::IsBotUserCreatesTopics(item.thread->peer())) { // sections.push_back({ // .text = { tr::lng_bot_new_chat(tr::now) }, // }); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index c8151595b6..974414a8a2 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -654,28 +654,31 @@ bool MainWidget::sendPaths( bool MainWidget::filesOrForwardDrop( not_null thread, - not_null data) { - if (const auto forum = thread->asForum()) { - Window::ShowDropMediaBox( - _controller, - Core::ShareMimeMediaData(data), - forum); - if (_hider) { - _hider->startHide(); - clearHider(_hider); + not_null data, + bool forumResolved) { + if (!forumResolved) { + if (const auto forum = thread->asForum()) { + Window::ShowDropMediaBox( + _controller, + Core::ShareMimeMediaData(data), + forum); + if (_hider) { + _hider->startHide(); + clearHider(_hider); + } + return true; + } else if (const auto history = thread->asHistory() + ; history && history->peer->monoforum()) { + Window::ShowDropMediaBox( + _controller, + Core::ShareMimeMediaData(data), + history->peer->monoforum()); + if (_hider) { + _hider->startHide(); + clearHider(_hider); + } + return true; } - return true; - } else if (const auto history = thread->asHistory() - ; history && history->peer->monoforum()) { - Window::ShowDropMediaBox( - _controller, - Core::ShareMimeMediaData(data), - history->peer->monoforum()); - if (_hider) { - _hider->startHide(); - clearHider(_hider); - } - return true; } if (data->hasFormat(u"application/x-td-forward"_q)) { auto draft = Data::ForwardDraft{ diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 198d2fffee..526c60b709 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -161,7 +161,8 @@ public: const QString &text) const; bool filesOrForwardDrop( not_null thread, - not_null data); + not_null data, + bool forumResolved = false); void sendBotCommand(Bot::SendCommandRequest request); void hideSingleUseKeyboard(FullMsgId replyToId); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 61cde97e4e..6c3dfdec1b 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -3278,7 +3278,7 @@ base::weak_qptr ShowDropMediaBox( navigation ](not_null thread) mutable { const auto content = navigation->parentController()->content(); - if (!content->filesOrForwardDrop(thread, data.get())) { + if (!content->filesOrForwardDrop(thread, data.get(), true)) { return; } else if (const auto strong = *weak) { strong->closeBox(); @@ -3318,7 +3318,7 @@ base::weak_qptr ShowDropMediaBox( navigation ](not_null sublist) mutable { const auto content = navigation->parentController()->content(); - if (!content->filesOrForwardDrop(sublist, data.get())) { + if (!content->filesOrForwardDrop(sublist, data.get(), true)) { return; } else if (const auto strong = *weak) { strong->closeBox();