diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index e0e7281441..2d1ccfd8cc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1886,6 +1886,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_linked_channel_posted" = "All new posts from this channel are forwarded to the group."; "lng_manage_discussion_group_warning" = "\"Chat history for new members\" will be switched to **Visible**."; +"lng_manage_monoforum" = "Direct Messages"; +"lng_manage_monoforum_off" = "Off"; +"lng_manage_monoforum_free" = "Free"; +"lng_manage_monoforum_allow" = "Allow Direct Messages"; +"lng_manage_monoforum_about" = "Allow users to write direct private messages to your channel, with the option to charge a fee for every message."; +"lng_manage_monoforum_price_about" = "Charge users for the ability to write a direct message to your channel. Your channel will receive {percent} of the selected fee ({amount}) for each incoming message."; + "lng_manage_history_visibility_title" = "Chat history for new members"; "lng_manage_history_visibility_shown" = "Visible"; "lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined."; @@ -2243,6 +2250,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_message_price_free" = "Messages are now free in this group."; "lng_action_message_price_paid#one" = "Messages now cost {count} Star each in this group."; "lng_action_message_price_paid#other" = "Messages now cost {count} Stars each in this group."; +"lng_action_direct_messages_enabled" = "Channel enabled Direct Messages."; +"lng_action_direct_messages_paid#one" = "Channel allows Direct Messages for {count} Star each."; +"lng_action_direct_messages_paid#other" = "Channel allows Direct Messages for {count} Stars each"; +"lng_action_direct_messages_disabled" = "Channel disabled Direct Messages."; "lng_you_paid_stars#one" = "You paid {count} Star."; "lng_you_paid_stars#other" = "You paid {count} Stars."; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 749c3722f9..0c3b2a521c 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_folder.h" #include "data/data_forum_topic.h" #include "data/data_forum.h" +#include "data/data_saved_messages.h" #include "data/data_saved_sublist.h" #include "data/data_search_controller.h" #include "data/data_session.h" @@ -381,6 +382,9 @@ void ApiWrap::savePinnedOrder(not_null forum) { } void ApiWrap::savePinnedOrder(not_null saved) { + if (saved->parentChat()) { + return; + } const auto &order = _session->data().pinnedChatsOrder(saved); const auto input = [](Dialogs::Key key) { if (const auto sublist = key.sublist()) { diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index c3c92325a0..1f83b004cb 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -1168,12 +1168,13 @@ rpl::producer SetupChargeSlider( struct State { rpl::variable stars; }; - const auto group = !peer->isUser(); + const auto broadcast = peer->isBroadcast(); + const auto group = !broadcast && !peer->isUser(); const auto state = container->lifetime().make_state(); const auto chargeStars = savedValue ? savedValue : kDefaultChargeStars; state->stars = chargeStars; - Ui::AddSubsectionTitle(container, group + Ui::AddSubsectionTitle(container, (group || broadcast) ? tr::lng_rights_charge_price() : tr::lng_messages_privacy_price()); @@ -1225,7 +1226,9 @@ rpl::producer SetupChargeSlider( const auto percent = peer->session().appConfig().paidMessageCommission(); Ui::AddDividerText( container, - (group + (broadcast + ? tr::lng_manage_monoforum_price_about + : group ? tr::lng_rights_charge_price_about : tr::lng_messages_privacy_price_about)( lt_percent, @@ -1235,3 +1238,54 @@ rpl::producer SetupChargeSlider( return state->stars.value(); } + +void EditDirectMessagesPriceBox( + not_null box, + not_null channel, + std::optional savedValue, + Fn)> callback) { + box->setTitle(tr::lng_manage_monoforum()); + + const auto toggle = box->addRow(object_ptr( + box, + tr::lng_manage_monoforum_allow(), + st::settingsButtonNoIcon + ), {})->toggleOn(rpl::single(savedValue.has_value())); + Ui::AddSkip(box->verticalLayout()); + + Ui::AddDividerText( + box->verticalLayout(), + tr::lng_manage_monoforum_about()); + + const auto wrap = box->addRow( + object_ptr>( + box, + object_ptr(box)), + {}); + wrap->toggle(savedValue.has_value(), anim::type::instant); + wrap->toggleOn(toggle->toggledChanges()); + + const auto result = box->lifetime().make_state( + savedValue.value_or(0)); + + const auto inner = wrap->entity(); + Ui::AddSkip(inner); + SetupChargeSlider( + inner, + channel, + savedValue.value_or(0) + ) | rpl::start_with_next([=](int stars) { + *result = stars; + }, box->lifetime()); + + box->addButton(tr::lng_settings_save(), [=] { + const auto weak = Ui::MakeWeak(box); + callback(toggle->toggled() ? *result : std::optional()); + if (const auto strong = weak.data()) { + strong->closeBox(); + } + }); + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); +} diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h index 256ebe5b51..d194572dc3 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.h +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h @@ -174,3 +174,9 @@ void EditMessagesPrivacyBox( not_null container, not_null peer, int savedValue); + +void EditDirectMessagesPriceBox( + not_null box, + not_null channel, + std::optional savedValue, + Fn)> callback); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 18c9341375..4bc44e3bd6 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/replace_boost_box.h" #include "boxes/peers/verify_peers_box.h" #include "boxes/peer_list_controllers.h" +#include "boxes/edit_privacy_box.h" // EditDirectMessagesPriceBox #include "boxes/stickers_box.h" #include "boxes/username_box.h" #include "chat_helpers/emoji_suggestions_widget.h" @@ -220,28 +221,41 @@ void SaveSlowmodeSeconds( } void SaveStarsPerMessage( + std::shared_ptr show, not_null channel, int starsPerMessage, - Fn done) { + Fn done) { const auto api = &channel->session().api(); const auto key = Api::RequestKey("stars_per_message", channel->id); + const auto broadcast = channel->isBroadcast(); + + using Flag = MTPchannels_UpdatePaidMessagesPrice::Flag; + const auto broadcastAllowed = broadcast && (starsPerMessage >= 0); const auto requestId = api->request(MTPchannels_UpdatePaidMessagesPrice( - MTP_flags(0), // #TODO Support broadcast_messages_allowed flag in UI + MTP_flags(broadcastAllowed + ? Flag::f_broadcast_messages_allowed + : Flag(0)), channel->inputChannel, MTP_long(starsPerMessage) )).done([=](const MTPUpdates &result) { api->clearModifyRequest(key); api->applyUpdates(result); - channel->setStarsPerMessage(starsPerMessage); - done(); + if (!broadcast) { + channel->setStarsPerMessage(starsPerMessage); + } + done(true); }).fail([=](const MTP::Error &error) { api->clearModifyRequest(key); if (error.type() != u"CHAT_NOT_MODIFIED"_q) { - return; + show->showToast(error.type()); + done(false); + } else { + if (!broadcast) { + channel->setStarsPerMessage(starsPerMessage); + } + done(true); } - channel->setStarsPerMessage(starsPerMessage); - done(); }).send(); api->registerModifyRequest(key, requestId); @@ -281,6 +295,7 @@ void SaveBoostsUnrestrict( void ShowEditPermissions( not_null navigation, not_null peer) { + const auto show = navigation->uiShow(); auto createBox = [=](not_null box) { const auto saving = box->lifetime().make_state(0); const auto save = [=]( @@ -299,7 +314,10 @@ void ShowEditPermissions( channel, result.boostsUnrestrict, close); - SaveStarsPerMessage(channel, result.starsPerMessage, close); + const auto price = result.starsPerMessage; + SaveStarsPerMessage(show, channel, price, [=](bool ok) { + close(); + }); } }; auto done = [=](EditPeerPermissionsBoxResult result) { @@ -366,6 +384,7 @@ private: std::optional joinToWrite; std::optional requestToJoin; std::optional discussionLink; + std::optional starsPerDirectMessage; }; [[nodiscard]] object_ptr createPhotoAndTitleEdit(); @@ -382,8 +401,10 @@ private: void showEditPeerTypeBox( std::optional> error = {}); void showEditDiscussionLinkBox(); + void showEditDirectMessagesBox(); void fillPrivacyTypeButton(); void fillDiscussionLinkButton(); + void fillDirectMessagesButton(); //void fillInviteLinkButton(); void fillForumButton(); void fillColorIndexButton(); @@ -412,6 +433,7 @@ private: [[nodiscard]] bool validateUsernamesOrder(Saving &to) const; [[nodiscard]] bool validateUsername(Saving &to) const; [[nodiscard]] bool validateDiscussionLink(Saving &to) const; + [[nodiscard]] bool validateDirectMessagesPrice(Saving &to) const; [[nodiscard]] bool validateTitle(Saving &to) const; [[nodiscard]] bool validateDescription(Saving &to) const; [[nodiscard]] bool validateHistoryVisibility(Saving &to) const; @@ -426,6 +448,7 @@ private: void saveUsernamesOrder(); void saveUsername(); void saveDiscussionLink(); + void saveDirectMessagesPrice(); void saveTitle(); void saveDescription(); void saveHistoryVisibility(); @@ -454,6 +477,7 @@ private: std::optional _discussionLinkSavedValue; ChannelData *_discussionLinkOriginalValue = nullptr; bool _channelHasLocationOriginalValue = false; + std::optional> _starsPerDirectMessageSavedValue; std::optional _historyVisibilitySavedValue; std::optional _typeDataSavedValue; std::optional _forumSavedValue; @@ -918,6 +942,20 @@ void Controller::showEditDiscussionLinkBox() { }).send(); } +void Controller::showEditDirectMessagesBox() { + Expects(_peer->isBroadcast()); + Expects(_starsPerDirectMessageSavedValue.has_value()); + + const auto stars = _starsPerDirectMessageSavedValue->current(); + _navigation->parentController()->show(Box( + EditDirectMessagesPriceBox, + _peer->asChannel(), + (stars >= 0) ? stars : std::optional(), + [=](std::optional value) { + *_starsPerDirectMessageSavedValue = value.value_or(-1); + })); +} + void Controller::fillPrivacyTypeButton() { Expects(_controls.buttonsLayout != nullptr); @@ -983,9 +1021,11 @@ void Controller::fillPrivacyTypeButton() { void Controller::fillDiscussionLinkButton() { Expects(_controls.buttonsLayout != nullptr); - _discussionLinkSavedValue = _discussionLinkOriginalValue = _peer->isChannel() - ? _peer->asChannel()->discussionLink() - : nullptr; + _discussionLinkSavedValue + = _discussionLinkOriginalValue + = (_peer->isChannel() + ? _peer->asChannel()->discussionLink() + : nullptr); const auto isGroup = (_peer->isChat() || _peer->isMegagroup()); auto text = !isGroup @@ -1019,6 +1059,33 @@ void Controller::fillDiscussionLinkButton() { { isGroup ? &st::menuIconChannel : &st::menuIconGroups }); _discussionLinkUpdates.fire_copy(*_discussionLinkSavedValue); } + +void Controller::fillDirectMessagesButton() { + Expects(_controls.buttonsLayout != nullptr); + + if (!_peer->isBroadcast() || !_peer->asChannel()->canEditInformation()) { + return; + } + + const auto monoforumLink = _peer->asChannel()->monoforumLink(); + _starsPerDirectMessageSavedValue = rpl::variable( + monoforumLink ? monoforumLink->starsPerMessage() : -1); + + auto label = _starsPerDirectMessageSavedValue->value( + ) | rpl::map([](int starsPerMessage) { + return (starsPerMessage < 0) + ? tr::lng_manage_monoforum_off() + : !starsPerMessage + ? tr::lng_manage_monoforum_free() + : rpl::single(Lang::FormatCountDecimal(starsPerMessage)); + }) | rpl::flatten_latest(); + AddButtonWithText( + _controls.buttonsLayout, + tr::lng_manage_monoforum(), + std::move(label), + [=] { showEditDirectMessagesBox(); }, + { &st::menuIconChatBubble }); +} // //void Controller::fillInviteLinkButton() { // Expects(_controls.buttonsLayout != nullptr); @@ -1359,6 +1426,8 @@ void Controller::fillManageSection() { const auto canViewOrEditDiscussionLink = isChannel && (channel->discussionLink() || (channel->isBroadcast() && channel->canEditInformation())); + const auto canEditDirectMessages = isChannel + && (channel->isBroadcast() && channel->canEditInformation()); ::AddSkip(_controls.buttonsLayout, 0); @@ -1370,6 +1439,9 @@ void Controller::fillManageSection() { if (canViewOrEditDiscussionLink) { fillDiscussionLinkButton(); } + if (canEditDirectMessages) { + fillDirectMessagesButton(); + } if (canEditPreHistoryHidden) { fillHistoryVisibilityButton(); } @@ -1973,6 +2045,7 @@ std::optional Controller::validate() const { if (validateUsernamesOrder(result) && validateUsername(result) && validateDiscussionLink(result) + && validateDirectMessagesPrice(result) && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) @@ -2022,6 +2095,14 @@ bool Controller::validateDiscussionLink(Saving &to) const { return true; } +bool Controller::validateDirectMessagesPrice(Saving &to) const { + if (!_starsPerDirectMessageSavedValue) { + return true; + } + to.starsPerDirectMessage = _starsPerDirectMessageSavedValue->current(); + return true; +} + bool Controller::validateTitle(Saving &to) const { if (!_controls.title) { return true; @@ -2120,6 +2201,7 @@ void Controller::save() { pushSaveStage([=] { saveUsernamesOrder(); }); pushSaveStage([=] { saveUsername(); }); pushSaveStage([=] { saveDiscussionLink(); }); + pushSaveStage([=] { saveDirectMessagesPrice(); }); pushSaveStage([=] { saveTitle(); }); pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); @@ -2277,6 +2359,30 @@ void Controller::saveDiscussionLink() { }).send(); } +void Controller::saveDirectMessagesPrice() { + const auto channel = _peer->asChannel(); + if (!channel) { + return continueSave(); + } + const auto monoforumLink = channel->monoforumLink(); + const auto current = monoforumLink ? monoforumLink->starsPerMessage() : -1; + const auto desired = _savingData.starsPerDirectMessage + ? *_savingData.starsPerDirectMessage + : current; + if (desired == current) { + return continueSave(); + } + const auto show = _navigation->uiShow(); + const auto done = [=](bool ok) { + if (ok) { + continueSave(); + } else { + cancelSave(); + } + }; + SaveStarsPerMessage(show, channel, desired, crl::guard(this, done)); +} + void Controller::saveTitle() { if (!_savingData.title || *_savingData.title == _peer->name()) { return continueSave(); diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 325e92c443..da4120f0f7 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -907,6 +907,7 @@ void PinsLimitBox( limits.dialogsPinnedPremium(), PinsCount(session->data().chatsList())); } + void SublistsPinsLimitBox( not_null box, not_null session) { diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 06ac557554..b0a98edbfb 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -871,6 +871,10 @@ historyGiftToChannel: IconButton(defaultIconButton) { rippleAreaSize: 40px; ripple: universalRippleAnimation; } +historyDirectMessage: IconButton(historyGiftToChannel) { + icon: icon{{ "menu/chat_bubble", windowActiveTextFg }}; + iconOver: icon{{ "menu/chat_bubble", windowActiveTextFg }}; +} historyUnblock: FlatButton(historyComposeButton) { color: attentionButtonFg; overColor: attentionButtonFgOver; diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 2eb84caedf..22cfec1e66 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -112,12 +112,13 @@ struct PeerUpdate { StickersSet = (1ULL << 46), EmojiSet = (1ULL << 47), DiscussionLink = (1ULL << 48), - ChannelLocation = (1ULL << 49), - Slowmode = (1ULL << 50), - GroupCall = (1ULL << 51), + MonoforumLink = (1ULL << 49), + ChannelLocation = (1ULL << 50), + Slowmode = (1ULL << 51), + GroupCall = (1ULL << 52), // For iteration - LastUsedBit = (1ULL << 51), + LastUsedBit = (1ULL << 52), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 8761065915..817e59fb97 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "data/data_group_call.h" #include "data/data_message_reactions.h" +#include "data/data_saved_messages.h" #include "data/data_wall_paper.h" #include "data/notify/data_notify_settings.h" #include "main/main_session.h" @@ -89,6 +90,29 @@ std::unique_ptr MegagroupInfo::takeForumData() { return nullptr; } +void MegagroupInfo::ensureMonoforum(not_null that) { + if (!_monoforum) { + const auto history = that->owner().history(that); + _monoforum = std::make_unique( + &that->owner(), + that); + history->monoforumChanged(nullptr); + } +} + +Data::SavedMessages *MegagroupInfo::monoforum() const { + return _monoforum.get(); +} + +std::unique_ptr MegagroupInfo::takeMonoforumData() { + if (auto result = base::take(_monoforum)) { + const auto history = result->owner().history(result->parentChat()); + history->monoforumChanged(result.get()); + return result; + } + return nullptr; +} + ChannelData::ChannelData(not_null owner, PeerId id) : PeerData(owner, id) , inputChannel( @@ -161,6 +185,12 @@ void ChannelData::setAccessHash(uint64 accessHash) { } void ChannelData::setFlags(ChannelDataFlags which) { + if (which & (Flag::Forum | Flag::Monoforum)) { + which |= Flag::Megagroup; + } + if (which & Flag::Monoforum) { + which &= ~Flag::Forum; + } const auto diff = flags() ^ which; if ((which & Flag::Megagroup) && !mgInfo) { mgInfo = std::make_unique(); @@ -276,8 +306,9 @@ const ChannelLocation *ChannelData::getLocation() const { } void ChannelData::setDiscussionLink(ChannelData *linked) { - if (_discussionLink != linked) { + if (_discussionLink != linked || !_discussionLinkKnown) { _discussionLink = linked; + _discussionLinkKnown = true; if (const auto history = owner().historyLoaded(this)) { history->forceFullResize(); } @@ -286,11 +317,22 @@ void ChannelData::setDiscussionLink(ChannelData *linked) { } ChannelData *ChannelData::discussionLink() const { - return _discussionLink.value_or(nullptr); + return _discussionLink; } bool ChannelData::discussionLinkKnown() const { - return _discussionLink.has_value(); + return _discussionLinkKnown; +} + +void ChannelData::setMonoforumLink(ChannelData *link) { + if (_monoforumLink != link) { + _monoforumLink = link; + session().changes().peerUpdated(this, UpdateFlag::MonoforumLink); + } +} + +ChannelData *ChannelData::monoforumLink() const { + return _monoforumLink; } void ChannelData::setMembersCount(int newMembersCount) { @@ -1240,6 +1282,11 @@ void ApplyChannelUpdate( } else { channel->setDiscussionLink(nullptr); } + if (const auto chat = update.vlinked_monoforum_id()) { + channel->setMonoforumLink(channel->owner().channelLoaded(chat->v)); + } else { + channel->setMonoforumLink(nullptr); + } if (const auto history = channel->owner().historyLoaded(channel)) { if (const auto available = update.vavailable_min_id()) { history->clearUpTill(available->v); diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index b4f99b5243..6dc802dcfc 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -16,6 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class ChannelData; +namespace Data { +class Forum; +class SavedMessages; +} // namespace Data + struct ChannelLocation { QString address; Data::LocationPoint point; @@ -74,6 +79,7 @@ enum class ChannelDataFlag : uint64 { StargiftsAvailable = (1ULL << 36), PaidMessagesAvailable = (1ULL << 37), AutoTranslation = (1ULL << 38), + Monoforum = (1ULL << 39), }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; using ChannelDataFlags = base::flags; @@ -118,6 +124,10 @@ public: [[nodiscard]] Data::Forum *forum() const; [[nodiscard]] std::unique_ptr takeForumData(); + void ensureMonoforum(not_null that); + [[nodiscard]] Data::SavedMessages *monoforum() const; + [[nodiscard]] std::unique_ptr takeMonoforumData(); + std::deque> lastParticipants; base::flat_map, Admin> lastAdmins; base::flat_map, Restricted> lastRestricted; @@ -154,6 +164,7 @@ private: ChannelLocation _location; Data::ChatBotCommands _botCommands; std::unique_ptr _forum; + std::unique_ptr _monoforum; int _starsPerMessage = 0; friend class ChannelData; @@ -301,6 +312,9 @@ public: [[nodiscard]] bool isForum() const { return flags() & Flag::Forum; } + [[nodiscard]] bool isMonoforum() const { + return flags() & Flag::Monoforum; + } [[nodiscard]] bool hasUsername() const { return flags() & Flag::Username; } @@ -413,6 +427,9 @@ public: [[nodiscard]] ChannelData *discussionLink() const; [[nodiscard]] bool discussionLinkKnown() const; + void setMonoforumLink(ChannelData *link); + [[nodiscard]] ChannelData *monoforumLink() const; + void ptsInit(int32 pts) { _ptsWaiter.init(pts); } @@ -510,6 +527,9 @@ public: [[nodiscard]] Data::Forum *forum() const { return mgInfo ? mgInfo->forum() : nullptr; } + [[nodiscard]] Data::SavedMessages *monoforum() const { + return mgInfo ? mgInfo->monoforum() : nullptr; + } void processTopics(const MTPVector &topics); @@ -546,18 +566,11 @@ private: std::vector &&reasons) override; Flags _flags = ChannelDataFlags(Flag::Forbidden); - int _peerGiftsCount = 0; PtsWaiter _ptsWaiter; Data::UsernamesInfo _username; - int _membersCount = -1; - int _adminsCount = 1; - int _restrictedCount = 0; - int _kickedCount = 0; - int _pendingRequestsCount = 0; - int _levelHint = 0; std::vector _recentRequesters; MsgId _availableMinId = 0; @@ -570,7 +583,18 @@ private: std::vector _unavailableReasons; std::unique_ptr _invitePeek; QString _inviteLink; - std::optional _discussionLink; + + ChannelData *_discussionLink = nullptr; + ChannelData *_monoforumLink = nullptr; + bool _discussionLinkKnown = false; + + int _peerGiftsCount = 0; + int _membersCount = -1; + int _adminsCount = 1; + int _restrictedCount = 0; + int _kickedCount = 0; + int _pendingRequestsCount = 0; + int _levelHint = 0; Data::AllowedReactions _allowedReactions; diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp index 81b3330972..2a85f44d13 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.cpp +++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp @@ -159,7 +159,8 @@ bool CanSendAnyOf( using Flag = ChannelDataFlag; const auto allowed = channel->amIn() || ((channel->flags() & Flag::HasLink) - && !(channel->flags() & Flag::JoinToWrite)); + && !(channel->flags() & Flag::JoinToWrite)) + || channel->isMonoforum(); if (!allowed || (forbidInForums && channel->isForum())) { return false; } else if (channel->canPostMessages()) { diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index c2c614577c..6a480a075a 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -1333,6 +1333,13 @@ bool PeerData::isForum() const { return false; } +bool PeerData::isMonoforum() const { + if (const auto channel = asChannel()) { + return channel->isMonoforum(); + } + return false; +} + bool PeerData::isGigagroup() const { if (const auto channel = asChannel()) { return channel->isGigagroup(); @@ -1416,6 +1423,13 @@ Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const { return nullptr; } +Data::SavedMessages *PeerData::monoforum() const { + if (const auto channel = asChannel()) { + return channel->monoforum(); + } + return nullptr; +} + bool PeerData::allowsForwarding() const { if (isUser()) { return true; diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index b0563c42d5..a4a1ebe531 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -37,6 +37,7 @@ class Forum; class ForumTopic; class Session; class GroupCall; +class SavedMessages; struct ReactionId; class WallPaper; @@ -232,6 +233,7 @@ public: [[nodiscard]] bool isMegagroup() const; [[nodiscard]] bool isBroadcast() const; [[nodiscard]] bool isForum() const; + [[nodiscard]] bool isMonoforum() const; [[nodiscard]] bool isGigagroup() const; [[nodiscard]] bool isRepliesChat() const; [[nodiscard]] bool isVerifyCodes() const; @@ -257,6 +259,8 @@ public: [[nodiscard]] Data::Forum *forum() const; [[nodiscard]] Data::ForumTopic *forumTopicFor(MsgId rootId) const; + [[nodiscard]] Data::SavedMessages *monoforum() const; + [[nodiscard]] Data::PeerNotifySettings ¬ify() { return _notify; } diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 287dd2e42b..0c435d5347 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -269,6 +269,7 @@ inline auto DefaultRestrictionValue( | Flag::Left | Flag::Forum | Flag::JoinToWrite + | Flag::Monoforum | Flag::HasLink | Flag::Forbidden | Flag::Creator @@ -292,7 +293,8 @@ inline auto DefaultRestrictionValue( && (flags & Flag::Forum); const auto allowed = !(flags & notAmInFlags) || ((flags & Flag::HasLink) - && !(flags & Flag::JoinToWrite)); + && !(flags & Flag::JoinToWrite)) + || (flags & Flag::Monoforum); const auto restricted = sendRestriction | (defaultSendRestriction && !unrestrictedByBoosts); return allowed diff --git a/Telegram/SourceFiles/data/data_saved_messages.cpp b/Telegram/SourceFiles/data/data_saved_messages.cpp index e587c83549..6a401afd17 100644 --- a/Telegram/SourceFiles/data/data_saved_messages.cpp +++ b/Telegram/SourceFiles/data/data_saved_messages.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_saved_messages.h" #include "apiwrap.h" +#include "data/data_channel.h" #include "data/data_peer.h" #include "data/data_saved_sublist.h" #include "data/data_session.h" @@ -25,12 +26,15 @@ constexpr auto kListFirstPerPage = 20; } // namespace -SavedMessages::SavedMessages(not_null owner) +SavedMessages::SavedMessages( + not_null owner, + ChannelData *parentChat) : _owner(owner) +, _parentChat(parentChat) , _chatsList( - &owner->session(), + &_owner->session(), FilterId(), - owner->maxPinnedChatsLimitValue(this)) + _owner->maxPinnedChatsLimitValue(this)) , _loadMore([=] { sendLoadMoreRequests(); }) { } @@ -40,6 +44,10 @@ bool SavedMessages::supported() const { return !_unsupported; } +ChannelData *SavedMessages::parentChat() const { + return _parentChat; +} + Session &SavedMessages::owner() const { return *_owner; } @@ -59,7 +67,11 @@ not_null SavedMessages::sublist(not_null peer) { } return _sublists.emplace( peer, - std::make_unique(peer)).first->second.get(); + std::make_unique(this, peer)).first->second.get(); +} + +rpl::producer<> SavedMessages::chatsListChanges() const { + return _chatsListChanges.events(); } void SavedMessages::loadMore() { @@ -78,10 +90,12 @@ void SavedMessages::sendLoadMore() { } else if (!_pinnedLoaded) { loadPinned(); } + using Flag = MTPmessages_GetSavedDialogs::Flag; _loadMoreRequestId = _owner->session().api().request( MTPmessages_GetSavedDialogs( - MTP_flags(MTPmessages_GetSavedDialogs::Flag::f_exclude_pinned), - MTPInputPeer(), // parent_peer + MTP_flags(Flag::f_exclude_pinned + | (_parentChat ? Flag::f_parent_peer : Flag(0))), + _parentChat ? _parentChat->input : MTPInputPeer(), MTP_int(_offsetDate), MTP_int(_offsetId), _offsetPeer ? _offsetPeer->input : MTP_inputPeerEmpty(), @@ -89,6 +103,7 @@ void SavedMessages::sendLoadMore() { MTP_long(0)) // hash ).done([=](const MTPmessages_SavedDialogs &result) { apply(result, false); + _chatsListChanges.fire({}); }).fail([=](const MTP::Error &error) { if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) { _unsupported = true; @@ -99,13 +114,14 @@ void SavedMessages::sendLoadMore() { } void SavedMessages::loadPinned() { - if (_pinnedRequestId) { + if (_pinnedRequestId || parentChat()) { return; } _pinnedRequestId = _owner->session().api().request( MTPmessages_GetPinnedSavedDialogs() ).done([=](const MTPmessages_SavedDialogs &result) { apply(result, true); + _chatsListChanges.fire({}); }).fail([=](const MTP::Error &error) { if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) { _unsupported = true; @@ -124,10 +140,11 @@ void SavedMessages::sendLoadMore(not_null sublist) { const auto offsetId = list.empty() ? MsgId(0) : list.back()->id; const auto offsetDate = list.empty() ? MsgId(0) : list.back()->date(); const auto limit = offsetId ? kPerPage : kFirstPerPage; + using Flag = MTPmessages_GetSavedHistory::Flag; const auto requestId = _owner->session().api().request( MTPmessages_GetSavedHistory( - MTP_flags(0), - MTPInputPeer(), // parent_peer + MTP_flags(_parentChat ? Flag::f_parent_peer : Flag(0)), + _parentChat ? _parentChat->input : MTPInputPeer(), sublist->peer()->input, MTP_int(offsetId), MTP_int(offsetDate), @@ -261,6 +278,8 @@ void SavedMessages::sendLoadMoreRequests() { } void SavedMessages::apply(const MTPDupdatePinnedSavedDialogs &update) { + Expects(!parentChat()); + const auto list = update.vorder(); if (!list) { loadPinned(); @@ -286,6 +305,8 @@ void SavedMessages::apply(const MTPDupdatePinnedSavedDialogs &update) { } void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) { + Expects(!parentChat()); + update.vpeer().match([&](const MTPDdialogPeer &data) { const auto peer = _owner->peer(peerFromMTP(data.vpeer())); const auto i = _sublists.find(peer); @@ -300,4 +321,8 @@ void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) { }); } +rpl::lifetime &SavedMessages::lifetime() { + return _lifetime; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_saved_messages.h b/Telegram/SourceFiles/data/data_saved_messages.h index 3e09f4db0a..3ef9aae9c0 100644 --- a/Telegram/SourceFiles/data/data_saved_messages.h +++ b/Telegram/SourceFiles/data/data_saved_messages.h @@ -20,10 +20,13 @@ class SavedSublist; class SavedMessages final { public: - explicit SavedMessages(not_null owner); + explicit SavedMessages( + not_null owner, + ChannelData *parentChat = nullptr); ~SavedMessages(); [[nodiscard]] bool supported() const; + [[nodiscard]] ChannelData *parentChat() const; [[nodiscard]] Session &owner() const; [[nodiscard]] Main::Session &session() const; @@ -31,12 +34,16 @@ public: [[nodiscard]] not_null chatsList(); [[nodiscard]] not_null sublist(not_null peer); + [[nodiscard]] rpl::producer<> chatsListChanges() const; + void loadMore(); void loadMore(not_null sublist); void apply(const MTPDupdatePinnedSavedDialogs &update); void apply(const MTPDupdateSavedDialogPinned &update); + [[nodiscard]] rpl::lifetime &lifetime(); + private: void loadPinned(); void apply(const MTPmessages_SavedDialogs &result, bool pinned); @@ -46,6 +53,7 @@ private: void sendLoadMoreRequests(); const not_null _owner; + ChannelData *_parentChat = nullptr; Dialogs::MainList _chatsList; base::flat_map< @@ -64,9 +72,13 @@ private: base::flat_set> _loadMoreSublistsScheduled; bool _loadMoreScheduled = false; + rpl::event_stream<> _chatsListChanges; + bool _pinnedLoaded = false; bool _unsupported = false; + rpl::lifetime _lifetime; + }; } // namespace Data diff --git a/Telegram/SourceFiles/data/data_saved_sublist.cpp b/Telegram/SourceFiles/data/data_saved_sublist.cpp index 2d129a5f67..134ada5295 100644 --- a/Telegram/SourceFiles/data/data_saved_sublist.cpp +++ b/Telegram/SourceFiles/data/data_saved_sublist.cpp @@ -17,13 +17,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { -SavedSublist::SavedSublist(not_null peer) +SavedSublist::SavedSublist( + not_null parent, + not_null peer) : Entry(&peer->owner(), Dialogs::Entry::Type::SavedSublist) +, _parent(parent) , _history(peer->owner().history(peer)) { } SavedSublist::~SavedSublist() = default; +not_null SavedSublist::parent() const { + return _parent; +} + +ChannelData *SavedSublist::parentChat() const { + return _parent->parentChat(); +} + not_null SavedSublist::history() const { return _history; } @@ -101,9 +112,7 @@ void SavedSublist::removeOne(not_null item) { updateChatListExistence(); } else { updateChatListEntry(); - crl::on_main(this, [=] { - owner().savedMessages().loadMore(this); - }); + crl::on_main(this, [=] { _parent->loadMore(this); }); } } else { setChatListTimeId(_items.front()->date()); diff --git a/Telegram/SourceFiles/data/data_saved_sublist.h b/Telegram/SourceFiles/data/data_saved_sublist.h index 15c3428fff..8e59854e45 100644 --- a/Telegram/SourceFiles/data/data_saved_sublist.h +++ b/Telegram/SourceFiles/data/data_saved_sublist.h @@ -16,12 +16,15 @@ class History; namespace Data { class Session; +class SavedMessages; class SavedSublist final : public Dialogs::Entry { public: - explicit SavedSublist(not_null peer); + SavedSublist(not_null parent,not_null peer); ~SavedSublist(); + [[nodiscard]] not_null parent() const; + [[nodiscard]] ChannelData *parentChat() const; [[nodiscard]] not_null history() const; [[nodiscard]] not_null peer() const; [[nodiscard]] bool isHiddenAuthor() const; @@ -72,6 +75,7 @@ private: void allowChatListMessageResolve(); void resolveChatListMessageGroup(); + const not_null _parent; const not_null _history; std::vector> _items; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 8a19ce6483..71368f588a 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -967,7 +967,8 @@ not_null Session::processChat(const MTPChat &data) { | ((!minimal && !data.is_stories_hidden_min()) ? Flag::StoriesHidden : Flag()) - | Flag::AutoTranslation; + | Flag::AutoTranslation + | Flag::Monoforum; const auto storiesState = minimal ? std::optional() : data.is_stories_unavailable() @@ -1007,7 +1008,8 @@ not_null Session::processChat(const MTPChat &data) { && data.is_stories_hidden()) ? Flag::StoriesHidden : Flag()) - | (data.is_autotranslation() ? Flag::AutoTranslation : Flag()); + | (data.is_autotranslation() ? Flag::AutoTranslation : Flag()) + | (data.is_monoforum() ? Flag::Monoforum : Flag()); channel->setFlags((channel->flags() & ~flagsMask) | flagsSet); channel->setBotVerifyDetailsIcon( data.vbot_verification_icon().value_or_empty()); @@ -2310,6 +2312,9 @@ void Session::applyDialog( bool Session::pinnedCanPin(not_null entry) const { if ([[maybe_unused]] const auto sublist = entry->asSublist()) { + if (sublist->parentChat()) { + return false; + } const auto saved = &savedMessages(); return pinnedChatsOrder(saved).size() < pinnedChatsLimit(saved); } else if (const auto topic = entry->asTopic()) { @@ -2351,6 +2356,9 @@ int Session::pinnedChatsLimit(not_null forum) const { } int Session::pinnedChatsLimit(not_null saved) const { + if (saved->parentChat()) { + return 0; + } const auto limits = Data::PremiumLimits(_session); return limits.savedSublistsPinnedCurrent(); } @@ -2391,6 +2399,9 @@ rpl::producer Session::maxPinnedChatsLimitValue( rpl::producer Session::maxPinnedChatsLimitValue( not_null saved) const { + if (saved->parentChat()) { + return rpl::single(0); + } // Premium limit from appconfig. // We always use premium limit in the MainList limit producer, // because it slices the list to that limit. We don't want to slice @@ -4563,12 +4574,12 @@ not_null Session::processFolder(const MTPDfolder &data) { not_null Session::chatsListFor( not_null entry) { - const auto topic = entry->asTopic(); - return topic - ? topic->forum()->topicsList() - : entry->asSublist() - ? _savedMessages->chatsList() - : chatsList(entry->folder()); + if (const auto topic = entry->asTopic()) { + return topic->forum()->topicsList(); + } else if (const auto sublist = entry->asSublist()) { + return sublist->parent()->chatsList(); + } + return chatsList(entry->folder()); } not_null Session::chatsList(Data::Folder *folder) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 4c2775aeeb..4b21f5c6e9 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -781,11 +781,14 @@ void InnerWidget::changeOpenedForum(Data::Forum *forum) { } } -void InnerWidget::showSavedSublists() { +void InnerWidget::showSavedSublists(ChannelData *parentChat) { + Expects(!parentChat || parentChat->monoforum()); Expects(!_geometryInited); Expects(!_savedSublists); - _savedSublists = true; + _savedSublists = parentChat + ? parentChat->monoforum() + : &session().data().savedMessages(); stopReorderPinned(); clearSelection(); @@ -2115,7 +2118,7 @@ bool InnerWidget::addQuickActionRipple( const std::vector &InnerWidget::pinnedChatsOrder() const { const auto owner = &session().data(); return _savedSublists - ? owner->pinnedChatsOrder(&owner->savedMessages()) + ? owner->pinnedChatsOrder(_savedSublists) : _openedForum ? owner->pinnedChatsOrder(_openedForum) : _filterId @@ -2179,6 +2182,9 @@ int InnerWidget::countPinnedIndex(Row *ofRow) { } void InnerWidget::savePinnedOrder() { + if (_savedSublists && _savedSublists->parentChat()) { + return; + } const auto &newOrder = pinnedChatsOrder(); if (newOrder.size() != _pinnedOnDragStart.size()) { return; // Something has changed in the set of pinned chats. @@ -2316,8 +2322,11 @@ bool InnerWidget::updateReorderPinned(QPoint localPosition) { const auto delta = [&] { if (localPosition.y() < _visibleTop) { return localPosition.y() - _visibleTop; - } else if ((_savedSublists || _openedFolder || _openedForum || _filterId) - && localPosition.y() > _visibleBottom) { + } else if ((localPosition.y() > _visibleBottom) + && (_savedSublists + || _openedFolder + || _openedForum + || _filterId)) { return localPosition.y() - _visibleBottom; } return 0; @@ -2685,8 +2694,8 @@ void InnerWidget::handleChatListEntryRefreshes() { return false; } else if (const auto topic = event.key.topic()) { return (topic->forum() == _openedForum); - } else if (event.key.sublist()) { - return _savedSublists; + } else if (const auto sublist = event.key.sublist()) { + return sublist->parent() == _savedSublists; } else { return !_openedForum; } @@ -2704,7 +2713,7 @@ void InnerWidget::handleChatListEntryRefreshes() { && (key.topic() ? (key.topic()->forum() == _openedForum) : key.sublist() - ? _savedSublists + ? (key.sublist()->parent() == _savedSublists) : (entry->folder() == _openedFolder))) { _dialogMoved.fire({ from, to }); } @@ -2909,7 +2918,8 @@ void InnerWidget::enterEventHook(QEnterEvent *e) { Row *InnerWidget::shownRowByKey(Key key) { const auto entry = key.entry(); if (_savedSublists) { - if (!entry->asSublist()) { + const auto sublist = entry->asSublist(); + if (!sublist || sublist->parent() != _savedSublists) { return nullptr; } } else if (_openedForum) { @@ -2978,7 +2988,7 @@ void InnerWidget::updateSelectedRow(Key key) { void InnerWidget::refreshShownList() { const auto list = _savedSublists - ? session().data().savedMessages().chatsList()->indexed() + ? _savedSublists->chatsList()->indexed() : _openedForum ? _openedForum->topicsList()->indexed() : _filterId @@ -3440,8 +3450,7 @@ void InnerWidget::applySearchState(SearchState state) { }; if (_searchState.filterChatsList() && !words.isEmpty()) { if (_savedSublists) { - const auto owner = &session().data(); - append(owner->savedMessages().chatsList()->indexed()); + append(_savedSublists->chatsList()->indexed()); } else if (_openedForum) { append(_openedForum->topicsList()->indexed()); } else { @@ -4012,7 +4021,7 @@ void InnerWidget::refreshEmpty() { const auto state = !_shownList->empty() ? EmptyState::None : _savedSublists - ? (data->savedMessages().chatsList()->loaded() + ? (_savedSublists->chatsList()->loaded() ? EmptyState::EmptySavedSublists : EmptyState::Loading) : _openedForum diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index 9842faf67a..bc6b54e832 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -58,6 +58,7 @@ class ChatFilter; class Thread; class Folder; class Forum; +class SavedMessages; struct ReactionId; } // namespace Data @@ -140,7 +141,7 @@ public: void changeOpenedFolder(Data::Folder *folder); void changeOpenedForum(Data::Forum *forum); - void showSavedSublists(); + void showSavedSublists(ChannelData *parentChat); void selectSkip(int32 direction); void selectSkipPage(int32 pixels, int32 direction); @@ -668,7 +669,8 @@ private: float64 _narrowRatio = 0.; bool _geometryInited = false; - bool _savedSublists = false; + Data::SavedMessages *_savedSublists = nullptr; + bool _searchLoading = false; bool _searchWaiting = false; diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index e3bf2f2100..d9b66c85f8 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -1712,6 +1712,7 @@ ServiceAction ParseServiceAction( }, [&](const MTPDmessageActionPaidMessagesPrice &data) { result.content = ActionPaidMessagesPrice{ .stars = int(data.vstars().v), + .broadcastAllowed = data.is_broadcast_messages_allowed(), }; }, [&](const MTPDmessageActionConferenceCall &data) { auto content = ActionPhoneCall(); diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index ca9a95916d..5a3e2c3ec2 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -673,6 +673,7 @@ struct ActionPaidMessagesRefunded { struct ActionPaidMessagesPrice { int stars = 0; + bool broadcastAllowed = false; }; struct ServiceAction { diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 3d960a38e2..ed73f81b0f 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -1383,7 +1383,15 @@ auto HtmlWriter::Wrap::pushMessage( + " messages to you"); return result; }, [&](const ActionPaidMessagesPrice &data) { - auto result = "Price per messages changed to " + if (isChannel) { + auto result = !data.broadcastAllowed + ? "Direct messages were disabled." + : ("Price per direct message changed to " + + QString::number(data.stars).toUtf8() + + " Telegram Stars."); + return result; + } + auto result = "Price per message changed to " + QString::number(data.stars).toUtf8() + " Telegram Stars."; return result; diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index 772bc6f3f2..7af9bc0b97 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -679,6 +679,7 @@ QByteArray SerializeMessage( pushActor(); pushAction("paid_messages_price_change"); push("price_stars", data.stars); + push("is_broadcast_messages_allowed", data.broadcastAllowed); }, [](v::null_t) {}); if (v::is_null(message.action.content)) { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index cd8578a860..98a7b830d8 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_drafts.h" +#include "data/data_saved_messages.h" #include "data/data_saved_sublist.h" #include "data/data_session.h" #include "data/data_media_types.h" @@ -3131,6 +3132,42 @@ bool History::isForum() const { return (_flags & Flag::IsForum); } +void History::monoforumChanged(Data::SavedMessages *old) { + if (inChatList()) { + notifyUnreadStateChange(old + ? AdjustedForumUnreadState(old->chatsList()->unreadState()) + : computeUnreadState()); + } + + if (const auto monoforum = peer->monoforum()) { + _flags |= Flag::IsMonoforum; + + monoforum->chatsList()->unreadStateChanges( + ) | rpl::filter([=] { + return (_flags & Flag::IsMonoforum) && inChatList(); + }) | rpl::map( + AdjustedForumUnreadState + ) | rpl::start_with_next([=](const Dialogs::UnreadState &old) { + notifyUnreadStateChange(old); + }, monoforum->lifetime()); + + monoforum->chatsListChanges( + ) | rpl::start_with_next([=] { + updateChatListEntry(); + }, monoforum->lifetime()); + } else { + _flags &= ~Flag::IsMonoforum; + } + if (cloudDraft(MsgId(0))) { + updateChatListSortPosition(); + } + _flags |= Flag::PendingAllItemsResize; +} + +bool History::isMonoforum() const { + return (_flags & Flag::IsMonoforum); +} + not_null History::migrateToOrMe() const { if (const auto to = peer->migrateTo()) { return owner().history(to); diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 57b1203d02..8963ab0a08 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -27,12 +27,14 @@ struct LanguageId; namespace Data { struct Draft; +class Forum; class Session; class Folder; class ChatFilter; struct SponsoredFrom; class SponsoredMessages; class HistoryMessages; +class SavedMessages; } // namespace Data namespace Dialogs { @@ -71,6 +73,9 @@ public: void forumChanged(Data::Forum *old); [[nodiscard]] bool isForum() const; + void monoforumChanged(Data::SavedMessages *old); + [[nodiscard]] bool isMonoforum() const; + [[nodiscard]] not_null migrateToOrMe() const; [[nodiscard]] History *migrateFrom() const; [[nodiscard]] MsgRange rangeForDifferenceRequest() const; @@ -430,9 +435,10 @@ private: PendingAllItemsResize = (1 << 1), IsTopPromoted = (1 << 2), IsForum = (1 << 3), - FakeUnreadWhileOpened = (1 << 4), - HasPinnedMessages = (1 << 5), - ResolveChatListMessage = (1 << 6), + IsMonoforum = (1 << 4), + FakeUnreadWhileOpened = (1 << 5), + HasPinnedMessages = (1 << 6), + ResolveChatListMessage = (1 << 7), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0b7571674c..275edd80f8 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -3563,6 +3563,12 @@ Data::SavedSublist *HistoryItem::savedSublist() const { that->AddComponents(HistoryMessageSaved::Bit()); that->Get()->sublist = sublist; return sublist; + } else if (const auto monoforum = _history->peer->monoforum()) { + const auto sublist = monoforum->sublist(_history->peer); + const auto that = const_cast(this); + that->AddComponents(HistoryMessageSaved::Bit()); + that->Get()->sublist = sublist; + return sublist; } return nullptr; } @@ -3785,7 +3791,9 @@ void HistoryItem::createComponents(CreateConfig &&config) { } } const auto peer = _history->owner().peer(config.savedSublistPeer); - saved->sublist = _history->owner().savedMessages().sublist(peer); + saved->sublist = _history->peer->isSelf() + ? _history->owner().savedMessages().sublist(peer) + : _history->peer->monoforum()->sublist(peer); } if (const auto reply = Get()) { @@ -5744,8 +5752,23 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { auto preparePaidMessagesPrice = [&](const MTPDmessageActionPaidMessagesPrice &action) { const auto stars = action.vstars().v; + const auto broadcastAllowed = action.is_broadcast_messages_allowed(); auto result = PreparedServiceText(); - result.text = stars + result.text = _history->peer->isBroadcast() + ? (stars > 0 + ? tr::lng_action_direct_messages_paid( + tr::now, + lt_count, + stars, + Ui::Text::WithEntities) + : broadcastAllowed + ? tr::lng_action_direct_messages_enabled( + tr::now, + Ui::Text::WithEntities) + : tr::lng_action_direct_messages_disabled( + tr::now, + Ui::Text::WithEntities)) + : stars ? tr::lng_action_message_price_paid( tr::now, lt_count, diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index f32fd69ffe..9e829886f7 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -383,6 +383,7 @@ HistoryWidget::HistoryWidget( _joinChannel->addClickHandler([=] { joinChannel(); }); _muteUnmute->addClickHandler([=] { toggleMuteUnmute(); }); setupGiftToChannelButton(); + setupDirectMessageButton(); _reportMessages->addClickHandler([=] { reportSelectedMessages(); }); _field->submits( ) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) { @@ -1050,15 +1051,23 @@ void HistoryWidget::refreshJoinChannelText() { } void HistoryWidget::refreshGiftToChannelShown() { - if (!_giftToChannelIn || !_giftToChannelOut) { + if (!_giftToChannel || !_peer) { return; } const auto channel = _peer->asChannel(); - const auto shown = channel + _giftToChannel->setVisible(channel && channel->isBroadcast() - && channel->stargiftsAvailable(); - _giftToChannelIn->setVisible(shown); - _giftToChannelOut->setVisible(shown); + && channel->stargiftsAvailable()); +} + +void HistoryWidget::refreshDirectMessageShown() { + if (!_directMessage || !_peer) { + return; + } + const auto channel = _peer->asChannel(); + _directMessage->setVisible(channel + && channel->isBroadcast() + && channel->monoforumLink()); } void HistoryWidget::refreshTopBarActiveChat() { @@ -2074,22 +2083,63 @@ void HistoryWidget::setupShortcuts() { } void HistoryWidget::setupGiftToChannelButton() { - const auto setupButton = [=](not_null parent) { - auto *button = Ui::CreateChild( - parent.get(), - st::historyGiftToChannel); - parent->widthValue() | rpl::start_with_next([=](int width) { - button->moveToRight(0, 0); - }, button->lifetime()); - button->setClickedCallback([=] { - if (_peer) { - Ui::ShowStarGiftBox(controller(), _peer); + _giftToChannel = Ui::CreateChild( + _muteUnmute.data(), + st::historyGiftToChannel); + widthValue() | rpl::start_with_next([=](int width) { + _giftToChannel->moveToRight(0, 0, width); + }, _giftToChannel->lifetime()); + _giftToChannel->setClickedCallback([=] { + Ui::ShowStarGiftBox(controller(), _peer); + }); + rpl::combine( + _muteUnmute->shownValue(), + _joinChannel->shownValue() + ) | rpl::start_with_next([=](bool muteUnmute, bool joinChannel) { + const auto newParent = (muteUnmute && !joinChannel) + ? _muteUnmute.data() + : (joinChannel && !muteUnmute) + ? _joinChannel.data() + : nullptr; + if (newParent) { + _giftToChannel->setParent(newParent); + _giftToChannel->moveToRight(0, 0); + refreshGiftToChannelShown(); + } + }, _giftToChannel->lifetime()); +} + +void HistoryWidget::setupDirectMessageButton() { + _directMessage = Ui::CreateChild( + _muteUnmute.data(), + st::historyDirectMessage); + widthValue() | rpl::start_with_next([=](int width) { + _directMessage->moveToRight(0, 0, width); + }, _directMessage->lifetime()); + _directMessage->setClickedCallback([=] { + if (const auto channel = _peer ? _peer->asChannel() : nullptr) { + if (const auto monoforum = channel->monoforumLink()) { + controller()->showPeerHistory( + monoforum, + Window::SectionShow::Way::Forward); } - }); - return button; - }; - _giftToChannelIn = setupButton(_muteUnmute); - _giftToChannelOut = setupButton(_joinChannel); + } + }); + rpl::combine( + _muteUnmute->shownValue(), + _joinChannel->shownValue() + ) | rpl::start_with_next([=](bool muteUnmute, bool joinChannel) { + const auto newParent = (muteUnmute && !joinChannel) + ? _muteUnmute.data() + : (joinChannel && !muteUnmute) + ? _joinChannel.data() + : nullptr; + if (newParent) { + _directMessage->setParent(newParent); + _directMessage->moveToLeft(0, 0); + refreshDirectMessageShown(); + } + }, _directMessage->lifetime()); } void HistoryWidget::pushReplyReturn(not_null item) { @@ -2456,6 +2506,7 @@ void HistoryWidget::showHistory( }, _contactStatus->bar().lifetime()); refreshGiftToChannelShown(); + refreshDirectMessageShown(); if (const auto user = _peer->asUser()) { _paysStatus = std::make_unique( controller(), @@ -5220,7 +5271,10 @@ bool HistoryWidget::isBlocked() const { } bool HistoryWidget::isJoinChannel() const { - return _peer && _peer->isChannel() && !_peer->asChannel()->amIn(); + if (const auto channel = _peer ? _peer->asChannel() : nullptr) { + return !channel->amIn() && !channel->isMonoforum(); + } + return false; } bool HistoryWidget::isChoosingTheme() const { @@ -8639,6 +8693,7 @@ void HistoryWidget::fullInfoUpdated() { sendBotStartCommand(); } refreshGiftToChannelShown(); + refreshDirectMessageShown(); } if (updateCmdStartShown()) { refresh = true; diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 33571de179..c11ecdc7c0 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -406,6 +406,7 @@ private: void refreshJoinChannelText(); void refreshGiftToChannelShown(); + void refreshDirectMessageShown(); void requestMessageData(MsgId msgId); void messageDataReceived(not_null peer, MsgId msgId); @@ -535,6 +536,7 @@ private: void setupShortcuts(); void setupGiftToChannelButton(); + void setupDirectMessageButton(); void handlePeerMigration(); @@ -797,8 +799,8 @@ private: object_ptr _botStart; object_ptr _joinChannel; object_ptr _muteUnmute; - QPointer _giftToChannelIn; - QPointer _giftToChannelOut; + QPointer _giftToChannel; + QPointer _directMessage; object_ptr _reportMessages; struct { object_ptr button = { nullptr }; diff --git a/Telegram/SourceFiles/history/view/history_view_sublist_section.cpp b/Telegram/SourceFiles/history/view/history_view_sublist_section.cpp index 793133bbd0..c3f74ac708 100644 --- a/Telegram/SourceFiles/history/view/history_view_sublist_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_sublist_section.cpp @@ -606,7 +606,7 @@ rpl::producer SublistWidget::listSource( ? (*result.fullCount - after - useBefore) : std::optional(); if (!result.fullCount || useBefore < limitBefore) { - _sublist->owner().savedMessages().loadMore(_sublist); + _sublist->parent()->loadMore(_sublist); } consumer.put_next(std::move(result)); }; diff --git a/Telegram/SourceFiles/info/media/info_media_buttons.cpp b/Telegram/SourceFiles/info/media/info_media_buttons.cpp index ae10a739e6..c15b1c8f1a 100644 --- a/Telegram/SourceFiles/info/media/info_media_buttons.cpp +++ b/Telegram/SourceFiles/info/media/info_media_buttons.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_stories_ids.h" #include "data/data_user.h" #include "history/view/history_view_sublist_section.h" +#include "history/history.h" #include "info/info_controller.h" #include "info/info_memento.h" #include "info/profile/info_profile_values.h" @@ -32,39 +33,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Info::Media { namespace { -[[nodiscard]] Window::SeparateSharedMediaType ToSeparateType( - Storage::SharedMediaType type) { +[[nodiscard]] bool SeparateSupported(Storage::SharedMediaType type) { using Type = Storage::SharedMediaType; - using SeparatedType = Window::SeparateSharedMediaType; return (type == Type::Photo) - ? SeparatedType::Photos - : (type == Type::Video) - ? SeparatedType::Videos - : (type == Type::File) - ? SeparatedType::Files - : (type == Type::MusicFile) - ? SeparatedType::Audio - : (type == Type::Link) - ? SeparatedType::Links - : (type == Type::RoundVoiceFile) - ? SeparatedType::Voices - : (type == Type::GIF) - ? SeparatedType::GIF - : SeparatedType::None; + || (type == Type::Video) + || (type == Type::File) + || (type == Type::MusicFile) + || (type == Type::Link) + || (type == Type::RoundVoiceFile) + || (type == Type::GIF); } [[nodiscard]] Window::SeparateId SeparateId( not_null peer, MsgId topicRootId, Storage::SharedMediaType type) { - if (peer->isSelf()) { + if (peer->isSelf() || !SeparateSupported(type)) { return { nullptr }; } - const auto separateType = ToSeparateType(type); - if (separateType == Window::SeparateSharedMediaType::None) { + const auto topic = topicRootId + ? peer->forumTopicFor(topicRootId) + : nullptr; + if (topicRootId && !topic) { return { nullptr }; } - return { Window::SeparateSharedMedia{ separateType, peer, topicRootId } }; + const auto thread = topic + ? (Data::Thread*)topic + : peer->owner().history(peer); + return { thread, type }; } void AddContextMenuToButton( diff --git a/Telegram/SourceFiles/info/media/info_media_widget.cpp b/Telegram/SourceFiles/info/media/info_media_widget.cpp index 5d2af53daa..e01d44f533 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_widget.cpp @@ -40,6 +40,28 @@ Type TabIndexToType(int index) { Unexpected("Index in Info::Media::TabIndexToType()"); } +tr::phrase<> SharedMediaTitle(Type type) { + switch (type) { + case Type::Photo: + return tr::lng_media_type_photos; + case Type::GIF: + return tr::lng_media_type_gifs; + case Type::Video: + return tr::lng_media_type_videos; + case Type::MusicFile: + return tr::lng_media_type_songs; + case Type::File: + return tr::lng_media_type_files; + case Type::RoundVoiceFile: + return tr::lng_media_type_audios; + case Type::Link: + return tr::lng_media_type_links; + case Type::RoundFile: + return tr::lng_media_type_rounds; + } + Unexpected("Bad media type in Info::TitleValue()"); +} + Memento::Memento(not_null controller) : Memento( (controller->peer() @@ -119,25 +141,7 @@ rpl::producer Widget::title() { if (controller()->key().peer()->sharedMediaInfo() && isStackBottom()) { return tr::lng_profile_shared_media(); } - switch (controller()->section().mediaType()) { - case Section::MediaType::Photo: - return tr::lng_media_type_photos(); - case Section::MediaType::GIF: - return tr::lng_media_type_gifs(); - case Section::MediaType::Video: - return tr::lng_media_type_videos(); - case Section::MediaType::MusicFile: - return tr::lng_media_type_songs(); - case Section::MediaType::File: - return tr::lng_media_type_files(); - case Section::MediaType::RoundVoiceFile: - return tr::lng_media_type_audios(); - case Section::MediaType::Link: - return tr::lng_media_type_links(); - case Section::MediaType::RoundFile: - return tr::lng_media_type_rounds(); - } - Unexpected("Bad media type in Info::TitleValue()"); + return SharedMediaTitle(controller()->section().mediaType())(); } void Widget::setIsStackBottom(bool isStackBottom) { diff --git a/Telegram/SourceFiles/info/media/info_media_widget.h b/Telegram/SourceFiles/info/media/info_media_widget.h index 693d01aeed..b7c53879b3 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_widget.h @@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_shared_media.h" #include "data/data_search_controller.h" +namespace tr { +template +struct phrase; +} // namespace tr + namespace Data { class ForumTopic; } // namespace Data @@ -19,8 +24,9 @@ namespace Info::Media { using Type = Storage::SharedMediaType; -std::optional TypeToTabIndex(Type type); -Type TabIndexToType(int index); +[[nodiscard]] std::optional TypeToTabIndex(Type type); +[[nodiscard]] Type TabIndexToType(int index); +[[nodiscard]] tr::phrase<> SharedMediaTitle(Type type); class InnerWidget; diff --git a/Telegram/SourceFiles/info/saved/info_saved_sublists_widget.cpp b/Telegram/SourceFiles/info/saved/info_saved_sublists_widget.cpp index 9a070889aa..dbd6557888 100644 --- a/Telegram/SourceFiles/info/saved/info_saved_sublists_widget.cpp +++ b/Telegram/SourceFiles/info/saved/info_saved_sublists_widget.cpp @@ -57,7 +57,7 @@ SublistsWidget::SublistsWidget( this, controller->parentController(), rpl::single(Dialogs::InnerWidget::ChildListShown()))); - _list->showSavedSublists(); + _list->showSavedSublists(nullptr); _list->setNarrowRatio(0.); _list->chosenRow() | rpl::start_with_next([=](Dialogs::ChosenRow row) { diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 81e21fb3c4..8b220f9792 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/platform/ui_platform_window.h" #include "platform/platform_window_title.h" #include "history/history.h" +#include "info/media/info_media_widget.h" // SharedMediaTitle. #include "window/window_separate_id.h" #include "window/window_session_controller.h" #include "window/window_lock_widgets.h" @@ -87,42 +88,24 @@ base::options::toggle OptionDisableTouchbar({ .restartRequired = true, }); -[[nodiscard]] QString TitleFromSeparateId( +[[nodiscard]] QString TitleFromSeparateSharedMedia( const Core::WindowTitleContent &settings, const SeparateId &id) { - if (id.sharedMedia == SeparateSharedMediaType::None - || !id.sharedMediaPeer()) { + if (id.type != SeparateType::SharedMedia) { return QString(); } - const auto result = (id.sharedMedia == SeparateSharedMediaType::Photos) - ? tr::lng_media_type_photos(tr::now) - : (id.sharedMedia == SeparateSharedMediaType::Videos) - ? tr::lng_media_type_videos(tr::now) - : (id.sharedMedia == SeparateSharedMediaType::Files) - ? tr::lng_media_type_files(tr::now) - : (id.sharedMedia == SeparateSharedMediaType::Audio) - ? tr::lng_media_type_songs(tr::now) - : (id.sharedMedia == SeparateSharedMediaType::Links) - ? tr::lng_media_type_links(tr::now) - : (id.sharedMedia == SeparateSharedMediaType::GIF) - ? tr::lng_media_type_gifs(tr::now) - : (id.sharedMedia == SeparateSharedMediaType::Voices) - ? tr::lng_media_type_audios(tr::now) - : QString(); - + const auto type = id.sharedMediaType; + const auto result = Info::Media::SharedMediaTitle(type)(tr::now); if (settings.hideChatName) { return result; } - const auto peer = id.sharedMediaPeer(); - const auto topicRootId = id.sharedMediaTopicRootId(); - const auto topic = topicRootId - ? peer->forumTopicFor(topicRootId) - : nullptr; + const auto thread = id.thread; + const auto topic = thread->asTopic(); const auto name = topic ? topic->title() - : peer->isSelf() + : thread->peer()->isSelf() ? tr::lng_saved_messages(tr::now) - : peer->name(); + : thread->peer()->name(); const auto wrapped = st::wrap_rtl(name); return name + u" @ "_q + result; } @@ -902,11 +885,11 @@ void MainWindow::updateTitle() { && Core::App().domain().accountsAuthedCount() > 1) ? st::wrap_rtl(session->authedName()) : QString(); - const auto separateIdTitle = session - ? TitleFromSeparateId(settings, session->windowId()) + const auto separateSharedMediaTitle = session + ? TitleFromSeparateSharedMedia(settings, session->windowId()) : QString(); - if (!separateIdTitle.isEmpty()) { - setTitle(separateIdTitle); + if (!separateSharedMediaTitle.isEmpty()) { + setTitle(separateSharedMediaTitle); return; } const auto key = (session && !settings.hideChatName) diff --git a/Telegram/SourceFiles/window/window_separate_id.cpp b/Telegram/SourceFiles/window/window_separate_id.cpp index 2b88968962..c6b0f0da62 100644 --- a/Telegram/SourceFiles/window/window_separate_id.cpp +++ b/Telegram/SourceFiles/window/window_separate_id.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/window_separate_id.h" +#include "data/data_channel.h" #include "data/data_folder.h" #include "data/data_peer.h" #include "data/data_saved_messages.h" @@ -30,10 +31,14 @@ SeparateId::SeparateId(SeparateType type, not_null session) , account(&session->account()) { } -SeparateId::SeparateId(SeparateType type, not_null thread) +SeparateId::SeparateId( + SeparateType type, + not_null thread, + ChannelData *parentChat) : type(type) , account(&thread->session().account()) -, thread(thread) { +, thread(thread) +, parentChat((type == SeparateType::SavedSublist) ? parentChat : nullptr) { } SeparateId::SeparateId(not_null thread) @@ -44,12 +49,13 @@ SeparateId::SeparateId(not_null peer) : SeparateId(SeparateType::Chat, peer->owner().history(peer)) { } -SeparateId::SeparateId(SeparateSharedMedia data) +SeparateId::SeparateId( + not_null thread, + Storage::SharedMediaType sharedMediaType) : type(SeparateType::SharedMedia) -, sharedMedia(data.type) -, account(&data.peer->session().account()) -, sharedMediaDataPeer(data.peer) -, sharedMediaDataTopicRootId(data.topicRootId) { +, sharedMediaType(sharedMediaType) +, account(&thread->session().account()) +, thread(thread) { } bool SeparateId::primary() const { @@ -71,9 +77,12 @@ Data::Folder *SeparateId::folder() const { } Data::SavedSublist *SeparateId::sublist() const { - return (type == SeparateType::SavedSublist) - ? thread->owner().savedMessages().sublist(thread->peer()).get() - : nullptr; + const auto monoforum = parentChat ? parentChat->monoforum() : nullptr; + return (type != SeparateType::SavedSublist) + ? nullptr + : monoforum + ? monoforum->sublist(thread->peer()).get() + : thread->owner().savedMessages().sublist(thread->peer()).get(); } bool SeparateId::hasChatsList() const { @@ -82,16 +91,4 @@ bool SeparateId::hasChatsList() const { || (type == SeparateType::Forum); } -PeerData *SeparateId::sharedMediaPeer() const { - return (type == SeparateType::SharedMedia) - ? sharedMediaDataPeer - : nullptr; -} - -MsgId SeparateId::sharedMediaTopicRootId() const { - return (type == SeparateType::SharedMedia) - ? sharedMediaDataTopicRootId - : MsgId(); -} - } // namespace Window diff --git a/Telegram/SourceFiles/window/window_separate_id.h b/Telegram/SourceFiles/window/window_separate_id.h index 81f417d4fb..e36e8c2a98 100644 --- a/Telegram/SourceFiles/window/window_separate_id.h +++ b/Telegram/SourceFiles/window/window_separate_id.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +class ChannelData; class PeerData; namespace Data { @@ -21,6 +22,10 @@ class Account; class Session; } // namespace Main +namespace Storage { +enum class SharedMediaType : signed char; +} // namespace Storage + namespace Window { enum class SeparateType { @@ -32,39 +37,30 @@ enum class SeparateType { SharedMedia, }; -enum class SeparateSharedMediaType { - None, - Photos, - Videos, - Files, - Audio, - Links, - Voices, - GIF, -}; - struct SeparateSharedMedia { - SeparateSharedMediaType type = SeparateSharedMediaType::None; - not_null peer; - MsgId topicRootId = MsgId(); + not_null thread; + Storage::SharedMediaType type = {}; }; struct SeparateId { SeparateId(std::nullptr_t); SeparateId(not_null account); SeparateId(SeparateType type, not_null session); - SeparateId(SeparateType type, not_null thread); + SeparateId( + SeparateType type, + not_null thread, + ChannelData *parentChat = nullptr); SeparateId(not_null thread); SeparateId(not_null peer); - SeparateId(SeparateSharedMedia data); + SeparateId( + not_null thread, + Storage::SharedMediaType sharedMediaType); SeparateType type = SeparateType::Primary; - SeparateSharedMediaType sharedMedia = SeparateSharedMediaType::None; + Storage::SharedMediaType sharedMediaType = {}; Main::Account *account = nullptr; Data::Thread *thread = nullptr; // For types except Main and Archive. - PeerData *sharedMediaDataPeer = nullptr; - MsgId sharedMediaDataTopicRootId = MsgId(); - + ChannelData *parentChat = nullptr; [[nodiscard]] bool valid() const { return account != nullptr; } @@ -77,8 +73,6 @@ struct SeparateId { [[nodiscard]] Data::Forum *forum() const; [[nodiscard]] Data::Folder *folder() const; [[nodiscard]] Data::SavedSublist *sublist() const; - [[nodiscard]] PeerData *sharedMediaPeer() const; - [[nodiscard]] MsgId sharedMediaTopicRootId() const; [[nodiscard]] bool hasChatsList() const; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 4f474261b7..6edfaf3da0 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1321,35 +1321,13 @@ void SessionNavigation::showByInitialId( showThread(id.thread, msgId, instant); break; case SeparateType::SharedMedia: { - Assert(id.sharedMedia != SeparateSharedMediaType::None); clearSectionStack(instant); - const auto type = (id.sharedMedia == SeparateSharedMediaType::Photos) - ? Storage::SharedMediaType::Photo - : (id.sharedMedia == SeparateSharedMediaType::Videos) - ? Storage::SharedMediaType::Video - : (id.sharedMedia == SeparateSharedMediaType::Files) - ? Storage::SharedMediaType::File - : (id.sharedMedia == SeparateSharedMediaType::Audio) - ? Storage::SharedMediaType::MusicFile - : (id.sharedMedia == SeparateSharedMediaType::Links) - ? Storage::SharedMediaType::Link - : (id.sharedMedia == SeparateSharedMediaType::Voices) - ? Storage::SharedMediaType::RoundVoiceFile - : (id.sharedMedia == SeparateSharedMediaType::GIF) - ? Storage::SharedMediaType::GIF - : Storage::SharedMediaType::Photo; - const auto topicRootId = id.sharedMediaTopicRootId(); - const auto peer = id.sharedMediaPeer(); - const auto topic = topicRootId - ? peer->forumTopicFor(topicRootId) - : nullptr; - if (topicRootId && !topic) { - break; - } + const auto type = id.sharedMediaType; + const auto topic = id.thread->asTopic(); showSection( - topicRootId + (topic ? std::make_shared(topic, type) - : std::make_shared(peer, type), + : std::make_shared(id.thread->peer(), type)), instant); parent->widget()->setMaximumWidth(st::maxWidthSharedMediaWindow); break;