diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index f21526288..cc240bddb 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -249,8 +249,6 @@ PRIVATE boxes/local_storage_box.h boxes/max_invite_box.cpp boxes/max_invite_box.h - boxes/mute_settings_box.cpp - boxes/mute_settings_box.h boxes/peer_list_box.cpp boxes/peer_list_box.h boxes/peer_list_controllers.cpp @@ -539,6 +537,8 @@ PRIVATE data/data_sponsored_messages.h data/data_streaming.cpp data/data_streaming.h + data/data_thread.cpp + data/data_thread.h data/data_types.cpp data/data_types.h data/data_user.cpp diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 16f251845..4cffcf426 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -474,6 +474,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer; notifyUsers#b4c83b4c = NotifyPeer; notifyChats#c007cec3 = NotifyPeer; notifyBroadcasts#d612e8ef = NotifyPeer; +notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer; sendMessageTypingAction#16bf744e = SendMessageAction; sendMessageCancelAction#fd5ec8f5 = SendMessageAction; @@ -1456,6 +1457,7 @@ stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username; +forumTopicDeleted#23f109b id:int = ForumTopic; forumTopic#5920d6dc flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings = ForumTopic; messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector messages:Vector chats:Vector users:Vector pts:int = messages.ForumTopics; diff --git a/Telegram/SourceFiles/api/api_unread_things.cpp b/Telegram/SourceFiles/api/api_unread_things.cpp index f0729fe35..8888d794f 100644 --- a/Telegram/SourceFiles/api/api_unread_things.cpp +++ b/Telegram/SourceFiles/api/api_unread_things.cpp @@ -30,12 +30,12 @@ UnreadThings::UnreadThings(not_null api) : _api(api) { } bool UnreadThings::trackMentions(Data::Thread *thread) const { - const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr; + const auto peer = thread ? thread->peer().get() : nullptr; return peer && (peer->isChat() || peer->isMegagroup()); } bool UnreadThings::trackReactions(Data::Thread *thread) const { - const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr; + const auto peer = thread ? thread->peer().get() : nullptr; return peer && (peer->isChat() || peer->isMegagroup()); } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index f9a0dcaa5..8c58f4e83 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1729,10 +1729,10 @@ void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) { const auto requestId = request(MTPaccount_GetNotifySettings( peer )).done([=](const MTPPeerNotifySettings &result) { - applyNotifySettings(peer, result); + _session->data().notifySettings().apply(peer, result); _notifySettingRequests.remove(key); }).fail([=] { - applyNotifySettings( + _session->data().notifySettings().apply( peer, MTP_peerNotifySettings( MTP_flags(0), @@ -1748,7 +1748,11 @@ void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) { } void ApiWrap::updateNotifySettingsDelayed( - not_null topic) { + not_null thread) { + const auto topic = thread->asTopic(); + if (!topic) { + return updateNotifySettingsDelayed(thread->peer()); + } if (_updateNotifyTopics.emplace(topic).second) { topic->destroyed( ) | rpl::start_with_next([=] { @@ -2122,51 +2126,6 @@ void ApiWrap::clearModifyRequest(const QString &key) { _modifyRequests.remove(key); } -void ApiWrap::applyNotifySettings( - MTPInputNotifyPeer notifyPeer, - const MTPPeerNotifySettings &settings) { - auto ¬ifySettings = _session->data().notifySettings(); - switch (notifyPeer.type()) { - case mtpc_inputNotifyUsers: - notifySettings.apply(MTP_notifyUsers(), settings); - break; - case mtpc_inputNotifyChats: - notifySettings.apply(MTP_notifyChats(), settings); - break; - case mtpc_inputNotifyBroadcasts: - notifySettings.apply( - MTP_notifyBroadcasts(), - settings); - break; - case mtpc_inputNotifyPeer: { - auto &peer = notifyPeer.c_inputNotifyPeer().vpeer(); - const auto apply = [&](PeerId peerId) { - notifySettings.apply( - MTP_notifyPeer(peerToMTP(peerId)), - settings); - }; - switch (peer.type()) { - case mtpc_inputPeerEmpty: - apply(0); - break; - case mtpc_inputPeerSelf: - apply(_session->userPeerId()); - break; - case mtpc_inputPeerUser: - apply(peerFromUser(peer.c_inputPeerUser().vuser_id())); - break; - case mtpc_inputPeerChat: - apply(peerFromChat(peer.c_inputPeerChat().vchat_id())); - break; - case mtpc_inputPeerChannel: - apply(peerFromChannel(peer.c_inputPeerChannel().vchannel_id())); - break; - } - } break; - } - Core::App().notifications().checkDelayed(); -} - void ApiWrap::gotStickerSet( uint64 setId, const MTPmessages_StickerSet &result) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 0ee848577..7cbfc2ab6 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -33,6 +33,7 @@ struct ResolvedForwardDraft; enum class DefaultNotify; enum class StickersType : uchar; class ForumTopic; +class Thread; } // namespace Data namespace InlineBots { @@ -144,10 +145,6 @@ public: void registerModifyRequest(const QString &key, mtpRequestId requestId); void clearModifyRequest(const QString &key); - void applyNotifySettings( - MTPInputNotifyPeer peer, - const MTPPeerNotifySettings &settings); - void saveCurrentDraftToCloud(); void savePinnedOrder(Data::Folder *folder); @@ -245,8 +242,7 @@ public: void leaveChannel(not_null channel); void requestNotifySettings(const MTPInputNotifyPeer &peer); - void updateNotifySettingsDelayed( - not_null topic); + void updateNotifySettingsDelayed(not_null thread); void updateNotifySettingsDelayed(not_null peer); void updateNotifySettingsDelayed(Data::DefaultNotify type); void saveDraftToCloudDelayed(not_null history); diff --git a/Telegram/SourceFiles/boxes/mute_settings_box.cpp b/Telegram/SourceFiles/boxes/mute_settings_box.cpp deleted file mode 100644 index ac9771884..000000000 --- a/Telegram/SourceFiles/boxes/mute_settings_box.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -This code is in Public Domain, see license terms in .github/CONTRIBUTING.md -Copyright (C) 2017, Nicholas Guriev -*/ -#include "boxes/mute_settings_box.h" - -#include "lang/lang_keys.h" -#include "main/main_session.h" -#include "data/notify/data_notify_settings.h" -#include "data/data_session.h" -#include "data/data_peer.h" -#include "ui/special_buttons.h" -#include "ui/widgets/checkbox.h" -#include "ui/widgets/labels.h" -#include "styles/style_layers.h" -#include "styles/style_boxes.h" - -namespace { - -constexpr auto kForeverHours = 24 * 365; - -} // namespace - -MuteSettingsBox::MuteSettingsBox(QWidget *parent, not_null peer) -: _peer(peer) { -} - -void MuteSettingsBox::prepare() { - setTitle(tr::lng_disable_notifications_from_tray()); - auto y = 0; - - object_ptr info(this, st::boxLabel); - info->setText(tr::lng_mute_box_tip(tr::now)); - info->moveToLeft(st::boxPadding.left(), y); - y += info->height() + st::boxLittleSkip; - - const auto icon = object_ptr( - this, - _peer, - Ui::UserpicButton::Role::Custom, - st::mutePhotoButton); - icon->setPointerCursor(false); - icon->moveToLeft(st::boxPadding.left(), y); - - object_ptr title(this, st::muteChatTitle); - title->setText(_peer->name()); - title->moveToLeft( - st::boxPadding.left() + st::muteChatTitleLeft, - y + (icon->height() / 2) - (title->height() / 2)); - // the icon is always higher than this chat title - y += icon->height() + st::boxMediumSkip; - - // in fact, this is mute only for 1 year - const auto group = std::make_shared(kForeverHours); - y += st::boxOptionListPadding.top(); - for (const auto hours : { 1, 4, 18, 72, kForeverHours }) { - const auto text = [&] { - if (hours < 24) { - return tr::lng_mute_duration_hours(tr::now, lt_count, hours); - } else if (hours < kForeverHours) { - return tr::lng_mute_duration_days(tr::now, lt_count, hours / 24); - } else { - return tr::lng_mute_duration_forever(tr::now); - } - }(); - object_ptr option(this, group, hours, text); - option->moveToLeft(st::boxPadding.left(), y); - y += option->heightNoMargins() + st::boxOptionListSkip; - } - y += st::boxOptionListPadding.bottom() - - st::boxOptionListSkip - + st::defaultCheckbox.margin.bottom(); - - _save = [=] { - const auto muteForSeconds = group->value() * 3600; - _peer->owner().notifySettings().update( - _peer, - { .period = muteForSeconds }); - closeBox(); - }; - addButton(tr::lng_box_ok(), _save); - addButton(tr::lng_cancel(), [this] { closeBox(); }); - - setDimensions(st::boxWidth, y); -} - -void MuteSettingsBox::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (_save) { - _save(); - } - } -} - -// vi: ts=4 tw=80 diff --git a/Telegram/SourceFiles/boxes/mute_settings_box.h b/Telegram/SourceFiles/boxes/mute_settings_box.h deleted file mode 100644 index d91692410..000000000 --- a/Telegram/SourceFiles/boxes/mute_settings_box.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -This code is in Public Domain, see license terms in .github/CONTRIBUTING.md -Copyright (C) 2017, Nicholas Guriev -*/ -#pragma once - -#include "boxes/abstract_box.h" - -/* This class implements a dialog-box with radio-buttons for pick duration of - * turning off notifications from a chat. The widget is opened by a context menu - * in the left list of dialogues. */ -class MuteSettingsBox : public Ui::BoxContent { -public: - MuteSettingsBox(QWidget *parent, not_null peer); - -protected: - void prepare() override; - - void keyPressEvent(QKeyEvent *e) override; - -private: - not_null _peer; - Fn _save; - -}; -// vi: ts=4 tw=80 diff --git a/Telegram/SourceFiles/boxes/ringtones_box.cpp b/Telegram/SourceFiles/boxes/ringtones_box.cpp index 23186daff..ef2d096ec 100644 --- a/Telegram/SourceFiles/boxes/ringtones_box.cpp +++ b/Telegram/SourceFiles/boxes/ringtones_box.cpp @@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_document_resolver.h" -#include "data/data_peer.h" +#include "data/data_thread.h" #include "data/data_session.h" #include "data/notify/data_notify_settings.h" #include "lang/lang_keys.h" @@ -342,11 +342,11 @@ void RingtonesBox( box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } -void PeerRingtonesBox( +void ThreadRingtonesBox( not_null box, - not_null peer) { - const auto now = peer->owner().notifySettings().sound(peer); - RingtonesBox(box, &peer->session(), now, [=](Data::NotifySound sound) { - peer->owner().notifySettings().update(peer, {}, {}, sound); + not_null thread) { + const auto now = thread->owner().notifySettings().sound(thread); + RingtonesBox(box, &thread->session(), now, [=](Data::NotifySound sound) { + thread->owner().notifySettings().update(thread, {}, {}, sound); }); } diff --git a/Telegram/SourceFiles/boxes/ringtones_box.h b/Telegram/SourceFiles/boxes/ringtones_box.h index d4e62baaa..13599efea 100644 --- a/Telegram/SourceFiles/boxes/ringtones_box.h +++ b/Telegram/SourceFiles/boxes/ringtones_box.h @@ -9,10 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" -class PeerData; - namespace Data { struct NotifySound; +class Thread; } // namespace Data namespace Main { @@ -31,6 +30,6 @@ void RingtonesBox( Data::NotifySound selected, Fn save); -void PeerRingtonesBox( +void ThreadRingtonesBox( not_null box, - not_null peer); + not_null thread); diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index c07ec3722..f3e4914a5 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -20,14 +20,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "data/data_message_reactions.h" #include "data/data_peer_bot_command.h" +#include "data/notify/data_notify_settings.h" #include "main/main_session.h" #include "main/session/send_as_peers.h" #include "base/unixtime.h" +#include "core/application.h" #include "history/history.h" #include "main/main_session.h" #include "api/api_chat_invite.h" #include "api/api_invite_links.h" #include "apiwrap.h" +#include "window/notifications_manager.h" namespace { @@ -131,9 +134,14 @@ void ChannelData::setFlags(ChannelDataFlags which) { session().changes().peerUpdated(this, UpdateFlag::Migration); } } - if (diff & Flag::CallNotEmpty) { + if (diff & (Flag::Forum | Flag::CallNotEmpty)) { if (const auto history = this->owner().historyLoaded(this)) { - history->updateChatListEntry(); + if (diff & Flag::CallNotEmpty) { + history->updateChatListEntry(); + } + if (diff & Flag::Forum) { + Core::App().notifications().clearFromHistory(history); + } } } } @@ -986,8 +994,8 @@ void ApplyChannelUpdate( session->changes().peerUpdated(channel, UpdateFlag::Rights); } - session->api().applyNotifySettings( - MTP_inputNotifyPeer(channel->input), + channel->owner().notifySettings().apply( + channel, update.vnotify_settings()); if (const auto sendAs = update.vdefault_send_as()) { diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index bd23f8ce4..40496aa50 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_group_call.h" #include "data/data_message_reactions.h" +#include "data/notify/data_notify_settings.h" #include "history/history.h" #include "main/main_session.h" #include "apiwrap.h" @@ -489,9 +490,7 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { update.vrequests_pending().value_or_empty(), update.vrecent_requesters().value_or_empty()); - chat->session().api().applyNotifySettings( - MTP_inputNotifyPeer(chat->input), - update.vnotify_settings()); + chat->owner().notifySettings().apply(chat, update.vnotify_settings()); } void ApplyChatUpdate( diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index 0badcd675..6d3622e4e 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -18,9 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/random.h" #include "apiwrap.h" #include "lang/lang_keys.h" +#include "core/application.h" #include "ui/layers/generic_box.h" #include "ui/widgets/input_fields.h" #include "window/window_session_controller.h" +#include "window/notifications_manager.h" #include "styles/style_boxes.h" namespace Data { @@ -114,6 +116,16 @@ void Forum::applyReceivedTopics(const MTPmessages_ForumTopics &result) { applyReceivedTopics(result, false); } +void Forum::applyTopicDeleted(MsgId rootId) { + const auto i = _topics.find(rootId); + if (i != end(_topics)) { + Core::App().notifications().clearFromTopic(i->second.get()); + _topicDestroyed.fire(i->second.get()); + _history->destroyMessagesByTopic(rootId); + _topics.erase(i); + } +} + void Forum::applyReceivedTopics( const MTPmessages_ForumTopics &topics, bool updateOffset) { @@ -124,23 +136,32 @@ void Forum::applyReceivedTopics( channel()->ptsReceived(data.vpts().v); const auto &list = data.vtopics().v; for (const auto &topic : list) { - const auto rootId = MsgId(topic.data().vid().v); - const auto i = _topics.find(rootId); - const auto creating = (i == end(_topics)); - const auto raw = creating - ? _topics.emplace( - rootId, - std::make_unique(this, rootId) - ).first->second.get() - : i->second.get(); - raw->applyTopic(topic); - if (updateOffset) { - if (const auto last = raw->lastServerMessage()) { - _offsetDate = last->date(); - _offsetId = last->id; + const auto rootId = topic.match([&](const auto &data) { + return data.vid().v; + }); + topic.match([&](const MTPDforumTopicDeleted &data) { + if (updateOffset) { + LOG(("API Error: Got a deleted topic in getForumTopics.")); } - _offsetTopicId = rootId; - } + applyTopicDeleted(rootId); + }, [&](const MTPDforumTopic &data) { + const auto i = _topics.find(rootId); + const auto creating = (i == end(_topics)); + const auto raw = creating + ? _topics.emplace( + rootId, + std::make_unique(this, rootId) + ).first->second.get() + : i->second.get(); + raw->applyTopic(data); + if (updateOffset) { + if (const auto last = raw->lastServerMessage()) { + _offsetDate = last->date(); + _offsetId = last->id; + } + _offsetTopicId = rootId; + } + }); } if (updateOffset && (list.isEmpty() || list.size() == data.vcount().v)) { @@ -184,11 +205,13 @@ void Forum::requestTopic(MsgId rootId, Fn done) { }); } -void Forum::applyTopicAdded( +ForumTopic *Forum::applyTopicAdded( MsgId rootId, const QString &title, int32 colorId, DocumentId iconId) { + Expects(rootId != 0); + const auto i = _topics.find(rootId); const auto raw = (i != end(_topics)) ? i->second.get() @@ -203,6 +226,7 @@ void Forum::applyTopicAdded( raw->addToChatList(FilterId(), topicsList()); _chatsListChanges.fire({}); } + return raw; } MsgId Forum::reserveCreatingId( @@ -261,10 +285,6 @@ void Forum::clearAllUnreadReactions() { } } -ForumTopic *Forum::topicFor(not_null item) { - return topicFor(item->topicRootId()); -} - ForumTopic *Forum::topicFor(MsgId rootId) { if (!rootId) { return nullptr; @@ -273,6 +293,18 @@ ForumTopic *Forum::topicFor(MsgId rootId) { return (i != end(_topics)) ? i->second.get() : nullptr; } +ForumTopic *Forum::enforceTopicFor(MsgId rootId) { + Expects(rootId != 0); + + const auto i = _topics.find(rootId); + if (i != end(_topics)) { + return i->second.get(); + } + const auto result = applyTopicAdded(rootId, {}, {}, {}); + requestTopic(rootId); + return result; +} + rpl::producer<> Forum::chatsListChanges() const { return _chatsListChanges.events(); } diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h index 117ad8b4a..b3eb61274 100644 --- a/Telegram/SourceFiles/data/data_forum.h +++ b/Telegram/SourceFiles/data/data_forum.h @@ -43,14 +43,15 @@ public: [[nodiscard]] rpl::producer<> chatsListLoadedEvents() const; void requestTopic(MsgId rootId, Fn done = nullptr); - void applyTopicAdded( + ForumTopic *applyTopicAdded( MsgId rootId, const QString &title, int32 colorId, DocumentId iconId); void applyTopicCreated(MsgId rootId, MsgId realId); - [[nodiscard]] ForumTopic *topicFor(not_null item); + void applyTopicDeleted(MsgId rootId); [[nodiscard]] ForumTopic *topicFor(MsgId rootId); + [[nodiscard]] ForumTopic *enforceTopicFor(MsgId rootId); void applyReceivedTopics(const MTPmessages_ForumTopics &topics); diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index 8fa2d05c3..49be44b6e 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -197,10 +197,9 @@ void ForumTopic::setRealRootId(MsgId realId) { } } -void ForumTopic::applyTopic(const MTPForumTopic &topic) { - Expects(_rootId == topic.data().vid().v); +void ForumTopic::applyTopic(const MTPDforumTopic &data) { + Expects(_rootId == data.vid().v); - const auto &data = topic.data(); applyTitle(qs(data.vtitle())); if (const auto iconId = data.vicon_emoji_id()) { applyIconId(iconId->v); @@ -218,6 +217,8 @@ void ForumTopic::applyTopic(const MTPForumTopic &topic) { } #endif + owner().notifySettings().apply(this, data.vnotify_settings()); + _replies->setInboxReadTill( data.vread_inbox_max_id().v, data.vunread_count().v); diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h index bc1d3ebac..26aa4e32f 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.h +++ b/Telegram/SourceFiles/data/data_forum_topic.h @@ -41,7 +41,7 @@ class Forum; const QString &title, const style::ForumTopicIcon &st); -class ForumTopic final : public Data::Thread { +class ForumTopic final : public Thread { public: ForumTopic(not_null forum, MsgId rootId); ~ForumTopic(); @@ -59,7 +59,7 @@ public: void setRealRootId(MsgId realId); - void applyTopic(const MTPForumTopic &topic); + void applyTopic(const MTPDforumTopic &data); TimeId adjustedChatListTimeId() const override; @@ -91,17 +91,17 @@ public: void applyItemAdded(not_null item); void applyItemRemoved(MsgId id); - [[nodiscard]] Data::PeerNotifySettings ¬ify() { + [[nodiscard]] PeerNotifySettings ¬ify() { return _notify; } - [[nodiscard]] const Data::PeerNotifySettings ¬ify() const { + [[nodiscard]] const PeerNotifySettings ¬ify() const { return _notify; } void loadUserpic() override; void paintUserpic( Painter &p, - std::shared_ptr &view, + std::shared_ptr &view, const Dialogs::Ui::PaintContext &context) const override; [[nodiscard]] int unreadCount() const; @@ -127,12 +127,12 @@ private: int count, bool known) const; - const not_null _forum; + const not_null _forum; const not_null _list; std::shared_ptr _replies; MsgId _rootId = 0; - Data::PeerNotifySettings _notify; + PeerNotifySettings _notify; QString _title; DocumentId _iconId = 0; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 805440472..47cb364e1 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -899,16 +899,6 @@ Data::Forum *PeerData::forum() const { return nullptr; } -Data::ForumTopic *PeerData::forumTopicFor( - not_null item) const { - if (const auto rootId = item->topicRootId()) { - if (const auto forum = this->forum()) { - return forum->topicFor(rootId); - } - } - return nullptr; -} - Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const { if (!rootId) { return nullptr; diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 329ec2c3f..cf4bef19c 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -198,8 +198,6 @@ public: } [[nodiscard]] Data::Forum *forum() const; - [[nodiscard]] Data::ForumTopic *forumTopicFor( - not_null item) const; [[nodiscard]] Data::ForumTopic *forumTopicFor(MsgId rootId) const; [[nodiscard]] Data::PeerNotifySettings ¬ify() { diff --git a/Telegram/SourceFiles/data/data_thread.cpp b/Telegram/SourceFiles/data/data_thread.cpp index a35ac1d8d..602eaf062 100644 --- a/Telegram/SourceFiles/data/data_thread.cpp +++ b/Telegram/SourceFiles/data/data_thread.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_thread.h" +#include "data/data_forum_topic.h" +#include "data/data_peer.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_unread_things.h" @@ -15,6 +17,19 @@ namespace Data { Thread::~Thread() = default; +not_null Thread::peer() const { + return owningHistory()->peer; +} + +PeerNotifySettings &Thread::notify() { + const auto topic = asTopic(); + return topic ? topic->notify() : peer()->notify(); +} + +const PeerNotifySettings &Thread::notify() const { + return const_cast(this)->notify(); +} + void Thread::setUnreadThingsKnown() { _flags |= Flag::UnreadThingsKnown; } @@ -72,7 +87,7 @@ void Thread::clearNotifications() { } void Thread::clearIncomingNotifications() { - if (!owningHistory()->peer->isSelf()) { + if (!peer()->isSelf()) { const auto proj = [](ItemNotification notification) { return notification.item->out(); }; diff --git a/Telegram/SourceFiles/data/data_thread.h b/Telegram/SourceFiles/data/data_thread.h index 80f5d0991..e3c8c08d9 100644 --- a/Telegram/SourceFiles/data/data_thread.h +++ b/Telegram/SourceFiles/data/data_thread.h @@ -30,6 +30,8 @@ extern const int &dialogsTextWidthMin; namespace Data { +class PeerNotifySettings; + enum class ItemNotificationType { Message, Reaction, @@ -55,6 +57,9 @@ public: [[nodiscard]] not_null owningHistory() const { return const_cast(this)->owningHistory(); } + [[nodiscard]] not_null peer() const; + [[nodiscard]] PeerNotifySettings ¬ify(); + [[nodiscard]] const PeerNotifySettings ¬ify() const; void setUnreadThingsKnown(); [[nodiscard]] HistoryUnreadThings::Proxy unreadMentions(); diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index eb6d62e6e..f3a2f1c47 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -13,8 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_peer_bot_command.h" #include "data/data_emoji_statuses.h" +#include "data/notify/data_notify_settings.h" #include "ui/text/text_options.h" -#include "apiwrap.h" #include "lang/lang_keys.h" #include "styles/style_chat.h" @@ -320,9 +320,7 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { user->owner().processPhoto(*photo); } user->setSettings(update.vsettings()); - user->session().api().applyNotifySettings( - MTP_inputNotifyPeer(user->input), - update.vnotify_settings()); + user->owner().notifySettings().apply(user, update.vnotify_settings()); user->setMessagesTTL(update.vttl_period().value_or_empty()); if (const auto info = update.vbot_info()) { diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp index 335d2dfa7..77f3bf207 100644 --- a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp +++ b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp @@ -62,50 +62,125 @@ void NotifySettings::request(not_null peer) { } } -void NotifySettings::request(not_null topic) { - if (topic->notify().settingsUnknown()) { - topic->session().api().requestNotifySettings( - MTP_inputNotifyForumTopic( - topic->channel()->input, - MTP_int(topic->rootId()))); +void NotifySettings::request(not_null thread) { + if (const auto topic = thread->asTopic()) { + if (topic->notify().settingsUnknown()) { + topic->session().api().requestNotifySettings( + MTP_inputNotifyForumTopic( + topic->channel()->input, + MTP_int(topic->rootId()))); + } } - request(topic->channel()); + request(thread->peer()); } void NotifySettings::apply( const MTPNotifyPeer ¬ifyPeer, const MTPPeerNotifySettings &settings) { - const auto set = [&](DefaultNotify type) { - if (defaultValue(type).settings.change(settings)) { - updateLocal(type); - } + notifyPeer.match([&](const MTPDnotifyUsers &) { + apply(DefaultNotify::User, settings); + }, [&](const MTPDnotifyChats &) { + apply(DefaultNotify::Group, settings); + }, [&](const MTPDnotifyBroadcasts &) { + apply(DefaultNotify::Broadcast, settings); + }, [&](const MTPDnotifyPeer &data) { + apply(peerFromMTP(data.vpeer()), settings); + }, [&](const MTPDnotifyForumTopic &data) { + apply(peerFromMTP(data.vpeer()), data.vtop_msg_id().v, settings); + }); +} + +void NotifySettings::apply( + const MTPInputNotifyPeer ¬ifyPeer, + const MTPPeerNotifySettings &settings) { + const auto peerFromInput = [&](const MTPInputPeer &peer) { + return peer.match([&](const MTPDinputPeerSelf &) { + return _owner->session().userPeerId(); + }, [](const MTPDinputPeerUser &data) { + return peerFromUser(data.vuser_id()); + }, [](const MTPDinputPeerChat &data) { + return peerFromChat(data.vchat_id()); + }, [](const MTPDinputPeerChannel &data) { + return peerFromChannel(data.vchannel_id()); + }, [](const MTPDinputPeerUserFromMessage &data) -> PeerId { + Unexpected("From message peer in NotifySettings::apply."); + }, [](const MTPDinputPeerChannelFromMessage &data) -> PeerId { + Unexpected("From message peer in NotifySettings::apply."); + }, [](const MTPDinputPeerEmpty &) -> PeerId { + Unexpected("Empty peer in NotifySettings::apply."); + }); }; - switch (notifyPeer.type()) { - case mtpc_notifyUsers: set(DefaultNotify::User); break; - case mtpc_notifyChats: set(DefaultNotify::Group); break; - case mtpc_notifyBroadcasts: set(DefaultNotify::Broadcast); break; - case mtpc_notifyPeer: { - const auto &data = notifyPeer.c_notifyPeer(); - if (const auto peer = _owner->peerLoaded(peerFromMTP(data.vpeer()))) { - if (peer->notify().change(settings)) { - updateLocal(peer); - } + notifyPeer.match([&](const MTPDinputNotifyUsers &) { + apply(DefaultNotify::User, settings); + }, [&](const MTPDinputNotifyChats &) { + apply(DefaultNotify::Group, settings); + }, [&](const MTPDinputNotifyBroadcasts &) { + apply(DefaultNotify::Broadcast, settings); + }, [&](const MTPDinputNotifyPeer &data) { + apply(peerFromInput(data.vpeer()), settings); + }, [&](const MTPDinputNotifyForumTopic &data) { + apply(peerFromInput(data.vpeer()), data.vtop_msg_id().v, settings); + }); +} + +void NotifySettings::apply( + DefaultNotify type, + const MTPPeerNotifySettings &settings) { + if (defaultValue(type).settings.change(settings)) { + updateLocal(type); + Core::App().notifications().checkDelayed(); + } +} + +void NotifySettings::apply( + PeerId peerId, + const MTPPeerNotifySettings &settings) { + if (const auto peer = _owner->peerLoaded(peerId)) { + apply(peer, settings); + } +} + +void NotifySettings::apply( + not_null peer, + const MTPPeerNotifySettings &settings) { + if (peer->notify().change(settings)) { + updateLocal(peer); + Core::App().notifications().checkDelayed(); + } +} + +void NotifySettings::apply( + PeerId peerId, + MsgId topicRootId, + const MTPPeerNotifySettings &settings) { + if (const auto peer = _owner->peerLoaded(peerId)) { + if (const auto topic = peer->forumTopicFor(topicRootId)) { + apply(topic, settings); } - } break; + } +} + +void NotifySettings::apply( + not_null topic, + const MTPPeerNotifySettings &settings) { + if (topic->notify().change(settings)) { + updateLocal(topic); + Core::App().notifications().checkDelayed(); } } void NotifySettings::update( - not_null topic, + not_null thread, Data::MuteValue muteForSeconds, + std::optional silentPosts, std::optional sound) { - if (topic->notify().change(muteForSeconds, std::nullopt, sound)) { - updateLocal(topic); - topic->session().api().updateNotifySettingsDelayed(topic); + if (thread->notify().change(muteForSeconds, silentPosts, sound)) { + updateLocal(thread); + thread->session().api().updateNotifySettingsDelayed(thread); } } -void NotifySettings::resetToDefault(not_null topic) { +void NotifySettings::resetToDefault(not_null thread) { const auto empty = MTP_peerNotifySettings( MTP_flags(0), MTPBool(), @@ -114,9 +189,9 @@ void NotifySettings::resetToDefault(not_null topic) { MTPNotificationSound(), MTPNotificationSound(), MTPNotificationSound()); - if (topic->notify().change(empty)) { - updateLocal(topic); - topic->session().api().updateNotifySettingsDelayed(topic); + if (thread->notify().change(empty)) { + updateLocal(thread); + thread->session().api().updateNotifySettingsDelayed(thread); } } @@ -186,7 +261,11 @@ void NotifySettings::defaultUpdate( } } -void NotifySettings::updateLocal(not_null topic) { +void NotifySettings::updateLocal(not_null thread) { + const auto topic = thread->asTopic(); + if (!topic) { + return updateLocal(thread->peer()); + } auto changesIn = crl::time(0); const auto muted = isMuted(topic, &changesIn); topic->setMuted(muted); @@ -350,35 +429,39 @@ void NotifySettings::unmuteByFinished() { } bool NotifySettings::isMuted( - not_null topic, + not_null thread, crl::time *changesIn) const { - const auto until = topic->notify().muteUntil(); + const auto topic = thread->asTopic(); + const auto until = topic ? topic->notify().muteUntil() : std::nullopt; return until ? MutedFromUntil(*until, changesIn) - : isMuted(topic->channel(), changesIn); + : isMuted(thread->peer(), changesIn); } -bool NotifySettings::isMuted(not_null topic) const { - return isMuted(topic, nullptr); +bool NotifySettings::isMuted(not_null thread) const { + return isMuted(thread, nullptr); } NotifySound NotifySettings::sound( - not_null topic) const { - const auto sound = topic->notify().sound(); - return sound ? *sound : this->sound(topic->channel()); + not_null thread) const { + const auto topic = thread->asTopic(); + const auto sound = topic ? topic->notify().sound() : std::nullopt; + return sound ? *sound : this->sound(thread->peer()); } bool NotifySettings::muteUnknown( - not_null topic) const { - return topic->notify().settingsUnknown() - || (!topic->notify().muteUntil().has_value() - && muteUnknown(topic->channel())); + not_null thread) const { + const auto topic = thread->asTopic(); + return (topic && topic->notify().settingsUnknown()) + || ((!topic || !topic->notify().muteUntil().has_value()) + && muteUnknown(thread->peer())); } bool NotifySettings::soundUnknown( - not_null topic) const { - return topic->notify().settingsUnknown() - || (!topic->notify().sound().has_value() + not_null thread) const { + const auto topic = thread->asTopic(); + return (topic && topic->notify().settingsUnknown()) + || ((!topic || !topic->notify().sound().has_value()) && soundUnknown(topic->channel())); } @@ -442,8 +525,11 @@ bool NotifySettings::settingsUnknown(not_null peer) const { } bool NotifySettings::settingsUnknown( - not_null topic) const { - return muteUnknown(topic) || soundUnknown(topic); + not_null thread) const { + const auto topic = thread->asTopic(); + return muteUnknown(thread) + || soundUnknown(thread) + || (!topic && silentPostsUnknown(thread->peer())); } rpl::producer<> NotifySettings::defaultUpdates(DefaultNotify type) const { diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.h b/Telegram/SourceFiles/data/notify/data_notify_settings.h index b07dd2f1e..4de0a8d96 100644 --- a/Telegram/SourceFiles/data/notify/data_notify_settings.h +++ b/Telegram/SourceFiles/data/notify/data_notify_settings.h @@ -17,6 +17,7 @@ namespace Data { class DocumentMedia; class Session; +class Thread; class ForumTopic; enum class DefaultNotify { @@ -30,15 +31,33 @@ public: NotifySettings(not_null owner); void request(not_null peer); - void request(not_null topic); + void request(not_null thread); + void apply( const MTPNotifyPeer ¬ifyPeer, const MTPPeerNotifySettings &settings); - void update( + void apply( + const MTPInputNotifyPeer ¬ifyPeer, + const MTPPeerNotifySettings &settings); + void apply(DefaultNotify type, const MTPPeerNotifySettings &settings); + void apply(PeerId peerId, const MTPPeerNotifySettings &settings); + void apply( + not_null peer, + const MTPPeerNotifySettings &settings); + void apply( + PeerId peerId, + MsgId topicRootId, + const MTPPeerNotifySettings &settings); + void apply( not_null topic, + const MTPPeerNotifySettings &settings); + + void update( + not_null thread, Data::MuteValue muteForSeconds, + std::optional silentPosts = std::nullopt, std::optional sound = std::nullopt); - void resetToDefault(not_null topic); + void resetToDefault(not_null thread); void update( not_null peer, Data::MuteValue muteForSeconds, @@ -64,14 +83,13 @@ public: std::optional silentPosts = std::nullopt, std::optional sound = std::nullopt); - [[nodiscard]] bool isMuted( - not_null topic) const; + [[nodiscard]] bool isMuted(not_null thread) const; [[nodiscard]] NotifySound sound( - not_null topic) const; + not_null thread) const; [[nodiscard]] bool muteUnknown( - not_null topic) const; + not_null thread) const; [[nodiscard]] bool soundUnknown( - not_null topic) const; + not_null thread) const; [[nodiscard]] bool isMuted(not_null peer) const; [[nodiscard]] bool silentPosts(not_null peer) const; @@ -90,7 +108,7 @@ private: void cacheSound(const std::optional &sound); [[nodiscard]] bool isMuted( - not_null peer, + not_null thread, crl::time *changesIn) const; [[nodiscard]] bool isMuted( not_null peer, @@ -102,11 +120,11 @@ private: not_null peer) const; [[nodiscard]] bool settingsUnknown(not_null peer) const; [[nodiscard]] bool settingsUnknown( - not_null topic) const; + not_null thread) const; void unmuteByFinished(); void unmuteByFinishedDelayed(crl::time delay); - void updateLocal(not_null topic); + void updateLocal(not_null thread); void updateLocal(not_null peer); void updateLocal(DefaultNotify type); diff --git a/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp b/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp index 619f61074..e1318e574 100644 --- a/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp +++ b/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp @@ -180,9 +180,7 @@ MTPinputPeerNotifySettings NotifyPeerSettingsValue::serialize() const { PeerNotifySettings::PeerNotifySettings() = default; bool PeerNotifySettings::change(const MTPPeerNotifySettings &settings) { - Expects(settings.type() == mtpc_peerNotifySettings); - - auto &data = settings.c_peerNotifySettings(); + auto &data = settings.data(); const auto empty = !data.vflags().v; if (empty) { if (!_known || _value) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index a0598c29a..9f3bc1a4d 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -81,6 +81,22 @@ Data::ForumTopic *Entry::asTopic() { : nullptr; } +const History *Entry::asHistory() const { + return const_cast(this)->asHistory(); +} + +const Data::Folder *Entry::asFolder() const { + return const_cast(this)->asFolder(); +} + +const Data::Thread *Entry::asThread() const { + return const_cast(this)->asThread(); +} + +const Data::ForumTopic *Entry::asTopic() const { + return const_cast(this)->asTopic(); +} + void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) { if (!filterId && session().supportMode()) { // Force reorder in support mode. diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index ae3c5f56f..5f7d49e40 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -119,6 +119,11 @@ public: Data::Thread *asThread(); Data::ForumTopic *asTopic(); + const History *asHistory() const; + const Data::Folder *asFolder() const; + const Data::Thread *asThread() const; + const Data::ForumTopic *asTopic() const; + PositionChange adjustByPosInChatList( FilterId filterId, not_null list); diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.cpp b/Telegram/SourceFiles/dialogs/dialogs_key.cpp index 9a43dc8a1..1156daf43 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_key.cpp @@ -53,17 +53,15 @@ Data::Thread *Key::thread() const { return _value ? _value->asThread() : nullptr; } -History *Key::parentHistory() const { - if (const auto result = history()) { - return result; - } else if (const auto child = topic()) { - return child->history(); +History *Key::owningHistory() const { + if (const auto thread = this->thread()) { + return thread->owningHistory(); } return nullptr; } PeerData *Key::peer() const { - if (const auto history = parentHistory()) { + if (const auto history = owningHistory()) { return history->peer; } return nullptr; diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h index 1e3bcb987..620b81ddd 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.h +++ b/Telegram/SourceFiles/dialogs/dialogs_key.h @@ -42,7 +42,7 @@ public: Data::Folder *folder() const; Data::ForumTopic *topic() const; Data::Thread *thread() const; - History *parentHistory() const; + History *owningHistory() const; PeerData *peer() const; friend inline constexpr auto operator<=>(Key, Key) noexcept = default; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index aa9550553..8a6749325 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -152,7 +152,7 @@ void History::checkChatListMessageRemoved(not_null item) { } void History::itemVanished(not_null item) { - item->thread()->removeNotification(item); + item->notificationThread()->removeNotification(item); if (lastKeyboardId == item->id) { clearLastKeyboard(); } @@ -450,6 +450,7 @@ void History::destroyMessage(not_null item) { void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) { auto toDestroy = std::vector>(); + toDestroy.reserve(_messages.size()); for (const auto &message : _messages) { if (message->isRegular() && message->date() > minDate @@ -462,6 +463,19 @@ void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) { } } +void History::destroyMessagesByTopic(MsgId topicRootId) { + auto toDestroy = std::vector>(); + toDestroy.reserve(_messages.size()); + for (const auto &message : _messages) { + if (message->topicRootId() == topicRootId) { + toDestroy.push_back(message.get()); + } + } + for (const auto item : toDestroy) { + item->destroy(); + } +} + void History::unpinAllMessages() { session().storage().remove( Storage::SharedMediaRemoveAll( @@ -1123,7 +1137,7 @@ void History::newItemAdded(not_null item) { .type = Data::ItemNotificationType::Message, }; if (item->showNotification()) { - item->thread()->pushNotification(notification); + item->notificationThread()->pushNotification(notification); } owner().notifyNewItemAdded(item); const auto stillShow = item->showNotification(); // Could be read already. diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index e495a992b..9bda7c26f 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -134,6 +134,7 @@ public: } void destroyMessage(not_null item); void destroyMessagesByDates(TimeId minDate, TimeId maxDate); + void destroyMessagesByTopic(MsgId topicRootId); void unpinAllMessages(); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 336d87736..12b4abca8 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -232,7 +232,7 @@ void CheckReactionNotificationSchedule( .reactionSender = user, .type = Data::ItemNotificationType::Reaction, }; - item->thread()->pushNotification(notification); + item->notificationThread()->pushNotification(notification); Core::App().notifications().schedule(notification); return; } @@ -620,9 +620,11 @@ void HistoryItem::destroy() { _history->destroyMessage(this); } -not_null HistoryItem::thread() const { - if (const auto topic = this->topic()) { - return topic; +not_null HistoryItem::notificationThread() const { + if (const auto rootId = topicRootId()) { + if (const auto forum = _history->peer->forum()) { + return forum->enforceTopicFor(rootId); + } } return _history; } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 953a14783..053f69629 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -123,7 +123,7 @@ public: const QString &label, const TextWithEntities &content); - [[nodiscard]] not_null thread() const; + [[nodiscard]] not_null notificationThread() const; [[nodiscard]] not_null history() const { return _history; } diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index a27e73979..56ec46ba2 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -743,12 +743,12 @@ void TopBarWidget::setActiveChat( _titleNameVersion = 0; _emojiInteractionSeen = nullptr; _activeChatLifetime.destroy(); - if (const auto history = _activeChat.key.parentHistory()) { + if (const auto peer = _activeChat.key.peer()) { session().changes().peerFlagsValue( - history->peer, + peer, Data::PeerUpdate::Flag::GroupCall ) | rpl::map([=] { - return history->peer->groupCall(); + return peer->groupCall(); }) | rpl::distinct_until_changed( ) | rpl::map([](Data::GroupCall *call) { return call ? call->fullCountValue() : rpl::single(-1); diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index d32c7d402..bde6109e9 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer_values.h" #include "data/data_session.h" #include "data/data_folder.h" +#include "data/data_forum_topic.h" #include "data/data_channel.h" #include "data/data_changes.h" #include "data/data_user.h" @@ -22,13 +23,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/box_content_divider.h" #include "ui/boxes/report_box.h" +#include "ui/boxes/confirm_box.h" #include "ui/layers/generic_box.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "history/history_location_manager.h" // LocationClickHandler. #include "history/view/history_view_context_menu.h" // HistoryView::ShowReportPeerBox #include "boxes/abstract_box.h" -#include "ui/boxes/confirm_box.h" #include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/add_contact_box.h" @@ -37,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/report_messages_box.h" #include "lang/lang_keys.h" #include "menu/menu_mute.h" +#include "history/history.h" #include "info/info_controller.h" #include "info/info_memento.h" #include "info/profile/info_profile_icon.h" @@ -132,6 +134,10 @@ public: not_null controller, not_null parent, not_null peer); + DetailsFiller( + not_null controller, + not_null parent, + not_null topic); object_ptr fill(); @@ -159,6 +165,7 @@ private: not_null _controller; not_null _parent; not_null _peer; + Data::ForumTopic *_topic = nullptr; object_ptr _wrap; }; @@ -202,6 +209,17 @@ DetailsFiller::DetailsFiller( , _wrap(_parent) { } +DetailsFiller::DetailsFiller( + not_null controller, + not_null parent, + not_null topic) +: _controller(controller) +, _parent(parent) +, _peer(topic->peer()) +, _topic(topic) +, _wrap(_parent) { +} + template bool SetClickContext( const ClickHandlerPtr &handler, @@ -338,16 +356,20 @@ object_ptr DetailsFiller::setupInfo() { [=] { controller->window().show(Box(EditContactBox, controller, user)); }, tracker); } else { + const auto topicRootId = _topic ? _topic->rootId() : 0; + const auto addToLink = topicRootId + ? "?topic=" + QString::number(topicRootId.bare) + : QString(); auto linkText = LinkValue( _peer - ) | rpl::map([](const QString &link) { + ) | rpl::map([=](const QString &link) { return link.isEmpty() ? TextWithEntities() : Ui::Text::Link( (link.startsWith(qstr("https://")) ? link.mid(qstr("https://").size()) - : link), - link); + : link) + addToLink, + link + addToLink); }); auto link = addInfoOneLine( tr::lng_info_link_label(), @@ -358,14 +380,14 @@ object_ptr DetailsFiller::setupInfo() { const auto link = peer->session().createInternalLinkFull( peer->userName()); if (!link.isEmpty()) { - QGuiApplication::clipboard()->setText(link); + QGuiApplication::clipboard()->setText(link + addToLink); Ui::Toast::Show( Window::Show(controller).toastParent(), tr::lng_username_copied(tr::now)); } }); - if (const auto channel = _peer->asChannel()) { + if (const auto channel = _topic ? nullptr : _peer->asChannel()) { auto locationText = LocationValue( channel ) | rpl::map([](const ChannelLocation *location) { @@ -382,7 +404,9 @@ object_ptr DetailsFiller::setupInfo() { )->setLinksTrusted(); } - addInfoLine(tr::lng_info_about_label(), AboutValue(_peer)); + addInfoLine( + tr::lng_info_about_label(), + _topic ? rpl::single(TextWithEntities()) : AboutValue(_peer)); } if (!_peer->isSelf()) { // No notifications toggle for Self => no separator. @@ -400,17 +424,27 @@ object_ptr DetailsFiller::setupInfo() { result, st::infoIconInformation, st::infoInformationIconPosition); + return result; } object_ptr DetailsFiller::setupMuteToggle() { const auto peer = _peer; + const auto topicRootId = _topic ? _topic->rootId() : MsgId(); + const auto makeThread = [=] { + return topicRootId + ? static_cast(peer->forumTopicFor(topicRootId)) + : peer->owner().history(peer).get(); + }; auto result = object_ptr( _wrap, tr::lng_profile_enable_notifications(), st::infoNotificationsButton); - result->toggleOn(NotificationsEnabledValue(peer), true); + result->toggleOn(_topic + ? NotificationsEnabledValue(_topic) + : NotificationsEnabledValue(peer), true); result->setAcceptBoth(); + const auto notifySettings = &peer->owner().notifySettings(); MuteMenu::SetupMuteMenu( result.data(), result->clicks( @@ -418,16 +452,15 @@ object_ptr DetailsFiller::setupMuteToggle() { if (button == Qt::RightButton) { return true; } - if (peer->owner().notifySettings().isMuted(peer)) { - peer->owner().notifySettings().update( - peer, - { .unmute = true }); + if (notifySettings->isMuted(peer)) { + notifySettings->update(peer, { .unmute = true }); return false; } else { return true; } }) | rpl::to_empty, - { peer, std::make_shared(_controller) }); + makeThread, + std::make_shared(_controller)); object_ptr( result, st::infoIconNotifications, @@ -834,6 +867,14 @@ object_ptr SetupDetails( return filler.fill(); } +object_ptr SetupDetails( + not_null controller, + not_null parent, + not_null topic) { + DetailsFiller filler(controller, parent, topic); + return filler.fill(); +} + object_ptr SetupActions( not_null controller, not_null parent, diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.h b/Telegram/SourceFiles/info/profile/info_profile_actions.h index 430263095..3dfae7358 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.h +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.h @@ -13,17 +13,26 @@ namespace Ui { class RpWidget; } // namespace Ui +namespace Data { +class ForumTopic; +} // namespace Data + namespace Info { - class Controller; +} // namespace Info -namespace Profile { +namespace Info::Profile { object_ptr SetupDetails( not_null controller, not_null parent, not_null peer); +object_ptr SetupDetails( + not_null controller, + not_null parent, + not_null topic); + object_ptr SetupActions( not_null controller, not_null parent, @@ -34,5 +43,4 @@ object_ptr SetupChannelMembers( not_null parent, not_null peer); -} // namespace Profile -} // namespace Info +} // namespace Info::Profile diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index e885c9a6a..e56be98d7 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -79,12 +79,14 @@ object_ptr InnerWidget::setupContent( }, _cover->lifetime()); _cover->setOnlineCount(rpl::single(0)); if (_topic) { - result->add(setupSharedMedia(result.data())); + result->add(SetupDetails(_controller, parent, _topic)); + } else { + result->add(SetupDetails(_controller, parent, _peer)); + } + result->add(setupSharedMedia(result.data())); + if (_topic) { return result; } - - result->add(SetupDetails(_controller, parent, _peer)); - result->add(setupSharedMedia(result.data())); if (auto members = SetupChannelMembers(_controller, result.data(), _peer)) { result->add(std::move(members)); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 3ef5d9729..5d150b7ee 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -181,7 +181,11 @@ rpl::producer LocationValue( } rpl::producer NotificationsEnabledValue( - not_null topic) { + not_null thread) { + const auto topic = thread->asTopic(); + if (!topic) { + return NotificationsEnabledValue(thread->peer()); + } return rpl::merge( topic->session().changes().topicFlagsValue( topic, diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index 6a011a64e..a687c9f6b 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -16,6 +16,7 @@ struct ChannelLocation; namespace Data { class ForumTopic; +class Thread; } // namespace Data namespace Main { @@ -63,7 +64,7 @@ rpl::producer> MigratedOrMeValue( [[nodiscard]] rpl::producer NotificationsEnabledValue( not_null peer); [[nodiscard]] rpl::producer NotificationsEnabledValue( - not_null topic); + not_null thread); [[nodiscard]] rpl::producer IsContactValue(not_null user); [[nodiscard]] rpl::producer InviteToChatButton( not_null user); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 090759dec..8488924c7 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -72,7 +72,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "inline_bots/inline_bot_layout_item.h" #include "ui/boxes/confirm_box.h" #include "boxes/sticker_set_box.h" -#include "boxes/mute_settings_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/download_path_box.h" #include "boxes/connection_box.h" diff --git a/Telegram/SourceFiles/menu/menu_mute.cpp b/Telegram/SourceFiles/menu/menu_mute.cpp index e5fee5703..116991c06 100644 --- a/Telegram/SourceFiles/menu/menu_mute.cpp +++ b/Telegram/SourceFiles/menu/menu_mute.cpp @@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "menu/menu_mute.h" #include "boxes/ringtones_box.h" -#include "data/data_peer.h" #include "data/data_session.h" +#include "data/data_thread.h" #include "data/notify/data_notify_settings.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" @@ -70,7 +70,7 @@ public: MuteItem( not_null parent, const style::Menu &st, - not_null peer); + not_null thread); protected: void paintEvent(QPaintEvent *e) override; @@ -85,7 +85,7 @@ private: MuteItem::MuteItem( not_null parent, const style::Menu &st, - not_null peer) + not_null thread) : Ui::Menu::Action( parent, st, @@ -93,10 +93,9 @@ MuteItem::MuteItem( nullptr, nullptr) , _itemIconPosition(st.itemIconPosition) -, _isMuted(peer->owner().notifySettings().isMuted(peer)) { - +, _isMuted(thread->owner().notifySettings().isMuted(thread)) { Info::Profile::NotificationsEnabledValue( - peer + thread ) | rpl::start_with_next([=](bool isUnmuted) { const auto isMuted = !isUnmuted; action()->setText(isMuted @@ -113,10 +112,13 @@ MuteItem::MuteItem( st::defaultPopupMenu.showDuration); }, lifetime()); + const auto weak = base::make_weak(thread.get()); setClickedCallback([=] { - peer->owner().notifySettings().update( - peer, - { .unmute = _isMuted, .forever = !_isMuted }); + if (const auto strong = weak.get()) { + strong->owner().notifySettings().update( + strong, + { .unmute = _isMuted, .forever = !_isMuted }); + } }); } @@ -138,7 +140,7 @@ void MuteItem::paintEvent(QPaintEvent *e) { icon.paint(p, _itemIconPosition, width(), color); } -void MuteBox(not_null box, not_null peer) { +void MuteBox(not_null box, not_null thread) { struct State { int lastSeconds = 0; }; @@ -158,11 +160,15 @@ void MuteBox(not_null box, not_null peer) { ? tr::lng_mute_menu_unmute() : tr::lng_mute_menu_mute(); }) | rpl::flatten_latest(); + + const auto weak = base::make_weak(thread.get()); Ui::ConfirmBox(box, { .confirmed = [=] { - peer->owner().notifySettings().update( - peer, - { .period = state->lastSeconds }); + if (const auto strong = weak.get()) { + strong->owner().notifySettings().update( + strong, + { .period = state->lastSeconds }); + } box->getDelegate()->hideLayer(); }, .confirmText = std::move(confirmText), @@ -170,7 +176,9 @@ void MuteBox(not_null box, not_null peer) { }); } -void PickMuteBox(not_null box, not_null peer) { +void PickMuteBox( + not_null box, + not_null thread) { struct State { base::unique_qptr menu; }; @@ -183,14 +191,17 @@ void PickMuteBox(not_null box, not_null peer) { const auto pickerCallback = TimePickerBox(box, seconds, phrases, 0); + const auto weak = base::make_weak(thread.get()); Ui::ConfirmBox(box, { .confirmed = [=] { const auto muteFor = pickerCallback(); - peer->owner().notifySettings().update( - peer, - { .period = muteFor }); - peer->session().settings().addMutePeriod(muteFor); - peer->session().saveSettings(); + if (const auto strong = weak.get()) { + strong->owner().notifySettings().update( + strong, + { .period = muteFor }); + strong->session().settings().addMutePeriod(muteFor); + strong->session().saveSettings(); + } box->closeBox(); }, .confirmText = tr::lng_mute_menu_mute(), @@ -209,7 +220,11 @@ void PickMuteBox(not_null box, not_null peer) { st::popupMenuWithIcons); state->menu->addAction( tr::lng_manage_messages_ttl_after_custom(tr::now), - [=] { box->getDelegate()->show(Box(MuteBox, peer)); }, + [=] { + if (const auto strong = weak.get()) { + box->getDelegate()->show(Box(MuteBox, strong)); + } + }, &st::menuIconCustomize); state->menu->setDestroyedCallback(crl::guard(top, [=] { top->setForceRippled(false); @@ -223,38 +238,44 @@ void PickMuteBox(not_null box, not_null peer) { void FillMuteMenu( not_null menu, - Args args) { - const auto peer = args.peer; + not_null thread, + std::shared_ptr show) { + const auto weak = base::make_weak(thread.get()); + const auto with = [=](Fn thread)> handler) { + return [=] { + if (const auto strong = weak.get()) { + handler(strong); + } + }; + }; menu->addAction( tr::lng_mute_menu_sound_select(tr::now), - [=, show = args.show] { - show->showBox(Box(PeerRingtonesBox, peer)); - }, + with([=](not_null thread) { + show->showBox(Box(ThreadRingtonesBox, thread)); + }), &st::menuIconSoundSelect); - const auto soundIsNone = peer->owner().notifySettings().sound(peer).none; + const auto notifySettings = &thread->owner().notifySettings(); + const auto soundIsNone = notifySettings->sound(thread).none; menu->addAction( soundIsNone ? tr::lng_mute_menu_sound_on(tr::now) : tr::lng_mute_menu_sound_off(tr::now), - [=] { - auto ¬ifySettings = peer->owner().notifySettings(); - auto sound = notifySettings.sound(peer); + with([=](not_null thread) { + auto sound = notifySettings->sound(thread); sound.none = !sound.none; - notifySettings.update(peer, {}, {}, sound); - }, + notifySettings->update(thread, {}, {}, sound); + }), soundIsNone ? &st::menuIconSoundOn : &st::menuIconSoundOff); const auto &st = menu->st().menu; const auto iconTextPosition = st.itemIconPosition + st::menuIconMuteForAnyTextPosition; - for (const auto &muteFor : peer->session().settings().mutePeriods()) { - const auto callback = [=] { - peer->owner().notifySettings().update( - peer, - { .period = muteFor }); - }; + for (const auto muteFor : thread->session().settings().mutePeriods()) { + const auto callback = with([=](not_null thread) { + notifySettings->update(thread, { .period = muteFor }); + }); auto item = base::make_unique_q( menu, @@ -272,23 +293,23 @@ void FillMuteMenu( menu->addAction(std::move(item)); } - const auto callback = [=, show = args.show] { - DEBUG_LOG(("Mute Info: PickMuteBox called.")); - show->showBox(Box(PickMuteBox, peer)); - }; menu->addAction( tr::lng_mute_menu_duration(tr::now), - callback, + with([=](not_null thread) { + DEBUG_LOG(("Mute Info: PickMuteBox called.")); + show->showBox(Box(PickMuteBox, thread)); + }), &st::menuIconMuteFor); menu->addAction( - base::make_unique_q(menu, menu->st().menu, peer)); + base::make_unique_q(menu, menu->st().menu, thread)); } void SetupMuteMenu( not_null parent, rpl::producer<> triggers, - Args args) { + Fn makeThread, + std::shared_ptr show) { struct State { base::unique_qptr menu; }; @@ -298,12 +319,13 @@ void SetupMuteMenu( ) | rpl::start_with_next([=] { if (state->menu) { return; + } else if (const auto thread = makeThread()) { + state->menu = base::make_unique_q( + parent, + st::popupMenuWithIcons); + FillMuteMenu(state->menu.get(), thread, show); + state->menu->popup(QCursor::pos()); } - state->menu = base::make_unique_q( - parent, - st::popupMenuWithIcons); - FillMuteMenu(state->menu.get(), args); - state->menu->popup(QCursor::pos()); }, parent->lifetime()); } diff --git a/Telegram/SourceFiles/menu/menu_mute.h b/Telegram/SourceFiles/menu/menu_mute.h index a89ea64d6..045cd4d11 100644 --- a/Telegram/SourceFiles/menu/menu_mute.h +++ b/Telegram/SourceFiles/menu/menu_mute.h @@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -class PeerData; +namespace Data { +class Thread; +} // namespace Data namespace Ui { class PopupMenu; @@ -17,18 +19,15 @@ class Show; namespace MuteMenu { -struct Args { - not_null peer; - std::shared_ptr show; -}; - void FillMuteMenu( not_null menu, - Args args); + not_null thread, + std::shared_ptr show); void SetupMuteMenu( not_null parent, rpl::producer<> triggers, - Args args); + Fn makeThread, + std::shared_ptr show); } // namespace MuteMenu diff --git a/Telegram/SourceFiles/menu/menu_send.cpp b/Telegram/SourceFiles/menu/menu_send.cpp index 5c9240cee..707e33bca 100644 --- a/Telegram/SourceFiles/menu/menu_send.cpp +++ b/Telegram/SourceFiles/menu/menu_send.cpp @@ -202,7 +202,7 @@ void SetupUnreadMentionsMenu( done(); return; } - const auto peer = thread->owningHistory()->peer; + const auto peer = thread->peer(); const auto topic = thread->asTopic(); const auto rootId = topic ? topic->rootId() : 0; using Flag = MTPmessages_ReadMentions::Flag; @@ -244,7 +244,7 @@ void SetupUnreadReactionsMenu( return; } const auto topic = thread->asTopic(); - const auto peer = thread->owningHistory()->peer; + const auto peer = thread->peer(); const auto rootId = topic ? topic->rootId() : 0; using Flag = MTPmessages_ReadReactions::Flag; peer->session().api().request(MTPmessages_ReadReactions( diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index cfcd305fa..57f7616ec 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -165,7 +165,7 @@ System::SkipState System::skipNotification( const auto item = notification.item; const auto type = notification.type; const auto messageType = (type == Data::ItemNotificationType::Message); - if (!item->thread()->currentNotification() + if (!item->notificationThread()->currentNotification() || (messageType && item->skipNotification()) || (type == Data::ItemNotificationType::Reaction && skipReactionNotification(item))) { @@ -178,7 +178,8 @@ System::SkipState System::computeSkipState( Data::ItemNotification notification) const { const auto type = notification.type; const auto item = notification.item; - const auto history = item->history(); + const auto thread = item->notificationThread(); + const auto notifySettings = &thread->owner().notifySettings(); const auto messageType = (type == Data::ItemNotificationType::Message); const auto withSilent = [&]( SkipState::Value value, @@ -188,8 +189,7 @@ System::SkipState System::computeSkipState( .silent = (forceSilent || !messageType || item->isSilent() - || history->owner().notifySettings().sound( - history->peer).none), + || notifySettings->sound(thread).none), }; }; const auto showForMuted = messageType @@ -201,35 +201,32 @@ System::SkipState System::computeSkipState( if (Core::Quitting()) { return { SkipState::Skip }; } else if (!Core::App().settings().notifyFromAll() - && &history->session().account() != &Core::App().domain().active()) { + && &thread->session().account() != &Core::App().domain().active()) { return { SkipState::Skip }; } if (messageType) { - history->owner().notifySettings().request( - history->peer); + notifySettings->request(thread); } else if (notifyBy->blockStatus() == PeerData::BlockStatus::Unknown) { notifyBy->updateFull(); } if (notifyBy) { - history->owner().notifySettings().request(notifyBy); + notifySettings->request(notifyBy); } - if (messageType - && history->owner().notifySettings().muteUnknown(history->peer)) { + if (messageType && notifySettings->muteUnknown(thread)) { return { SkipState::Unknown }; - } else if (messageType - && !history->owner().notifySettings().isMuted(history->peer)) { + } else if (messageType && !notifySettings->isMuted(thread)) { return withSilent(SkipState::DontSkip); } else if (!notifyBy) { return withSilent( showForMuted ? SkipState::DontSkip : SkipState::Skip, showForMuted); - } else if (history->owner().notifySettings().muteUnknown(notifyBy) + } else if (notifySettings->muteUnknown(notifyBy) || (!messageType && notifyBy->blockStatus() == PeerData::BlockStatus::Unknown)) { return withSilent(SkipState::Unknown); - } else if (!history->owner().notifySettings().isMuted(notifyBy) + } else if (!notifySettings->isMuted(notifyBy) && (messageType || !notifyBy->isBlocked())) { return withSilent(SkipState::DontSkip); } else { @@ -240,13 +237,13 @@ System::SkipState System::computeSkipState( } System::Timing System::countTiming( - not_null history, + not_null thread, crl::time minimalDelay) const { auto delay = minimalDelay; const auto t = base::unixtime::now(); const auto ms = crl::now(); - const auto &updates = history->session().updates(); - const auto &config = history->session().serverConfig(); + const auto &updates = thread->session().updates(); + const auto &config = thread->session().serverConfig(); const bool isOnline = updates.lastWasOnline(); const auto otherNotOld = ((cOtherOnline() * 1000LL) + config.onlineCloudTimeout > t * 1000LL); const bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - updates.lastSetOnline()) > t * 1000LL); @@ -261,15 +258,26 @@ System::Timing System::countTiming( }; } +void System::registerThread(not_null thread) { + if (const auto topic = thread->asTopic()) { + const auto [i, ok] = _watchedTopics.emplace(topic, rpl::lifetime()); + if (ok) { + topic->destroyed() | rpl::start_with_next([=] { + clearFromTopic(topic); + }, i->second); + } + } +} + void System::schedule(Data::ItemNotification notification) { Expects(_manager != nullptr); const auto item = notification.item; const auto type = notification.type; - const auto history = item->history(); + const auto thread = item->notificationThread(); const auto skip = skipNotification(notification); if (skip.value == SkipState::Skip) { - history->popNotification(notification); + thread->popNotification(notification); return; } const auto ready = (skip.value != SkipState::Unknown) @@ -280,25 +288,27 @@ void System::schedule(Data::ItemNotification notification) { : item->Has() ? kMinimalForwardDelay : kMinimalDelay; - const auto timing = countTiming(history, minimalDelay); + const auto timing = countTiming(thread, minimalDelay); const auto notifyBy = (type == Data::ItemNotificationType::Message) ? item->specialNotificationPeer() : notification.reactionSender; if (!skip.silent) { - _whenAlerts[history].emplace(timing.when, notifyBy); + registerThread(thread); + _whenAlerts[thread].emplace(timing.when, notifyBy); } if (Core::App().settings().desktopNotify() && !_manager->skipToast()) { + registerThread(thread); const auto key = NotificationInHistoryKey(notification); - auto &whenMap = _whenMaps[history]; + auto &whenMap = _whenMaps[thread]; if (whenMap.find(key) == whenMap.end()) { whenMap.emplace(key, timing.when); } auto &addTo = ready ? _waiters : _settingWaiters; - const auto it = addTo.find(history); + const auto it = addTo.find(thread); if (it == addTo.end() || it->second.when > timing.when) { - addTo.emplace(history, Waiter{ + addTo.emplace(thread, Waiter{ .key = key, .reactionSender = notification.reactionSender, .type = notification.type, @@ -312,7 +322,6 @@ void System::schedule(Data::ItemNotification notification) { _waitTimer.callOnce(timing.delay); } } - } void System::clearAll() { @@ -327,6 +336,7 @@ void System::clearAll() { _whenAlerts.clear(); _waiters.clear(); _settingWaiters.clear(); + _watchedTopics.clear(); } void System::clearFromTopic(not_null topic) { @@ -340,6 +350,8 @@ void System::clearFromTopic(not_null topic) { _waiters.remove(topic); _settingWaiters.remove(topic); + _watchedTopics.remove(topic); + _waitTimer.cancel(); showNext(); } @@ -357,10 +369,17 @@ void System::clearForThreadIf(Fn)> predicate) { _whenAlerts.remove(thread); _waiters.remove(thread); _settingWaiters.remove(thread); + if (const auto topic = thread->asTopic()) { + _watchedTopics.remove(topic); + } } const auto clearFrom = [&](auto &map) { for (auto i = map.begin(); i != map.end();) { - if (predicate(i->first)) { + const auto thread = i->first; + if (predicate(thread)) { + if (const auto topic = thread->asTopic()) { + _watchedTopics.remove(topic); + } i = map.erase(i); } else { ++i; @@ -424,13 +443,14 @@ void System::clearAllFast() { _whenAlerts.clear(); _waiters.clear(); _settingWaiters.clear(); + _watchedTopics.clear(); } void System::checkDelayed() { for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) { const auto remove = [&] { const auto thread = i->first; - const auto peer = thread->owningHistory()->peer; + const auto peer = thread->peer(); const auto fullId = FullMsgId(peer->id, i->second.key.messageId); const auto item = thread->owner().message(fullId); if (!item) { @@ -501,21 +521,21 @@ void System::showNext() { }; auto ms = crl::now(), nextAlert = crl::time(0); - auto alertPeer = (PeerData*)nullptr; + auto alertThread = (Data::Thread*)nullptr; for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) { while (!i->second.empty() && i->second.begin()->first <= ms) { - const auto peer = i->first->owningHistory()->peer; - const auto ¬ifySettings = peer->owner().notifySettings(); - const auto peerUnknown = notifySettings.muteUnknown(peer); - const auto peerAlert = !peerUnknown - && !notifySettings.isMuted(peer); + const auto thread = i->first; + const auto notifySettings = &thread->owner().notifySettings(); + const auto threadUnknown = notifySettings->muteUnknown(thread); + const auto threadAlert = !threadUnknown + && !notifySettings->isMuted(thread); const auto from = i->second.begin()->second; const auto fromUnknown = (!from - || notifySettings.muteUnknown(from)); + || notifySettings->muteUnknown(from)); const auto fromAlert = !fromUnknown - && !notifySettings.isMuted(from); - if (peerAlert || fromAlert) { - alertPeer = peer; + && !notifySettings->isMuted(from); + if (threadAlert || fromAlert) { + alertThread = thread; } while (!i->second.empty() && i->second.begin()->first <= ms + kMinimalAlertDelay) { @@ -532,7 +552,7 @@ void System::showNext() { } } const auto &settings = Core::App().settings(); - if (alertPeer) { + if (alertThread) { if (settings.flashBounceNotify() && !_manager->skipFlashBounce()) { if (const auto window = Core::App().primaryWindow()) { if (const auto handle = window->widget()->windowHandle()) { @@ -543,8 +563,8 @@ void System::showNext() { } if (settings.soundNotify() && !_manager->skipAudio()) { const auto track = lookupSound( - &alertPeer->owner(), - alertPeer->owner().notifySettings().sound(alertPeer).id); + &alertThread->owner(), + alertThread->owner().notifySettings().sound(alertThread).id); track->playOnce(); Media::Player::mixer()->suppressAll(track->getLengthMs()); Media::Player::mixer()->faderOnTimer(); @@ -620,7 +640,7 @@ void System::showNext() { : nullptr; auto forwardedCount = isForwarded ? 1 : 0; - const auto thread = notifyItem->thread(); + const auto thread = notifyItem->notificationThread(); const auto j = _whenMaps.find(thread); if (j == _whenMaps.cend()) { thread->clearNotifications(); @@ -997,7 +1017,7 @@ void Manager::openNotificationMessage( && item && item->isRegular() && (item->out() || (item->mentionsMe() && !history->peer->isUser())); - const auto topic = item ? history->peer->forumTopicFor(item) : nullptr; + const auto topic = item ? item->topic() : nullptr; const auto separate = Core::App().separateWindowForPeer(history->peer); const auto window = separate ? separate->sessionController() diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index ec353451a..6a1dbdeb0 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -155,7 +155,7 @@ private: [[nodiscard]] SkipState computeSkipState( Data::ItemNotification notification) const; [[nodiscard]] Timing countTiming( - not_null history, + not_null thread, crl::time minimalDelay) const; [[nodiscard]] bool skipReactionNotification( not_null item) const; @@ -167,6 +167,8 @@ private: not_null owner, DocumentId id); + void registerThread(not_null thread); + base::flat_map< not_null, base::flat_map> _whenMaps; @@ -193,6 +195,10 @@ private: DocumentId, std::unique_ptr> _customSoundTracks; + base::flat_map< + not_null, + rpl::lifetime> _watchedTopics; + int _lastForwardedCount = 0; uint64 _lastHistorySessionId = 0; FullMsgId _lastHistoryItemId; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 70637fd6d..61061e111 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "boxes/delete_messages_box.h" #include "boxes/max_invite_box.h" -#include "boxes/mute_settings_box.h" #include "boxes/add_contact_box.h" #include "boxes/choose_filter_box.h" #include "boxes/create_poll_box.h" @@ -134,28 +133,39 @@ void MarkAsReadChatList(not_null list) { void PeerMenuAddMuteSubmenuAction( not_null controller, - not_null peer, + not_null thread, const PeerMenuCallback &addAction) { - peer->owner().notifySettings().request(peer); - const auto isMuted = peer->owner().notifySettings().isMuted(peer); + const auto notifySettings = &thread->owner().notifySettings(); + notifySettings->request(thread); + const auto weak = base::make_weak(thread.get()); + const auto with = [=](Fn)> callback) { + return [=] { + if (const auto strong = weak.get()) { + callback(strong); + } + }; + }; + const auto isMuted = notifySettings->isMuted(thread); if (isMuted) { const auto text = tr::lng_context_unmute(tr::now) + '\t' - + Ui::FormatMuteForTiny(peer->notify().muteUntil().value_or(0) + + Ui::FormatMuteForTiny(thread->notify().muteUntil().value_or(0) - base::unixtime::now()); - addAction(text, [=] { - peer->owner().notifySettings().update(peer, { .unmute = true }); - }, &st::menuIconUnmute); + addAction(text, with([=](not_null thread) { + notifySettings->update(thread, { .unmute = true }); + }), &st::menuIconUnmute); } else { const auto show = std::make_shared(controller); addAction(PeerMenuCallback::Args{ .text = tr::lng_context_mute(tr::now), .handler = nullptr, - .icon = peer->owner().notifySettings().sound(peer).none + .icon = (notifySettings->sound(thread).none ? &st::menuIconSilent - : &st::menuIconMute, + : &st::menuIconMute), .fillSubmenu = [=](not_null menu) { - MuteMenu::FillMuteMenu(menu, { peer, show }); + if (const auto strong = weak.get()) { + MuteMenu::FillMuteMenu(menu, strong, show); + } }, }); } @@ -209,8 +219,8 @@ private: not_null _controller; Dialogs::EntryState _request; + Data::Thread *_thread = nullptr; PeerData *_peer = nullptr; - Data::ForumTopic *_topic = nullptr; Data::Folder *_folder = nullptr; const PeerMenuCallback &_addAction; @@ -329,8 +339,8 @@ Filler::Filler( const PeerMenuCallback &addAction) : _controller(controller) , _request(request) +, _thread(request.key.thread()) , _peer(request.key.peer()) -, _topic(request.key.topic()) , _folder(request.key.folder()) , _addAction(addAction) { } @@ -385,10 +395,10 @@ void Filler::addTogglePin() { } void Filler::addToggleMuteSubmenu(bool addSeparator) { - if (_peer->isSelf()) { + if (_thread->peer()->isSelf()) { return; } - PeerMenuAddMuteSubmenuAction(_controller, _peer, _addAction); + PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction); if (addSeparator) { _addAction(PeerMenuCallback::Args{ .isSeparator = true }); } @@ -412,26 +422,30 @@ void Filler::addInfo() { if (_peer && (_peer->isSelf() || _peer->isRepliesChat())) { return; } else if (_controller->adaptive().isThreeColumn()) { - const auto peer = _controller->activeChatCurrent().peer(); - const auto topic = _controller->activeChatCurrent().topic(); - if ((peer && peer == _peer) || (topic && topic == _topic)) { + const auto thread = _controller->activeChatCurrent().thread(); + if (thread && thread == _thread) { if (Core::App().settings().thirdSectionInfoEnabled() || Core::App().settings().tabbedReplacedWithInfo()) { return; } } + } else if (!_thread) { + return; } const auto controller = _controller; - const auto id = _topic ? _topic->rootId() : 0; - const auto peer = _peer; - const auto text = (peer->isChat() || peer->isMegagroup()) + const auto weak = base::make_weak(_thread); + const auto text = _thread->asTopic() + ? u"View Topic Info"_q // #TODO lang-forum + : (_peer->isChat() || _peer->isMegagroup()) ? tr::lng_context_view_group(tr::now) - : (peer->isUser() - ? tr::lng_context_view_profile(tr::now) - : tr::lng_context_view_channel(tr::now)); + : _peer->isUser() + ? tr::lng_context_view_profile(tr::now) + : tr::lng_context_view_channel(tr::now); _addAction(text, [=] { - controller->showPeerInfo(peer); - }, peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo); + if (const auto strong = weak.get()) { + controller->showPeerInfo(strong); + } + }, _peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo); } void Filler::addToggleFolder() { @@ -547,7 +561,7 @@ void Filler::addDeleteChat() { void Filler::addLeaveChat() { const auto channel = _peer->asChannel(); - if (_topic || !channel || !channel->amIn()) { + if (_thread->asTopic() || !channel || !channel->amIn()) { return; } _addAction({ @@ -632,7 +646,7 @@ void Filler::addViewDiscussion() { } void Filler::addExportChat() { - if (_topic || !_peer->canExportChatHistory()) { + if (_thread->asTopic() || !_peer->canExportChatHistory()) { return; } const auto peer = _peer; @@ -748,15 +762,17 @@ void Filler::addDeleteContact() { } void Filler::addManageTopic() { - if (!_topic) { + const auto topic = _thread->asTopic(); + if (!topic) { return; } // #TODO lang-forum - const auto history = _topic->history(); - const auto rootId = _topic->rootId(); + const auto history = topic->history(); + const auto rootId = topic->rootId(); const auto navigation = _controller; _addAction(u"Edit topic"_q, [=] { - navigation->show(Box(EditForumTopicBox, navigation, history, rootId)); + navigation->show( + Box(EditForumTopicBox, navigation, history, rootId)); }, &st::menuIconEdit); } @@ -821,7 +837,7 @@ void Filler::addThemeEdit() { } void Filler::addTTLSubmenu(bool addSeparator) { - if (_topic) { + if (_thread->asTopic()) { return; // #TODO later forum } const auto validator = TTLMenu::TTLValidator( @@ -928,7 +944,7 @@ void Filler::fillProfileActions() { } void Filler::fillRepliesActions() { - if (_topic) { + if (_thread->asTopic()) { addInfo(); addManageTopic(); addManageChat(); @@ -1498,35 +1514,6 @@ void UnpinAllMessages( Ui::LayerOption::CloseOther); } -void PeerMenuAddMuteAction( - not_null controller, - not_null peer, - const PeerMenuCallback &addAction) { - // There is no async to make weak from controller. - peer->owner().notifySettings().request(peer); - const auto muteText = [](bool isUnmuted) { - return isUnmuted - ? tr::lng_context_mute(tr::now) - : tr::lng_context_unmute(tr::now); - }; - const auto muteAction = addAction(QString("-"), [=] { - if (!peer->owner().notifySettings().isMuted(peer)) { - controller->show( - Box(peer), - Ui::LayerOption::CloseOther); - } else { - peer->owner().notifySettings().update(peer, { .unmute = true }); - } - }, (peer->owner().notifySettings().isMuted(peer) - ? &st::menuIconUnmute - : &st::menuIconMute)); - - auto actionText = Info::Profile::NotificationsEnabledValue( - peer - ) | rpl::map(muteText); - SetActionText(muteAction, std::move(actionText)); -} - void MenuAddMarkAsReadAllChatsAction( not_null controller, const PeerMenuCallback &addAction) { diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index 62afac6ef..b314082a1 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -46,11 +46,6 @@ void FillDialogsEntryMenu( Dialogs::EntryState request, const PeerMenuCallback &addAction); -void PeerMenuAddMuteAction( - not_null controller, - not_null peer, - const PeerMenuCallback &addAction); - void MenuAddMarkAsReadAllChatsAction( not_null controller, const PeerMenuCallback &addAction); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 7f05f1041..287d8aa17 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -567,9 +567,13 @@ void SessionNavigation::showPeerInfo( } void SessionNavigation::showPeerInfo( - not_null history, + not_null thread, const SectionShow ¶ms) { - showPeerInfo(history->peer->id, params); + if (const auto topic = thread->asTopic()) { + showSection(std::make_shared(topic), params); + } else { + showPeerInfo(thread->peer()->id, params); + } } void SessionNavigation::showPeerHistory( diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index efec0dc0c..02a74419b 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -69,6 +69,7 @@ class MessageSendingAnimationController; namespace Data { struct CloudTheme; enum class CloudThemeType; +class Thread; } // namespace Data namespace HistoryView::Reactions { @@ -219,7 +220,7 @@ public: not_null peer, const SectionShow ¶ms = SectionShow()); void showPeerInfo( - not_null history, + not_null thread, const SectionShow ¶ms = SectionShow()); virtual void showPeerHistory(