Extract Data::Thread common for History / ForumTopic.

This commit is contained in:
John Preston 2022-10-13 14:32:03 +04:00
parent 9fccdf21cc
commit b8bdca8921
38 changed files with 675 additions and 627 deletions

View file

@ -24,39 +24,27 @@ constexpr auto kPreloadIfLess = 5;
constexpr auto kFirstRequestLimit = 10; constexpr auto kFirstRequestLimit = 10;
constexpr auto kNextRequestLimit = 100; constexpr auto kNextRequestLimit = 100;
[[nodiscard]] not_null<History*> ResolveHistory(
not_null<Dialogs::Entry*> entry) {
if (const auto history = entry->asHistory()) {
return history;
}
const auto topic = entry->asTopic();
Ensures(topic != nullptr);
return topic->history();
}
} // namespace } // namespace
UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) { UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
} }
bool UnreadThings::trackMentions(DialogsEntry *entry) const { bool UnreadThings::trackMentions(Data::Thread *thread) const {
const auto peer = entry ? ResolveHistory(entry)->peer.get() : nullptr; const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr;
return peer && (peer->isChat() || peer->isMegagroup()); return peer && (peer->isChat() || peer->isMegagroup());
} }
bool UnreadThings::trackReactions(DialogsEntry *entry) const { bool UnreadThings::trackReactions(Data::Thread *thread) const {
const auto peer = entry ? ResolveHistory(entry)->peer.get() : nullptr; const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr;
return peer && (peer->isChat() || peer->isMegagroup()); return peer && (peer->isChat() || peer->isMegagroup());
} }
void UnreadThings::preloadEnough(DialogsEntry *entry) { void UnreadThings::preloadEnough(Data::Thread *thread) {
if (trackMentions(entry)) { if (trackMentions(thread)) {
preloadEnoughMentions(entry); preloadEnoughMentions(thread);
} }
if (trackReactions(entry)) { if (trackReactions(thread)) {
preloadEnoughReactions(entry); preloadEnoughReactions(thread);
} }
} }
@ -75,48 +63,48 @@ void UnreadThings::mediaAndMentionsRead(
} }
} }
void UnreadThings::preloadEnoughMentions(not_null<DialogsEntry*> entry) { void UnreadThings::preloadEnoughMentions(not_null<Data::Thread*> thread) {
const auto fullCount = entry->unreadMentions().count(); const auto fullCount = thread->unreadMentions().count();
const auto loadedCount = entry->unreadMentions().loadedCount(); const auto loadedCount = thread->unreadMentions().loadedCount();
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount); const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) { if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
requestMentions(entry, loadedCount); requestMentions(thread, loadedCount);
} }
} }
void UnreadThings::preloadEnoughReactions(not_null<DialogsEntry*> entry) { void UnreadThings::preloadEnoughReactions(not_null<Data::Thread*> thread) {
const auto fullCount = entry->unreadReactions().count(); const auto fullCount = thread->unreadReactions().count();
const auto loadedCount = entry->unreadReactions().loadedCount(); const auto loadedCount = thread->unreadReactions().loadedCount();
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount); const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) { if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
requestReactions(entry, loadedCount); requestReactions(thread, loadedCount);
} }
} }
void UnreadThings::cancelRequests(not_null<DialogsEntry*> entry) { void UnreadThings::cancelRequests(not_null<Data::Thread*> thread) {
if (const auto requestId = _mentionsRequests.take(entry)) { if (const auto requestId = _mentionsRequests.take(thread)) {
_api->request(*requestId).cancel(); _api->request(*requestId).cancel();
} }
if (const auto requestId = _reactionsRequests.take(entry)) { if (const auto requestId = _reactionsRequests.take(thread)) {
_api->request(*requestId).cancel(); _api->request(*requestId).cancel();
} }
} }
void UnreadThings::requestMentions( void UnreadThings::requestMentions(
not_null<DialogsEntry*> entry, not_null<Data::Thread*> thread,
int loaded) { int loaded) {
if (_mentionsRequests.contains(entry)) { if (_mentionsRequests.contains(thread)) {
return; return;
} }
const auto offsetId = std::max( const auto offsetId = std::max(
entry->unreadMentions().maxLoaded(), thread->unreadMentions().maxLoaded(),
MsgId(1)); MsgId(1));
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit; const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
const auto addOffset = loaded ? -(limit + 1) : -limit; const auto addOffset = loaded ? -(limit + 1) : -limit;
const auto maxId = 0; const auto maxId = 0;
const auto minId = 0; const auto minId = 0;
const auto history = ResolveHistory(entry); const auto history = thread->owningHistory();
const auto topic = entry->asTopic(); const auto topic = thread->asTopic();
using Flag = MTPmessages_GetUnreadMentions::Flag; using Flag = MTPmessages_GetUnreadMentions::Flag;
const auto requestId = _api->request(MTPmessages_GetUnreadMentions( const auto requestId = _api->request(MTPmessages_GetUnreadMentions(
MTP_flags(topic ? Flag::f_top_msg_id : Flag()), MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
@ -128,29 +116,29 @@ void UnreadThings::requestMentions(
MTP_int(maxId), MTP_int(maxId),
MTP_int(minId) MTP_int(minId)
)).done([=](const MTPmessages_Messages &result) { )).done([=](const MTPmessages_Messages &result) {
_mentionsRequests.remove(entry); _mentionsRequests.remove(thread);
entry->unreadMentions().addSlice(result, loaded); thread->unreadMentions().addSlice(result, loaded);
}).fail([=] { }).fail([=] {
_mentionsRequests.remove(entry); _mentionsRequests.remove(thread);
}).send(); }).send();
_mentionsRequests.emplace(entry, requestId); _mentionsRequests.emplace(thread, requestId);
} }
void UnreadThings::requestReactions( void UnreadThings::requestReactions(
not_null<DialogsEntry*> entry, not_null<Data::Thread*> thread,
int loaded) { int loaded) {
if (_reactionsRequests.contains(entry)) { if (_reactionsRequests.contains(thread)) {
return; return;
} }
const auto offsetId = loaded const auto offsetId = loaded
? std::max(entry->unreadReactions().maxLoaded(), MsgId(1)) ? std::max(thread->unreadReactions().maxLoaded(), MsgId(1))
: MsgId(1); : MsgId(1);
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit; const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
const auto addOffset = loaded ? -(limit + 1) : -limit; const auto addOffset = loaded ? -(limit + 1) : -limit;
const auto maxId = 0; const auto maxId = 0;
const auto minId = 0; const auto minId = 0;
const auto history = ResolveHistory(entry); const auto history = thread->owningHistory();
const auto topic = entry->asTopic(); const auto topic = thread->asTopic();
using Flag = MTPmessages_GetUnreadReactions::Flag; using Flag = MTPmessages_GetUnreadReactions::Flag;
const auto requestId = _api->request(MTPmessages_GetUnreadReactions( const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
MTP_flags(topic ? Flag::f_top_msg_id : Flag()), MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
@ -162,12 +150,12 @@ void UnreadThings::requestReactions(
MTP_int(maxId), MTP_int(maxId),
MTP_int(minId) MTP_int(minId)
)).done([=](const MTPmessages_Messages &result) { )).done([=](const MTPmessages_Messages &result) {
_reactionsRequests.remove(entry); _reactionsRequests.remove(thread);
entry->unreadReactions().addSlice(result, loaded); thread->unreadReactions().addSlice(result, loaded);
}).fail([=] { }).fail([=] {
_reactionsRequests.remove(entry); _reactionsRequests.remove(thread);
}).send(); }).send();
_reactionsRequests.emplace(entry, requestId); _reactionsRequests.emplace(thread, requestId);
} }
} // namespace UnreadThings } // namespace UnreadThings

View file

@ -11,40 +11,38 @@ class ApiWrap;
class PeerData; class PeerData;
class ChannelData; class ChannelData;
namespace Dialogs { namespace Data {
class Entry; class Thread;
} // namespace Dialogs } // namespace Data
namespace Api { namespace Api {
class UnreadThings final { class UnreadThings final {
public: public:
using DialogsEntry = Dialogs::Entry;
explicit UnreadThings(not_null<ApiWrap*> api); explicit UnreadThings(not_null<ApiWrap*> api);
[[nodiscard]] bool trackMentions(DialogsEntry *entry) const; [[nodiscard]] bool trackMentions(Data::Thread *thread) const;
[[nodiscard]] bool trackReactions(DialogsEntry *entry) const; [[nodiscard]] bool trackReactions(Data::Thread *thread) const;
void preloadEnough(DialogsEntry *entry); void preloadEnough(Data::Thread *thread);
void mediaAndMentionsRead( void mediaAndMentionsRead(
const base::flat_set<MsgId> &readIds, const base::flat_set<MsgId> &readIds,
ChannelData *channel = nullptr); ChannelData *channel = nullptr);
void cancelRequests(not_null<DialogsEntry*> entry); void cancelRequests(not_null<Data::Thread*> thread);
private: private:
void preloadEnoughMentions(not_null<DialogsEntry*> entry); void preloadEnoughMentions(not_null<Data::Thread*> thread);
void preloadEnoughReactions(not_null<DialogsEntry*> entry); void preloadEnoughReactions(not_null<Data::Thread*> thread);
void requestMentions(not_null<DialogsEntry*> entry, int loaded); void requestMentions(not_null<Data::Thread*> thread, int loaded);
void requestReactions(not_null<DialogsEntry*> entry, int loaded); void requestReactions(not_null<Data::Thread*> thread, int loaded);
const not_null<ApiWrap*> _api; const not_null<ApiWrap*> _api;
base::flat_map<not_null<DialogsEntry*>, mtpRequestId> _mentionsRequests; base::flat_map<not_null<Data::Thread*>, mtpRequestId> _mentionsRequests;
base::flat_map<not_null<DialogsEntry*>, mtpRequestId> _reactionsRequests; base::flat_map<not_null<Data::Thread*>, mtpRequestId> _reactionsRequests;
}; };

View file

@ -130,7 +130,7 @@ void Forum::applyReceivedTopics(
const auto raw = creating const auto raw = creating
? _topics.emplace( ? _topics.emplace(
rootId, rootId,
std::make_unique<ForumTopic>(_history, rootId) std::make_unique<ForumTopic>(this, rootId)
).first->second.get() ).first->second.get()
: i->second.get(); : i->second.get();
raw->applyTopic(topic); raw->applyTopic(topic);
@ -194,7 +194,7 @@ void Forum::applyTopicAdded(
? i->second.get() ? i->second.get()
: _topics.emplace( : _topics.emplace(
rootId, rootId,
std::make_unique<ForumTopic>(_history, rootId) std::make_unique<ForumTopic>(this, rootId)
).first->second.get(); ).first->second.get();
raw->applyTitle(title); raw->applyTitle(title);
raw->applyColorId(colorId); raw->applyColorId(colorId);
@ -262,11 +262,13 @@ void Forum::clearAllUnreadReactions() {
} }
ForumTopic *Forum::topicFor(not_null<const HistoryItem*> item) { ForumTopic *Forum::topicFor(not_null<const HistoryItem*> item) {
const auto maybe = topicFor(item->replyToTop()); return topicFor(item->topicRootId());
return maybe ? maybe : topicFor(item->topicRootId());
} }
ForumTopic *Forum::topicFor(MsgId rootId) { ForumTopic *Forum::topicFor(MsgId rootId) {
if (!rootId) {
return nullptr;
}
const auto i = _topics.find(rootId); const auto i = _topics.find(rootId);
return (i != end(_topics)) ? i->second.get() : nullptr; return (i != end(_topics)) ? i->second.get() : nullptr;
} }

View file

@ -137,13 +137,14 @@ QImage ForumTopicIconFrame(
return background; return background;
} }
ForumTopic::ForumTopic(not_null<History*> history, MsgId rootId) ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
: Entry(&history->owner(), Type::ForumTopic) : Thread(&forum->history()->owner(), Type::ForumTopic)
, _forum(history->peer->forum()) , _forum(forum)
, _list(_forum->topicsList()) , _list(_forum->topicsList())
, _replies(std::make_shared<RepliesList>(history, rootId)) , _replies(std::make_shared<RepliesList>(history(), rootId))
, _rootId(rootId) , _rootId(rootId) {
, _flags(owner().notifySettings().isMuted(this) ? Flag::Muted : Flag(0)) { Thread::setMuted(owner().notifySettings().isMuted(this));
_replies->unreadCountValue( _replies->unreadCountValue(
) | rpl::combine_previous( ) | rpl::combine_previous(
) | rpl::filter([=] { ) | rpl::filter([=] {
@ -486,13 +487,9 @@ int ForumTopic::unreadCountForBadge() const {
return (!result && unreadMark()) ? 1 : result; return (!result && unreadMark()) ? 1 : result;
} }
bool ForumTopic::muted() const { void ForumTopic::setMuted(bool muted) {
return (_flags & Flag::Muted);
}
bool ForumTopic::changeMuted(bool muted) {
if (this->muted() == muted) { if (this->muted() == muted) {
return false; return;
} }
const auto refresher = gsl::finally([&] { const auto refresher = gsl::finally([&] {
if (inChatList()) { if (inChatList()) {
@ -504,12 +501,7 @@ bool ForumTopic::changeMuted(bool muted) {
}); });
const auto notify = (unreadCountForBadge() > 0); const auto notify = (unreadCountForBadge() > 0);
const auto notifier = unreadStateChangeNotifier(notify); const auto notifier = unreadStateChangeNotifier(notify);
if (muted) { Thread::setMuted(muted);
_flags |= Flag::Muted;
} else {
_flags &= ~Flag::Muted;
}
return true;
} }
bool ForumTopic::unreadCountKnown() const { bool ForumTopic::unreadCountKnown() const {
@ -528,15 +520,7 @@ void ForumTopic::setUnreadMark(bool unread) {
session().changes().topicUpdated(this, UpdateFlag::UnreadView); session().changes().topicUpdated(this, UpdateFlag::UnreadView);
}); });
const auto notifier = unreadStateChangeNotifier(noUnreadMessages); const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
if (unread) { Thread::setUnreadMark(unread);
_flags |= Flag::UnreadMark;
} else {
_flags &= ~Flag::UnreadMark;
}
}
bool ForumTopic::unreadMark() const {
return (_flags & Flag::UnreadMark);
} }
int ForumTopic::chatListUnreadCount() const { int ForumTopic::chatListUnreadCount() const {

View file

@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "dialogs/dialogs_entry.h" #include "data/data_thread.h"
#include "dialogs/ui/dialogs_message_view.h"
#include "data/notify/data_peer_notify_settings.h" #include "data/notify/data_peer_notify_settings.h"
#include "base/flags.h" #include "base/flags.h"
@ -42,13 +41,14 @@ class Forum;
const QString &title, const QString &title,
const style::ForumTopicIcon &st); const style::ForumTopicIcon &st);
class ForumTopic final : public Dialogs::Entry { class ForumTopic final : public Data::Thread {
public: public:
ForumTopic(not_null<History*> history, MsgId rootId); ForumTopic(not_null<Forum*> forum, MsgId rootId);
~ForumTopic(); ~ForumTopic();
ForumTopic(const ForumTopic &) = delete; not_null<History*> owningHistory() override {
ForumTopic &operator=(const ForumTopic &) = delete; return history();
}
[[nodiscard]] std::shared_ptr<RepliesList> replies() const; [[nodiscard]] std::shared_ptr<RepliesList> replies() const;
[[nodiscard]] not_null<ChannelData*> channel() const; [[nodiscard]] not_null<ChannelData*> channel() const;
@ -108,22 +108,11 @@ public:
[[nodiscard]] bool unreadCountKnown() const; [[nodiscard]] bool unreadCountKnown() const;
[[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0. [[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
[[nodiscard]] bool muted() const;
bool changeMuted(bool muted);
void setUnreadMark(bool unread); void setMuted(bool muted) override;
[[nodiscard]] bool unreadMark() const; void setUnreadMark(bool unread) override;
Ui::Text::String cloudDraftTextCache;
Dialogs::Ui::MessageView lastItemDialogsView;
private: private:
enum class Flag : uchar {
UnreadMark = (1 << 0),
Muted = (1 << 1),
};
friend inline constexpr bool is_flag_type(Flag) { return true; }
void indexTitleParts(); void indexTitleParts();
void validateDefaultIcon() const; void validateDefaultIcon() const;
void applyTopicTopMessage(MsgId topMessageId); void applyTopicTopMessage(MsgId topMessageId);
@ -159,7 +148,6 @@ private:
std::optional<HistoryItem*> _lastServerMessage; std::optional<HistoryItem*> _lastServerMessage;
std::optional<HistoryItem*> _chatListMessage; std::optional<HistoryItem*> _chatListMessage;
base::flat_set<FullMsgId> _requestedGroups; base::flat_set<FullMsgId> _requestedGroups;
base::flags<Flag> _flags; // Initializer accesses _notify, be careful.
rpl::lifetime _lifetime; rpl::lifetime _lifetime;

View file

@ -901,14 +901,18 @@ Data::Forum *PeerData::forum() const {
Data::ForumTopic *PeerData::forumTopicFor( Data::ForumTopic *PeerData::forumTopicFor(
not_null<const HistoryItem*> item) const { not_null<const HistoryItem*> item) const {
if (const auto forum = this->forum()) { if (const auto rootId = item->topicRootId()) {
return forum->topicFor(item); if (const auto forum = this->forum()) {
return forum->topicFor(rootId);
}
} }
return nullptr; return nullptr;
} }
Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const { Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const {
if (const auto forum = this->forum()) { if (!rootId) {
return nullptr;
} else if (const auto forum = this->forum()) {
return forum->topicFor(rootId); return forum->topicFor(rootId);
} }
return nullptr; return nullptr;

View file

@ -1496,9 +1496,14 @@ void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
} }
} }
const auto history = item->history(); const auto history = item->history();
if (history->lastItemDialogsView.dependsOn(item)) { if (history->lastItemDialogsView().dependsOn(item)) {
history->updateChatListEntry(); history->updateChatListEntry();
} }
if (const auto topic = item->topic()) {
if (topic->lastItemDialogsView().dependsOn(item)) {
topic->updateChatListEntry();
}
}
} }
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const { rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {

View file

@ -0,0 +1,133 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_thread.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_unread_things.h"
namespace Data {
Thread::~Thread() = default;
void Thread::setUnreadThingsKnown() {
_flags |= Flag::UnreadThingsKnown;
}
HistoryUnreadThings::Proxy Thread::unreadMentions() {
return {
this,
_unreadThings,
HistoryUnreadThings::Type::Mentions,
!!(_flags & Flag::UnreadThingsKnown),
};
}
HistoryUnreadThings::ConstProxy Thread::unreadMentions() const {
return {
_unreadThings ? &_unreadThings->mentions : nullptr,
!!(_flags & Flag::UnreadThingsKnown),
};
}
HistoryUnreadThings::Proxy Thread::unreadReactions() {
return {
this,
_unreadThings,
HistoryUnreadThings::Type::Reactions,
!!(_flags & Flag::UnreadThingsKnown),
};
}
HistoryUnreadThings::ConstProxy Thread::unreadReactions() const {
return {
_unreadThings ? &_unreadThings->reactions : nullptr,
!!(_flags & Flag::UnreadThingsKnown),
};
}
const base::flat_set<MsgId> &Thread::unreadMentionsIds() const {
if (!_unreadThings) {
static const auto Result = base::flat_set<MsgId>();
return Result;
}
return _unreadThings->mentions.ids();
}
const base::flat_set<MsgId> &Thread::unreadReactionsIds() const {
if (!_unreadThings) {
static const auto Result = base::flat_set<MsgId>();
return Result;
}
return _unreadThings->reactions.ids();
}
void Thread::clearNotifications() {
_notifications.clear();
}
void Thread::clearIncomingNotifications() {
if (!owningHistory()->peer->isSelf()) {
const auto proj = [](ItemNotification notification) {
return notification.item->out();
};
_notifications.erase(
ranges::remove(_notifications, false, proj),
end(_notifications));
}
}
void Thread::removeNotification(not_null<HistoryItem*> item) {
_notifications.erase(
ranges::remove(_notifications, item, &ItemNotification::item),
end(_notifications));
}
std::optional<ItemNotification> Thread::currentNotification() const {
return empty(_notifications)
? std::nullopt
: std::make_optional(_notifications.front());
}
bool Thread::hasNotification() const {
return !empty(_notifications);
}
void Thread::skipNotification() {
if (!empty(_notifications)) {
_notifications.pop_front();
}
}
void Thread::pushNotification(ItemNotification notification) {
_notifications.push_back(notification);
}
void Thread::popNotification(ItemNotification notification) {
if (!empty(_notifications) && (_notifications.back() == notification)) {
_notifications.pop_back();
}
}
void Thread::setMuted(bool muted) {
if (muted) {
_flags |= Flag::Muted;
} else {
_flags &= ~Flag::Muted;
}
}
void Thread::setUnreadMark(bool unread) {
if (unread) {
_flags |= Flag::UnreadMark;
} else {
_flags &= ~Flag::UnreadMark;
}
}
} // namespace Data

View file

@ -0,0 +1,112 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "dialogs/dialogs_entry.h"
#include "dialogs/ui/dialogs_message_view.h"
#include "ui/text/text.h"
#include <deque>
namespace Main {
class Session;
} // namespace Main
namespace HistoryUnreadThings {
enum class AddType;
struct All;
class Proxy;
class ConstProxy;
} // namespace HistoryUnreadThings
namespace st {
extern const int &dialogsTextWidthMin;
} // namespace st
namespace Data {
enum class ItemNotificationType {
Message,
Reaction,
};
struct ItemNotification {
not_null<HistoryItem*> item;
UserData *reactionSender = nullptr;
ItemNotificationType type = ItemNotificationType::Message;
friend inline auto operator<=>(
ItemNotification a,
ItemNotification b) = default;
};
class Thread : public Dialogs::Entry {
public:
using Entry::Entry;
~Thread();
[[nodiscard]] virtual not_null<History*> owningHistory() = 0;
[[nodiscard]] not_null<const History*> owningHistory() const {
return const_cast<Thread*>(this)->owningHistory();
}
void setUnreadThingsKnown();
[[nodiscard]] HistoryUnreadThings::Proxy unreadMentions();
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadMentions() const;
[[nodiscard]] HistoryUnreadThings::Proxy unreadReactions();
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const;
void removeNotification(not_null<HistoryItem*> item);
void clearNotifications();
void clearIncomingNotifications();
[[nodiscard]] auto currentNotification() const
-> std::optional<ItemNotification>;
bool hasNotification() const;
void skipNotification();
void pushNotification(ItemNotification notification);
void popNotification(ItemNotification notification);
[[nodiscard]] bool muted() const {
return (_flags & Flag::Muted);
}
virtual void setMuted(bool muted);
[[nodiscard]] bool unreadMark() const {
return (_flags & Flag::UnreadMark);
}
virtual void setUnreadMark(bool unread);
[[nodiscard]] const base::flat_set<MsgId> &unreadMentionsIds() const;
[[nodiscard]] const base::flat_set<MsgId> &unreadReactionsIds() const;
[[nodiscard]] Ui::Text::String &cloudDraftTextCache() {
return _cloudDraftTextCache;
}
[[nodiscard]] Dialogs::Ui::MessageView &lastItemDialogsView() {
return _lastItemDialogsView;
}
private:
enum class Flag : uchar {
UnreadMark = (1 << 0),
Muted = (1 << 1),
UnreadThingsKnown = (1 << 2),
};
friend inline constexpr bool is_flag_type(Flag) { return true; }
Ui::Text::String _cloudDraftTextCache = { st::dialogsTextWidthMin };
Dialogs::Ui::MessageView _lastItemDialogsView;
std::unique_ptr<HistoryUnreadThings::All> _unreadThings;
std::deque<ItemNotification> _notifications;
base::flags<Flag> _flags;
};
} // namespace Data

View file

@ -189,7 +189,7 @@ void NotifySettings::defaultUpdate(
void NotifySettings::updateLocal(not_null<Data::ForumTopic*> topic) { void NotifySettings::updateLocal(not_null<Data::ForumTopic*> topic) {
auto changesIn = crl::time(0); auto changesIn = crl::time(0);
const auto muted = isMuted(topic, &changesIn); const auto muted = isMuted(topic, &changesIn);
topic->changeMuted(muted); topic->setMuted(muted);
if (muted) { if (muted) {
auto &lifetime = _mutedTopics.emplace( auto &lifetime = _mutedTopics.emplace(
topic, topic,
@ -209,7 +209,9 @@ void NotifySettings::updateLocal(not_null<PeerData*> peer) {
const auto history = _owner->historyLoaded(peer->id); const auto history = _owner->historyLoaded(peer->id);
auto changesIn = crl::time(0); auto changesIn = crl::time(0);
const auto muted = isMuted(peer, &changesIn); const auto muted = isMuted(peer, &changesIn);
if (history && history->changeMuted(muted)) { const auto changeInHistory = history && (history->muted() != muted);
if (changeInHistory) {
history->setMuted(muted);
// Notification already sent. // Notification already sent.
} else { } else {
peer->session().changes().peerUpdated( peer->session().changes().peerUpdated(
@ -316,18 +318,15 @@ void NotifySettings::unmuteByFinished() {
const auto history = _owner->historyLoaded((*i)->id); const auto history = _owner->historyLoaded((*i)->id);
auto changesIn = crl::time(0); auto changesIn = crl::time(0);
const auto muted = isMuted(*i, &changesIn); const auto muted = isMuted(*i, &changesIn);
if (history) {
history->setMuted(muted);
}
if (muted) { if (muted) {
if (history) {
history->changeMuted(true);
}
if (!changesInMin || changesInMin > changesIn) { if (!changesInMin || changesInMin > changesIn) {
changesInMin = changesIn; changesInMin = changesIn;
} }
++i; ++i;
} else { } else {
if (history) {
history->changeMuted(false);
}
i = _mutedPeers.erase(i); i = _mutedPeers.erase(i);
} }
} }
@ -335,14 +334,13 @@ void NotifySettings::unmuteByFinished() {
auto changesIn = crl::time(0); auto changesIn = crl::time(0);
const auto topic = i->first; const auto topic = i->first;
const auto muted = isMuted(topic, &changesIn); const auto muted = isMuted(topic, &changesIn);
topic->setMuted(muted);
if (muted) { if (muted) {
topic->changeMuted(true);
if (!changesInMin || changesInMin > changesIn) { if (!changesInMin || changesInMin > changesIn) {
changesInMin = changesIn; changesInMin = changesIn;
} }
++i; ++i;
} else { } else {
topic->changeMuted(false);
i = _mutedTopics.erase(i); i = _mutedTopics.erase(i);
} }
} }

View file

@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_options.h" #include "ui/text/text_options.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_unread_things.h"
#include "styles/style_dialogs.h" // st::dialogsTextWidthMin #include "styles/style_dialogs.h" // st::dialogsTextWidthMin
namespace Dialogs { namespace Dialogs {
@ -70,6 +69,12 @@ Data::Folder *Entry::asFolder() {
: nullptr; : nullptr;
} }
Data::Thread *Entry::asThread() {
return (_type == Type::History || _type == Type::ForumTopic)
? static_cast<Data::Thread*>(this)
: nullptr;
}
Data::ForumTopic *Entry::asTopic() { Data::ForumTopic *Entry::asTopic() {
return (_type == Type::ForumTopic) return (_type == Type::ForumTopic)
? static_cast<Data::ForumTopic*>(this) ? static_cast<Data::ForumTopic*>(this)
@ -104,41 +109,6 @@ void Entry::cachePinnedIndex(FilterId filterId, int index) {
pinnedIndexChanged(filterId, was, index); pinnedIndexChanged(filterId, was, index);
} }
void Entry::cacheTopPromoted(bool promoted) {
if (isTopPromoted() == promoted) {
return;
} else if (promoted) {
_flags |= Flag::IsTopPromoted;
} else {
_flags &= ~Flag::IsTopPromoted;
}
updateChatListSortPosition();
updateChatListEntry();
if (!isTopPromoted()) {
updateChatListExistence();
}
}
bool Entry::isTopPromoted() const {
return (_flags & Flag::IsTopPromoted);
}
const base::flat_set<MsgId> &Entry::unreadMentionsIds() const {
if (!_unreadThings) {
static const auto Result = base::flat_set<MsgId>();
return Result;
}
return _unreadThings->mentions.ids();
}
const base::flat_set<MsgId> &Entry::unreadReactionsIds() const {
if (!_unreadThings) {
static const auto Result = base::flat_set<MsgId>();
return Result;
}
return _unreadThings->reactions.ids();
}
bool Entry::needUpdateInChatList() const { bool Entry::needUpdateInChatList() const {
return inChatList() || shouldBeInChatList(); return inChatList() || shouldBeInChatList();
} }
@ -220,42 +190,6 @@ TimeId Entry::adjustedChatListTimeId() const {
return chatListTimeId(); return chatListTimeId();
} }
void Entry::setUnreadThingsKnown() {
_flags |= Flag::UnreadThingsKnown;
}
HistoryUnreadThings::Proxy Entry::unreadMentions() {
return {
this,
_unreadThings,
HistoryUnreadThings::Type::Mentions,
!!(_flags & Flag::UnreadThingsKnown),
};
}
HistoryUnreadThings::ConstProxy Entry::unreadMentions() const {
return {
_unreadThings ? &_unreadThings->mentions : nullptr,
!!(_flags & Flag::UnreadThingsKnown),
};
}
HistoryUnreadThings::Proxy Entry::unreadReactions() {
return {
this,
_unreadThings,
HistoryUnreadThings::Type::Reactions,
!!(_flags & Flag::UnreadThingsKnown),
};
}
HistoryUnreadThings::ConstProxy Entry::unreadReactions() const {
return {
_unreadThings ? &_unreadThings->reactions : nullptr,
!!(_flags & Flag::UnreadThingsKnown),
};
}
void Entry::changedChatListPinHook() { void Entry::changedChatListPinHook() {
} }

View file

@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "ui/unread_badge.h" #include "ui/unread_badge.h"
class HistoryItem;
class UserData;
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -23,13 +26,6 @@ class ForumTopic;
class CloudImageView; class CloudImageView;
} // namespace Data } // namespace Data
namespace HistoryUnreadThings {
enum class AddType;
struct All;
class Proxy;
class ConstProxy;
} // namespace HistoryUnreadThings
namespace Ui { namespace Ui {
} // namespace Ui } // namespace Ui
@ -113,8 +109,6 @@ public:
ForumTopic, ForumTopic,
}; };
Entry(not_null<Data::Session*> owner, Type type); Entry(not_null<Data::Session*> owner, Type type);
Entry(const Entry &other) = delete;
Entry &operator=(const Entry &other) = delete;
virtual ~Entry(); virtual ~Entry();
[[nodiscard]] Data::Session &owner() const; [[nodiscard]] Data::Session &owner() const;
@ -122,6 +116,7 @@ public:
History *asHistory(); History *asHistory();
Data::Folder *asFolder(); Data::Folder *asFolder();
Data::Thread *asThread();
Data::ForumTopic *asTopic(); Data::ForumTopic *asTopic();
PositionChange adjustByPosInChatList( PositionChange adjustByPosInChatList(
@ -149,7 +144,6 @@ public:
return lookupPinnedIndex(filterId) != 0; return lookupPinnedIndex(filterId) != 0;
} }
void cachePinnedIndex(FilterId filterId, int index); void cachePinnedIndex(FilterId filterId, int index);
[[nodiscard]] bool isTopPromoted() const;
[[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const { [[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const {
return filterId return filterId
? computeSortPosition(filterId) ? computeSortPosition(filterId)
@ -161,12 +155,6 @@ public:
bool needUpdateInChatList() const; bool needUpdateInChatList() const;
virtual TimeId adjustedChatListTimeId() const; virtual TimeId adjustedChatListTimeId() const;
void setUnreadThingsKnown();
[[nodiscard]] HistoryUnreadThings::Proxy unreadMentions();
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadMentions() const;
[[nodiscard]] HistoryUnreadThings::Proxy unreadReactions();
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const;
virtual int fixedOnTopIndex() const = 0; virtual int fixedOnTopIndex() const = 0;
static constexpr auto kArchiveFixOnTopIndex = 1; static constexpr auto kArchiveFixOnTopIndex = 1;
static constexpr auto kTopPromotionFixOnTopIndex = 2; static constexpr auto kTopPromotionFixOnTopIndex = 2;
@ -220,18 +208,7 @@ protected:
[[nodiscard]] int lookupPinnedIndex(FilterId filterId) const; [[nodiscard]] int lookupPinnedIndex(FilterId filterId) const;
void cacheTopPromoted(bool promoted);
[[nodiscard]] const base::flat_set<MsgId> &unreadMentionsIds() const;
[[nodiscard]] const base::flat_set<MsgId> &unreadReactionsIds() const;
private: private:
enum class Flag : uchar {
IsTopPromoted = 0x01,
UnreadThingsKnown = 0x02,
};
friend inline constexpr bool is_flag_type(Flag) { return true; }
virtual void changedChatListPinHook(); virtual void changedChatListPinHook();
void pinnedIndexChanged(FilterId filterId, int was, int now); void pinnedIndexChanged(FilterId filterId, int was, int now);
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const; [[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
@ -247,12 +224,10 @@ private:
uint64 _sortKeyInChatList = 0; uint64 _sortKeyInChatList = 0;
uint64 _sortKeyByDate = 0; uint64 _sortKeyByDate = 0;
base::flat_map<FilterId, int> _pinnedIndex; base::flat_map<FilterId, int> _pinnedIndex;
std::unique_ptr<HistoryUnreadThings::All> _unreadThings;
mutable Ui::PeerBadge _chatListBadge; mutable Ui::PeerBadge _chatListBadge;
mutable Ui::Text::String _chatListNameText; mutable Ui::Text::String _chatListNameText;
mutable int _chatListNameVersion = 0; mutable int _chatListNameVersion = 0;
TimeId _timeId = 0; TimeId _timeId = 0;
base::flags<Flag> _flags;
const Type _type; const Type _type;
}; };

View file

@ -12,12 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
namespace Dialogs { namespace Dialogs {
namespace {
using Folder = Data::Folder;
using ForumTopic = Data::ForumTopic;
} // namespace
Key::Key(History *history) : _value(history) { Key::Key(History *history) : _value(history) {
} }
@ -47,14 +41,18 @@ History *Key::history() const {
return _value ? _value->asHistory() : nullptr; return _value ? _value->asHistory() : nullptr;
} }
Folder *Key::folder() const { Data::Folder *Key::folder() const {
return _value ? _value->asFolder() : nullptr; return _value ? _value->asFolder() : nullptr;
} }
ForumTopic *Key::topic() const { Data::ForumTopic *Key::topic() const {
return _value ? _value->asTopic() : nullptr; return _value ? _value->asTopic() : nullptr;
} }
Data::Thread *Key::thread() const {
return _value ? _value->asThread() : nullptr;
}
History *Key::parentHistory() const { History *Key::parentHistory() const {
if (const auto result = history()) { if (const auto result = history()) {
return result; return result;

View file

@ -11,6 +11,7 @@ class History;
class PeerData; class PeerData;
namespace Data { namespace Data {
class Thread;
class Folder; class Folder;
class ForumTopic; class ForumTopic;
} // namespace Data } // namespace Data
@ -40,6 +41,7 @@ public:
History *history() const; History *history() const;
Data::Folder *folder() const; Data::Folder *folder() const;
Data::ForumTopic *topic() const; Data::ForumTopic *topic() const;
Data::Thread *thread() const;
History *parentHistory() const; History *parentHistory() const;
PeerData *peer() const; PeerData *peer() const;

View file

@ -96,6 +96,9 @@ public:
[[nodiscard]] Data::ForumTopic *topic() const { [[nodiscard]] Data::ForumTopic *topic() const {
return _id.topic(); return _id.topic();
} }
[[nodiscard]] Data::Thread *thread() const {
return _id.thread();
}
[[nodiscard]] not_null<Entry*> entry() const { [[nodiscard]] not_null<Entry*> entry() const {
return _id.entry(); return _id.entry();
} }

View file

@ -35,7 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum_topic.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
namespace Dialogs::Ui { namespace Dialogs::Ui {
@ -346,7 +345,7 @@ void PaintRow(
row->paintRipple(p, 0, 0, context.width, &ripple->c); row->paintRipple(p, 0, 0, context.width, &ripple->c);
const auto history = chat.history(); const auto history = chat.history();
const auto topic = chat.topic(); const auto thread = chat.thread();
if (flags & Flag::SavedMessages) { if (flags & Flag::SavedMessages) {
EmptyUserpic::PaintSavedMessages( EmptyUserpic::PaintSavedMessages(
@ -418,8 +417,8 @@ void PaintRow(
if (promoted && !history->topPromotionMessage().isEmpty()) { if (promoted && !history->topPromotionMessage().isEmpty()) {
auto availableWidth = namewidth; auto availableWidth = namewidth;
p.setFont(st::dialogsTextFont); p.setFont(st::dialogsTextFont);
if (history->cloudDraftTextCache.isEmpty()) { if (history->cloudDraftTextCache().isEmpty()) {
history->cloudDraftTextCache.setText( history->cloudDraftTextCache().setText(
st::dialogsTextStyle, st::dialogsTextStyle,
history->topPromotionMessage(), history->topPromotionMessage(),
DialogTextOptions()); DialogTextOptions());
@ -429,7 +428,7 @@ void PaintRow(
: context.selected : context.selected
? st::dialogsTextFgOver ? st::dialogsTextFgOver
: st::dialogsTextFg); : st::dialogsTextFg);
history->cloudDraftTextCache.draw(p, { history->cloudDraftTextCache().draw(p, {
.position = { nameleft, texttop }, .position = { nameleft, texttop },
.availableWidth = availableWidth, .availableWidth = availableWidth,
.spoiler = Text::DefaultSpoilerCache(), .spoiler = Text::DefaultSpoilerCache(),
@ -475,7 +474,7 @@ void PaintRow(
context.width, context.width,
color, color,
context.paused)) { context.paused)) {
if (history->cloudDraftTextCache.isEmpty()) { if (history->cloudDraftTextCache().isEmpty()) {
using namespace TextUtilities; using namespace TextUtilities;
auto draftWrapped = Text::PlainLink( auto draftWrapped = Text::PlainLink(
tr::lng_dialogs_text_from_wrapped( tr::lng_dialogs_text_from_wrapped(
@ -500,7 +499,7 @@ void PaintRow(
.session = &history->session(), .session = &history->session(),
.customEmojiRepaint = customEmojiRepaint, .customEmojiRepaint = customEmojiRepaint,
}; };
history->cloudDraftTextCache.setMarkedText( history->cloudDraftTextCache().setMarkedText(
st::dialogsTextStyle, st::dialogsTextStyle,
draftText, draftText,
DialogTextOptions(), DialogTextOptions(),
@ -511,7 +510,7 @@ void PaintRow(
: context.selected : context.selected
? st::dialogsTextFgOver ? st::dialogsTextFgOver
: st::dialogsTextFg); : st::dialogsTextFg);
history->cloudDraftTextCache.draw(p, { history->cloudDraftTextCache().draw(p, {
.position = { nameleft, texttop }, .position = { nameleft, texttop },
.availableWidth = availableWidth, .availableWidth = availableWidth,
.palette = &(supportMode .palette = &(supportMode
@ -562,7 +561,7 @@ void PaintRow(
// Empty history // Empty history
} }
} else if (!item->isEmpty()) { } else if (!item->isEmpty()) {
if ((history || topic) && !promoted) { if (thread && !promoted) {
PaintRowDate(p, date, rectForName, context); PaintRowDate(p, date, rectForName, context);
} }
@ -886,7 +885,7 @@ void RowPainter::Paint(
const PaintContext &context) { const PaintContext &context) {
const auto entry = row->entry(); const auto entry = row->entry();
const auto history = row->history(); const auto history = row->history();
const auto topic = row->topic(); const auto thread = row->thread();
const auto peer = history ? history->peer.get() : nullptr; const auto peer = history ? history->peer.get() : nullptr;
const auto unreadCount = entry->chatListUnreadCount(); const auto unreadCount = entry->chatListUnreadCount();
const auto unreadMark = entry->chatListUnreadMark(); const auto unreadMark = entry->chatListUnreadMark();
@ -916,12 +915,11 @@ void RowPainter::Paint(
? base::unixtime::parse(cloudDraft->date) ? base::unixtime::parse(cloudDraft->date)
: QDateTime(); : QDateTime();
}(); }();
const auto displayMentionBadge = (history const auto displayMentionBadge = thread
&& history->unreadMentions().has()) && thread->unreadMentions().has();
|| (topic && topic->unreadMentions().has());
const auto displayReactionBadge = !displayMentionBadge const auto displayReactionBadge = !displayMentionBadge
&& ((history && history->unreadReactions().has()) && thread
|| (topic && topic->unreadReactions().has())); && thread->unreadReactions().has();
const auto mentionOrReactionMuted = (entry->folder() != nullptr) const auto mentionOrReactionMuted = (entry->folder() != nullptr)
|| (!displayMentionBadge && unreadMuted); || (!displayMentionBadge && unreadMuted);
const auto displayUnreadCounter = [&] { const auto displayUnreadCounter = [&] {
@ -991,10 +989,8 @@ void RowPainter::Paint(
: false; : false;
const auto view = actionWasPainted const auto view = actionWasPainted
? nullptr ? nullptr
: history : thread
? &history->lastItemDialogsView ? &thread->lastItemDialogsView()
: topic
? &topic->lastItemDialogsView
: nullptr; : nullptr;
if (const auto folder = row->folder()) { if (const auto folder = row->folder()) {
PaintListEntryText(p, row, context, rect); PaintListEntryText(p, row, context, rect);

View file

@ -70,13 +70,13 @@ using UpdateFlag = Data::HistoryUpdate::Flag;
} // namespace } // namespace
History::History(not_null<Data::Session*> owner, PeerId peerId) History::History(not_null<Data::Session*> owner, PeerId peerId)
: Entry(owner, Type::History) : Thread(owner, Type::History)
, peer(owner->peer(peerId)) , peer(owner->peer(peerId))
, cloudDraftTextCache(st::dialogsTextWidthMin)
, _delegateMixin(HistoryInner::DelegateMixin()) , _delegateMixin(HistoryInner::DelegateMixin())
, _flags(owner->notifySettings().isMuted(peer) ? Flag::Muted : Flag(0))
, _chatListNameSortKey(owner->nameSortKey(peer->name())) , _chatListNameSortKey(owner->nameSortKey(peer->name()))
, _sendActionPainter(this) { , _sendActionPainter(this) {
Thread::setMuted(owner->notifySettings().isMuted(peer));
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
if (user->isBot()) { if (user->isBot()) {
_outboxReadBefore = std::numeric_limits<MsgId>::max(); _outboxReadBefore = std::numeric_limits<MsgId>::max();
@ -84,6 +84,8 @@ History::History(not_null<Data::Session*> owner, PeerId peerId)
} }
} }
History::~History() = default;
void History::clearLastKeyboard() { void History::clearLastKeyboard() {
if (lastKeyboardId) { if (lastKeyboardId) {
if (lastKeyboardId == lastKeyboardHiddenId) { if (lastKeyboardId == lastKeyboardHiddenId) {
@ -100,38 +102,6 @@ int History::height() const {
return _height; return _height;
} }
void History::removeNotification(not_null<HistoryItem*> item) {
_notifications.erase(
ranges::remove(_notifications, item, &ItemNotification::item),
end(_notifications));
}
auto History::currentNotification() const -> std::optional<ItemNotification> {
return empty(_notifications)
? std::nullopt
: std::make_optional(_notifications.front());
}
bool History::hasNotification() const {
return !empty(_notifications);
}
void History::skipNotification() {
if (!empty(_notifications)) {
_notifications.pop_front();
}
}
void History::pushNotification(ItemNotification notification) {
_notifications.push_back(notification);
}
void History::popNotification(ItemNotification notification) {
if (!empty(_notifications) && (_notifications.back() == notification)) {
_notifications.pop_back();
}
}
bool History::hasPendingResizedItems() const { bool History::hasPendingResizedItems() const {
return _flags & Flag::HasPendingResizedItems; return _flags & Flag::HasPendingResizedItems;
} }
@ -182,7 +152,7 @@ void History::checkChatListMessageRemoved(not_null<HistoryItem*> item) {
} }
void History::itemVanished(not_null<HistoryItem*> item) { void History::itemVanished(not_null<HistoryItem*> item) {
removeNotification(item); item->thread()->removeNotification(item);
if (lastKeyboardId == item->id) { if (lastKeyboardId == item->id) {
clearLastKeyboard(); clearLastKeyboard();
} }
@ -255,7 +225,7 @@ void History::setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft)
} }
const auto changingCloudDraft = (key == Data::DraftKey::Cloud()); const auto changingCloudDraft = (key == Data::DraftKey::Cloud());
if (changingCloudDraft) { if (changingCloudDraft) {
cloudDraftTextCache.clear(); cloudDraftTextCache().clear();
} }
if (draft) { if (draft) {
_drafts[key] = std::move(draft); _drafts[key] = std::move(draft);
@ -283,7 +253,7 @@ void History::clearDrafts() {
const auto changingCloudDraft = _drafts.contains(Data::DraftKey::Cloud()); const auto changingCloudDraft = _drafts.contains(Data::DraftKey::Cloud());
_drafts.clear(); _drafts.clear();
if (changingCloudDraft) { if (changingCloudDraft) {
cloudDraftTextCache.clear(); cloudDraftTextCache().clear();
updateChatListSortPosition(); updateChatListSortPosition();
} }
} }
@ -314,7 +284,7 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
existing->date = base::unixtime::now(); existing->date = base::unixtime::now();
} }
cloudDraftTextCache.clear(); cloudDraftTextCache().clear();
updateChatListSortPosition(); updateChatListSortPosition();
return cloudDraft(); return cloudDraft();
@ -1148,12 +1118,12 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
from->madeAction(item->date()); from->madeAction(item->date());
} }
item->contributeToSlowmode(); item->contributeToSlowmode();
auto notification = ItemNotification{ auto notification = Data::ItemNotification{
.item = item, .item = item,
.type = ItemNotificationType::Message, .type = Data::ItemNotificationType::Message,
}; };
if (item->showNotification()) { if (item->showNotification()) {
pushNotification(notification); item->thread()->pushNotification(notification);
} }
owner().notifyNewItemAdded(item); owner().notifyNewItemAdded(item);
const auto stillShow = item->showNotification(); // Could be read already. const auto stillShow = item->showNotification(); // Could be read already.
@ -1732,7 +1702,7 @@ void History::setUnreadMark(bool unread) {
if (clearUnreadOnClientSide()) { if (clearUnreadOnClientSide()) {
unread = false; unread = false;
} }
if (_unreadMark == unread) { if (unreadMark() == unread) {
return; return;
} }
const auto noUnreadMessages = !unreadCount(); const auto noUnreadMessages = !unreadCount();
@ -1744,11 +1714,7 @@ void History::setUnreadMark(bool unread) {
session().changes().historyUpdated(this, UpdateFlag::UnreadView); session().changes().historyUpdated(this, UpdateFlag::UnreadView);
}); });
const auto notifier = unreadStateChangeNotifier(noUnreadMessages); const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
_unreadMark = unread; Thread::setUnreadMark(unread);
}
bool History::unreadMark() const {
return _unreadMark;
} }
void History::setFakeUnreadWhileOpened(bool enabled) { void History::setFakeUnreadWhileOpened(bool enabled) {
@ -1768,13 +1734,9 @@ void History::setFakeUnreadWhileOpened(bool enabled) {
return _fakeUnreadWhileOpened; return _fakeUnreadWhileOpened;
} }
bool History::muted() const { void History::setMuted(bool muted) {
return (_flags & Flag::Muted);
}
bool History::changeMuted(bool muted) {
if (this->muted() == muted) { if (this->muted() == muted) {
return false; return;
} }
const auto refresher = gsl::finally([&] { const auto refresher = gsl::finally([&] {
if (inChatList()) { if (inChatList()) {
@ -1787,12 +1749,7 @@ bool History::changeMuted(bool muted) {
}); });
const auto notify = (unreadCountForBadge() > 0); const auto notify = (unreadCountForBadge() > 0);
const auto notifier = unreadStateChangeNotifier(notify); const auto notifier = unreadStateChangeNotifier(notify);
if (muted) { Thread::setMuted(muted);
_flags |= Flag::Muted;
} else {
_flags &= ~Flag::Muted;
}
return true;
} }
void History::getNextFirstUnreadMessage() { void History::getNextFirstUnreadMessage() {
@ -2082,7 +2039,7 @@ bool History::chatListMutedBadge() const {
Dialogs::UnreadState History::chatListUnreadState() const { Dialogs::UnreadState History::chatListUnreadState() const {
auto result = Dialogs::UnreadState(); auto result = Dialogs::UnreadState();
const auto count = _unreadCount.value_or(0); const auto count = _unreadCount.value_or(0);
const auto mark = !count && _unreadMark; const auto mark = !count && unreadMark();
const auto muted = this->muted(); const auto muted = this->muted();
result.messages = count; result.messages = count;
result.messagesMuted = muted ? count : 0; result.messagesMuted = muted ? count : 0;
@ -2164,21 +2121,6 @@ void History::finishBuildingFrontBlock() {
} }
} }
void History::clearNotifications() {
_notifications.clear();
}
void History::clearIncomingNotifications() {
if (!peer->isSelf()) {
const auto proj = [](ItemNotification notification) {
return notification.item->out();
};
_notifications.erase(
ranges::remove(_notifications, false, proj),
end(_notifications));
}
}
bool History::loadedAtBottom() const { bool History::loadedAtBottom() const {
return _loadedAtBottom; return _loadedAtBottom;
} }
@ -2697,9 +2639,9 @@ void History::cacheTopPromotion(
if (topPromotionType() != type || _topPromotedMessage != message) { if (topPromotionType() != type || _topPromotedMessage != message) {
_topPromotedType = type; _topPromotedType = type;
_topPromotedMessage = message; _topPromotedMessage = message;
cloudDraftTextCache.clear(); cloudDraftTextCache().clear();
} else if (changed) { } else if (changed) {
cloudDraftTextCache.clear(); cloudDraftTextCache().clear();
} }
} }
@ -3169,7 +3111,7 @@ void History::clear(ClearType type) {
for (const auto &item : local) { for (const auto &item : local) {
item->destroy(); item->destroy();
} }
_notifications.clear(); clearNotifications();
owner().notifyHistoryCleared(this); owner().notifyHistoryCleared(this);
if (unreadCountKnown()) { if (unreadCountKnown()) {
setUnreadCount(0); setUnreadCount(0);
@ -3265,7 +3207,24 @@ void History::setHasPinnedMessages(bool has) {
session().changes().historyUpdated(this, UpdateFlag::PinnedMessages); session().changes().historyUpdated(this, UpdateFlag::PinnedMessages);
} }
History::~History() = default; void History::cacheTopPromoted(bool promoted) {
if (isTopPromoted() == promoted) {
return;
} else if (promoted) {
_flags |= Flag::IsTopPromoted;
} else {
_flags &= ~Flag::IsTopPromoted;
}
updateChatListSortPosition();
updateChatListEntry();
if (!isTopPromoted()) {
updateChatListExistence();
}
}
bool History::isTopPromoted() const {
return (_flags & Flag::IsTopPromoted);
}
HistoryBlock::HistoryBlock(not_null<History*> history) HistoryBlock::HistoryBlock(not_null<History*> history)
: _history(history) { : _history(history) {

View file

@ -10,8 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h" #include "data/data_types.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "dialogs/dialogs_entry.h" #include "data/data_thread.h"
#include "dialogs/ui/dialogs_message_view.h"
#include "history/view/history_view_send_action.h" #include "history/view/history_view_send_action.h"
#include "base/observer.h" #include "base/observer.h"
#include "base/timer.h" #include "base/timer.h"
@ -71,31 +70,17 @@ enum class NewMessageType {
Existing, Existing,
}; };
enum class ItemNotificationType { class History final : public Data::Thread {
Message,
Reaction,
};
struct ItemNotification {
not_null<HistoryItem*> item;
UserData *reactionSender = nullptr;
ItemNotificationType type = ItemNotificationType::Message;
friend inline bool operator==(ItemNotification a, ItemNotification b) {
return (a.item == b.item)
&& (a.reactionSender == b.reactionSender)
&& (a.type == b.type);
}
};
class History final : public Dialogs::Entry {
public: public:
using Element = HistoryView::Element; using Element = HistoryView::Element;
History(not_null<Data::Session*> owner, PeerId peerId); History(not_null<Data::Session*> owner, PeerId peerId);
History(const History &) = delete;
History &operator=(const History &) = delete;
~History(); ~History();
not_null<History*> owningHistory() override {
return this;
}
[[nodiscard]] auto delegateMixin() const [[nodiscard]] auto delegateMixin() const
-> not_null<HistoryMainElementDelegateMixin*> { -> not_null<HistoryMainElementDelegateMixin*> {
return _delegateMixin.get(); return _delegateMixin.get();
@ -257,21 +242,17 @@ public:
[[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const; [[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const;
void setUnreadCount(int newUnreadCount); void setUnreadCount(int newUnreadCount);
void setUnreadMark(bool unread); void setUnreadMark(bool unread) override;
[[nodiscard]] bool unreadMark() const;
void setFakeUnreadWhileOpened(bool enabled); void setFakeUnreadWhileOpened(bool enabled);
[[nodiscard]] bool fakeUnreadWhileOpened() const; [[nodiscard]] bool fakeUnreadWhileOpened() const;
[[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0. [[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
[[nodiscard]] bool muted() const; void setMuted(bool muted) override;
bool changeMuted(bool muted);
void addUnreadBar(); void addUnreadBar();
void destroyUnreadBar(); void destroyUnreadBar();
[[nodiscard]] Element *unreadBar() const; [[nodiscard]] Element *unreadBar() const;
void calculateFirstUnreadMessage(); void calculateFirstUnreadMessage();
void unsetFirstUnreadMessage(); void unsetFirstUnreadMessage();
[[nodiscard]] Element *firstUnreadMessage() const; [[nodiscard]] Element *firstUnreadMessage() const;
void clearNotifications();
void clearIncomingNotifications();
[[nodiscard]] bool loadedAtBottom() const; // last message is in the list [[nodiscard]] bool loadedAtBottom() const; // last message is in the list
void setNotLoadedAtBottom(); void setNotLoadedAtBottom();
@ -315,12 +296,6 @@ public:
void itemRemoved(not_null<HistoryItem*> item); void itemRemoved(not_null<HistoryItem*> item);
void itemVanished(not_null<HistoryItem*> item); void itemVanished(not_null<HistoryItem*> item);
[[nodiscard]] std::optional<ItemNotification> currentNotification() const;
bool hasNotification() const;
void skipNotification();
void pushNotification(ItemNotification notification);
void popNotification(ItemNotification notification);
bool hasPendingResizedItems() const; bool hasPendingResizedItems() const;
void setHasPendingResizedItems(); void setHasPendingResizedItems();
@ -441,11 +416,13 @@ public:
[[nodiscard]] bool hasPinnedMessages() const; [[nodiscard]] bool hasPinnedMessages() const;
void setHasPinnedMessages(bool has); void setHasPinnedMessages(bool has);
[[nodiscard]] bool isTopPromoted() const;
const not_null<PeerData*> peer;
// Still public data. // Still public data.
std::deque<std::unique_ptr<HistoryBlock>> blocks; std::deque<std::unique_ptr<HistoryBlock>> blocks;
not_null<PeerData*> peer;
// we save the last showAtMsgId to restore the state when switching // we save the last showAtMsgId to restore the state when switching
// between different conversation histories // between different conversation histories
MsgId showAtMsgId = ShowAtUnreadMsgId; MsgId showAtMsgId = ShowAtUnreadMsgId;
@ -464,21 +441,20 @@ public:
mtpRequestId sendRequestId = 0; mtpRequestId sendRequestId = 0;
Ui::Text::String cloudDraftTextCache;
Dialogs::Ui::MessageView lastItemDialogsView;
private: private:
friend class HistoryBlock; friend class HistoryBlock;
enum class Flag : uchar { enum class Flag : uchar {
HasPendingResizedItems = (1 << 0), HasPendingResizedItems = (1 << 0),
Muted = (1 << 1), IsTopPromoted = (1 << 1),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { friend inline constexpr auto is_flag_type(Flag) {
return true; return true;
}; };
void cacheTopPromoted(bool promoted);
// when this item is destroyed scrollTopItem just points to the next one // when this item is destroyed scrollTopItem just points to the next one
// and scrollTopOffset remains the same // and scrollTopOffset remains the same
// if we are at the bottom of the window scrollTopItem == nullptr and // if we are at the bottom of the window scrollTopItem == nullptr and
@ -530,7 +506,6 @@ private:
void mainViewRemoved( void mainViewRemoved(
not_null<HistoryBlock*> block, not_null<HistoryBlock*> block,
not_null<Element*> view); not_null<Element*> view);
void removeNotification(not_null<HistoryItem*> item);
TimeId adjustedChatListTimeId() const override; TimeId adjustedChatListTimeId() const override;
void changedChatListPinHook() override; void changedChatListPinHook() override;
@ -614,7 +589,6 @@ private:
QString _chatListNameSortKey; QString _chatListNameSortKey;
bool _unreadMark = false;
bool _fakeUnreadWhileOpened = false; bool _fakeUnreadWhileOpened = false;
bool _hasPinnedMessages = false; bool _hasPinnedMessages = false;
@ -637,8 +611,6 @@ private:
HistoryView::SendActionPainter _sendActionPainter; HistoryView::SendActionPainter _sendActionPainter;
std::deque<ItemNotification> _notifications;
}; };
class HistoryBlock { class HistoryBlock {

View file

@ -2746,6 +2746,7 @@ void HistoryInner::checkHistoryActivation() {
adjustCurrent(_visibleAreaBottom); adjustCurrent(_visibleAreaBottom);
if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) { if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) {
// Clear possible message notifications. // Clear possible message notifications.
// Side-effect: Also clears all notifications from forum topics.
Core::App().notifications().clearFromHistory(_history); Core::App().notifications().clearFromHistory(_history);
} }
if (_curHistory != _history || _history->isEmpty()) { if (_curHistory != _history || _history->isEmpty()) {

View file

@ -227,12 +227,12 @@ void CheckReactionNotificationSchedule(
if (user->blockStatus() == Status::Unknown) { if (user->blockStatus() == Status::Unknown) {
user->updateFull(); user->updateFull();
} }
const auto notification = ItemNotification{ const auto notification = Data::ItemNotification{
.item = item, .item = item,
.reactionSender = user, .reactionSender = user,
.type = ItemNotificationType::Reaction, .type = Data::ItemNotificationType::Reaction,
}; };
item->history()->pushNotification(notification); item->thread()->pushNotification(notification);
Core::App().notifications().schedule(notification); Core::App().notifications().schedule(notification);
return; return;
} }
@ -374,9 +374,9 @@ void HistoryItem::invalidateChatListEntry() {
history()->session().changes().messageUpdated( history()->session().changes().messageUpdated(
this, this,
Data::MessageUpdate::Flag::DialogRowRefresh); Data::MessageUpdate::Flag::DialogRowRefresh);
history()->lastItemDialogsView.itemInvalidated(this); history()->lastItemDialogsView().itemInvalidated(this);
if (const auto topic = this->topic()) { if (const auto topic = this->topic()) {
topic->lastItemDialogsView.itemInvalidated(this); topic->lastItemDialogsView().itemInvalidated(this);
} }
} }
@ -620,9 +620,20 @@ void HistoryItem::destroy() {
_history->destroyMessage(this); _history->destroyMessage(this);
} }
not_null<Data::Thread*> HistoryItem::thread() const {
if (const auto topic = this->topic()) {
return topic;
}
return _history;
}
Data::ForumTopic *HistoryItem::topic() const { Data::ForumTopic *HistoryItem::topic() const {
const auto forum = _history->peer->forum(); if (const auto rootId = topicRootId()) {
return forum ? forum->topicFor(this) : nullptr; if (const auto forum = _history->peer->forum()) {
return forum->topicFor(rootId);
}
}
return nullptr;
} }
void HistoryItem::refreshMainView() { void HistoryItem::refreshMainView() {

View file

@ -46,6 +46,7 @@ class Media;
struct MessageReaction; struct MessageReaction;
class MessageReactions; class MessageReactions;
class ForumTopic; class ForumTopic;
class Thread;
} // namespace Data } // namespace Data
namespace Main { namespace Main {
@ -122,6 +123,7 @@ public:
const QString &label, const QString &label,
const TextWithEntities &content); const TextWithEntities &content);
[[nodiscard]] not_null<Data::Thread*> thread() const;
[[nodiscard]] not_null<History*> history() const { [[nodiscard]] not_null<History*> history() const {
return _history; return _history;
} }

View file

@ -42,7 +42,7 @@ template <typename Update>
void Proxy::setCount(int count) { void Proxy::setCount(int count) {
if (!_known) { if (!_known) {
_entry->setUnreadThingsKnown(); _thread->setUnreadThingsKnown();
} }
if (!_data) { if (!_data) {
if (!count) { if (!count) {
@ -69,16 +69,16 @@ void Proxy::setCount(int count) {
const auto has = (count > 0); const auto has = (count > 0);
if (has != had) { if (has != had) {
if (_type == Type::Mentions) { if (_type == Type::Mentions) {
if (const auto history = _entry->asHistory()) { if (const auto history = _thread->asHistory()) {
_entry->owner().chatsFilters().refreshHistory(history); _thread->owner().chatsFilters().refreshHistory(history);
} }
} }
_entry->updateChatListEntry(); _thread->updateChatListEntry();
} }
} }
bool Proxy::add(MsgId msgId, AddType type) { bool Proxy::add(MsgId msgId, AddType type) {
if (const auto history = _entry->asHistory()) { if (const auto history = _thread->asHistory()) {
if (history->peer->isBroadcast()) { if (history->peer->isBroadcast()) {
return false; return false;
} }
@ -130,7 +130,7 @@ void Proxy::addSlice(const MTPmessages_Messages &slice, int alreadyLoaded) {
if (!alreadyLoaded && _data) { if (!alreadyLoaded && _data) {
resolveList().clear(); resolveList().clear();
} }
const auto history = resolveHistory(); const auto history = _thread->owningHistory();
auto fullCount = slice.match([&]( auto fullCount = slice.match([&](
const MTPDmessages_messagesNotModified &) { const MTPDmessages_messagesNotModified &) {
LOG(("API Error: received messages.messagesNotModified! " LOG(("API Error: received messages.messagesNotModified! "
@ -150,7 +150,7 @@ void Proxy::addSlice(const MTPmessages_Messages &slice, int alreadyLoaded) {
return data.vcount().v; return data.vcount().v;
}); });
auto &owner = _entry->owner(); auto &owner = _thread->owner();
const auto messages = slice.match([&]( const auto messages = slice.match([&](
const MTPDmessages_messagesNotModified &) { const MTPDmessages_messagesNotModified &) {
LOG(("API Error: received messages.messagesNotModified! " LOG(("API Error: received messages.messagesNotModified! "
@ -203,7 +203,7 @@ void Proxy::checkAdd(MsgId msgId, bool resolved) {
if (!list.loadedCount() || list.maxLoaded() <= msgId) { if (!list.loadedCount() || list.maxLoaded() <= msgId) {
return; return;
} }
const auto history = resolveHistory(); const auto history = _thread->owningHistory();
const auto peer = history->peer; const auto peer = history->peer;
const auto item = peer->owner().message(peer, msgId); const auto item = peer->owner().message(peer, msgId);
if (item && item->hasUnreadReaction()) { if (item && item->hasUnreadReaction()) {
@ -216,11 +216,11 @@ void Proxy::checkAdd(MsgId msgId, bool resolved) {
} }
void Proxy::notifyUpdated() { void Proxy::notifyUpdated() {
if (const auto history = _entry->asHistory()) { if (const auto history = _thread->asHistory()) {
history->session().changes().historyUpdated( history->session().changes().historyUpdated(
history, history,
HistoryUpdateFlag(_type)); HistoryUpdateFlag(_type));
} else if (const auto topic = _entry->asTopic()) { } else if (const auto topic = _thread->asTopic()) {
topic->session().changes().topicUpdated( topic->session().changes().topicUpdated(
topic, topic,
TopicUpdateFlag(_type)); TopicUpdateFlag(_type));
@ -245,9 +245,4 @@ List &Proxy::resolveList() {
Unexpected("Unread things type in Proxy::resolveList."); Unexpected("Unread things type in Proxy::resolveList.");
} }
not_null<History*> Proxy::resolveHistory() const {
const auto result = _entry->asHistory();
return result ? not_null(result) : _entry->asTopic()->history();
}
} // namespace HistoryUnreadThings } // namespace HistoryUnreadThings

View file

@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History; class History;
namespace Dialogs { namespace Data {
class Entry; class Thread;
} // namespace Data } // namespace Data
namespace HistoryUnreadThings { namespace HistoryUnreadThings {
@ -108,7 +108,7 @@ private:
class Proxy final : public ConstProxy { class Proxy final : public ConstProxy {
public: public:
Proxy( Proxy(
not_null<Dialogs::Entry*> entry, not_null<Data::Thread*> thread,
std::unique_ptr<All> &data, std::unique_ptr<All> &data,
Type type, Type type,
bool known) bool known)
@ -119,7 +119,7 @@ public:
? &data->mentions ? &data->mentions
: &data->reactions), : &data->reactions),
known) known)
, _entry(entry) , _thread(thread)
, _data(data) , _data(data)
, _type(type) , _type(type)
, _known(known) { , _known(known) {
@ -138,9 +138,8 @@ private:
void createData(); void createData();
void notifyUpdated(); void notifyUpdated();
[[nodiscard]] List &resolveList(); [[nodiscard]] List &resolveList();
[[nodiscard]] not_null<History*> resolveHistory() const;
const not_null<Dialogs::Entry*> _entry; const not_null<Data::Thread*> _thread;
std::unique_ptr<All> &_data; std::unique_ptr<All> &_data;
Type _type = Type::Mentions; Type _type = Type::Mentions;
bool _known = false; bool _known = false;

View file

@ -2865,6 +2865,7 @@ void HistoryWidget::newItemAdded(not_null<HistoryItem*> item) {
session().data().histories().readInboxOnNewMessage(item); session().data().histories().readInboxOnNewMessage(item);
// Also clear possible scheduled messages notifications. // Also clear possible scheduled messages notifications.
// Side-effect: Also clears all notifications from forum topics.
Core::App().notifications().clearFromHistory(_history); Core::App().notifications().clearFromHistory(_history);
} }
} }
@ -3954,7 +3955,7 @@ void HistoryWidget::cornerButtonsShowAtPosition(
} }
} }
Dialogs::Entry *HistoryWidget::cornerButtonsEntry() { Data::Thread *HistoryWidget::cornerButtonsThread() {
return _history; return _history;
} }

View file

@ -325,7 +325,7 @@ private:
void cornerButtonsShowAtPosition( void cornerButtonsShowAtPosition(
Data::MessagePosition position) override; Data::MessagePosition position) override;
Dialogs::Entry *cornerButtonsEntry() override; Data::Thread *cornerButtonsThread() override;
FullMsgId cornerButtonsCurrentId() override; FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override; bool cornerButtonsIgnoreVisibility() override;
std::optional<bool> cornerButtonsDownShown() override; std::optional<bool> cornerButtonsDownShown() override;

View file

@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_unread_things.h" #include "history/history_unread_things.h"
#include "dialogs/dialogs_entry.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "menu/menu_send.h" #include "menu/menu_send.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -56,10 +55,10 @@ CornerButtons::CornerButtons(
filterScroll(_reactions); filterScroll(_reactions);
SendMenu::SetupUnreadMentionsMenu(_mentions.widget.data(), [=] { SendMenu::SetupUnreadMentionsMenu(_mentions.widget.data(), [=] {
return _delegate->cornerButtonsEntry(); return _delegate->cornerButtonsThread();
}); });
SendMenu::SetupUnreadReactionsMenu(_reactions.widget.data(), [=] { SendMenu::SetupUnreadReactionsMenu(_reactions.widget.data(), [=] {
return _delegate->cornerButtonsEntry(); return _delegate->cornerButtonsThread();
}); });
} }
@ -86,14 +85,14 @@ void CornerButtons::mentionsClick() {
if (!history) { if (!history) {
return; return;
} }
const auto entry = _delegate->cornerButtonsEntry(); const auto thread = _delegate->cornerButtonsThread();
const auto msgId = entry->unreadMentions().minLoaded(); const auto msgId = thread->unreadMentions().minLoaded();
const auto already = (_delegate->cornerButtonsCurrentId().msg == msgId); const auto already = (_delegate->cornerButtonsCurrentId().msg == msgId);
// Mark mention voice/video message as read. // Mark mention voice/video message as read.
// See https://github.com/telegramdesktop/tdesktop/issues/5623 // See https://github.com/telegramdesktop/tdesktop/issues/5623
if (msgId && already) { if (msgId && already) {
if (const auto item = entry->owner().message(history->peer, msgId)) { if (const auto item = thread->owner().message(history->peer, msgId)) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto document = media->document()) { if (const auto document = media->document()) {
if (!media->webpage() if (!media->webpage()
@ -113,8 +112,8 @@ void CornerButtons::reactionsClick() {
if (!history) { if (!history) {
return; return;
} }
const auto entry = _delegate->cornerButtonsEntry(); const auto thread = _delegate->cornerButtonsThread();
showAt(entry->unreadReactions().minLoaded()); showAt(thread->unreadReactions().minLoaded());
} }
void CornerButtons::clearReplyReturns() { void CornerButtons::clearReplyReturns() {
@ -135,10 +134,10 @@ void CornerButtons::setReplyReturns(QVector<FullMsgId> replyReturns) {
} }
void CornerButtons::computeCurrentReplyReturn() { void CornerButtons::computeCurrentReplyReturn() {
const auto entry = _delegate->cornerButtonsEntry(); const auto thread = _delegate->cornerButtonsThread();
_replyReturn = (!entry || _replyReturns.empty()) _replyReturn = (!thread || _replyReturns.empty())
? nullptr ? nullptr
: entry->owner().message(_replyReturns.back()); : thread->owner().message(_replyReturns.back());
} }
void CornerButtons::skipReplyReturn(FullMsgId id) { void CornerButtons::skipReplyReturn(FullMsgId id) {
@ -188,15 +187,8 @@ CornerButton &CornerButtons::buttonByType(Type type) {
} }
History *CornerButtons::lookupHistory() const { History *CornerButtons::lookupHistory() const {
const auto entry = _delegate->cornerButtonsEntry(); const auto thread = _delegate->cornerButtonsThread();
if (!entry) { return thread ? thread->owningHistory().get() : nullptr;
return nullptr;
} else if (const auto history = entry->asHistory()) {
return history;
} else if (const auto topic = entry->asTopic()) {
return topic->history();
}
return nullptr;
} }
void CornerButtons::showAt(MsgId id) { void CornerButtons::showAt(MsgId id) {
@ -225,14 +217,14 @@ void CornerButtons::updateUnreadThingsVisibility() {
if (_delegate->cornerButtonsIgnoreVisibility()) { if (_delegate->cornerButtonsIgnoreVisibility()) {
return; return;
} }
const auto entry = _delegate->cornerButtonsEntry(); const auto thread = _delegate->cornerButtonsThread();
if (!entry) { if (!thread) {
updateVisibility(Type::Mentions, false); updateVisibility(Type::Mentions, false);
updateVisibility(Type::Reactions, false); updateVisibility(Type::Reactions, false);
return; return;
} }
auto &unreadThings = entry->session().api().unreadThings(); auto &unreadThings = thread->session().api().unreadThings();
unreadThings.preloadEnough(entry); unreadThings.preloadEnough(thread);
const auto updateWithCount = [&](Type type, int count) { const auto updateWithCount = [&](Type type, int count) {
updateVisibility( updateVisibility(
@ -240,25 +232,25 @@ void CornerButtons::updateUnreadThingsVisibility() {
(count > 0) && _delegate->cornerButtonsUnreadMayBeShown()); (count > 0) && _delegate->cornerButtonsUnreadMayBeShown());
}; };
if (_delegate->cornerButtonsHas(Type::Mentions) if (_delegate->cornerButtonsHas(Type::Mentions)
&& unreadThings.trackMentions(entry)) { && unreadThings.trackMentions(thread)) {
if (const auto count = entry->unreadMentions().count(0)) { if (const auto count = thread->unreadMentions().count(0)) {
_mentions.widget->setUnreadCount(count); _mentions.widget->setUnreadCount(count);
} }
updateWithCount( updateWithCount(
Type::Mentions, Type::Mentions,
entry->unreadMentions().loadedCount()); thread->unreadMentions().loadedCount());
} else { } else {
updateVisibility(Type::Mentions, false); updateVisibility(Type::Mentions, false);
} }
if (_delegate->cornerButtonsHas(Type::Reactions) if (_delegate->cornerButtonsHas(Type::Reactions)
&& unreadThings.trackReactions(entry)) { && unreadThings.trackReactions(thread)) {
if (const auto count = entry->unreadReactions().count(0)) { if (const auto count = thread->unreadReactions().count(0)) {
_reactions.widget->setUnreadCount(count); _reactions.widget->setUnreadCount(count);
} }
updateWithCount( updateWithCount(
Type::Reactions, Type::Reactions,
entry->unreadReactions().loadedCount()); thread->unreadReactions().loadedCount());
} else { } else {
updateVisibility(Type::Reactions, false); updateVisibility(Type::Reactions, false);
} }
@ -353,8 +345,8 @@ Fn<void(bool found)> CornerButtons::doneJumpFrom(
return [=](bool found) { return [=](bool found) {
skipReplyReturn(targetId); skipReplyReturn(targetId);
if (originId) { if (originId) {
if (const auto entry = _delegate->cornerButtonsEntry()) { if (const auto thread = _delegate->cornerButtonsThread()) {
if (const auto item = entry->owner().message(originId)) { if (const auto item = thread->owner().message(originId)) {
pushReplyReturn(item); pushReplyReturn(item);
} }
} }

View file

@ -22,12 +22,9 @@ class HistoryDownButton;
namespace Data { namespace Data {
struct MessagePosition; struct MessagePosition;
class Thread;
} // namespace Data } // namespace Data
namespace Dialogs {
class Entry;
} // namespace Dialogs
namespace HistoryView { namespace HistoryView {
struct CornerButton { struct CornerButton {
@ -50,7 +47,7 @@ class CornerButtonsDelegate {
public: public:
virtual void cornerButtonsShowAtPosition( virtual void cornerButtonsShowAtPosition(
Data::MessagePosition position) = 0; Data::MessagePosition position) = 0;
[[nodiscard]] virtual Dialogs::Entry *cornerButtonsEntry() = 0; [[nodiscard]] virtual Data::Thread *cornerButtonsThread() = 0;
[[nodiscard]] virtual FullMsgId cornerButtonsCurrentId() = 0; [[nodiscard]] virtual FullMsgId cornerButtonsCurrentId() = 0;
[[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0; [[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0;
[[nodiscard]] virtual std::optional<bool> cornerButtonsDownShown() = 0; [[nodiscard]] virtual std::optional<bool> cornerButtonsDownShown() = 0;

View file

@ -193,7 +193,7 @@ void PinnedWidget::cornerButtonsShowAtPosition(
showAtPosition(position); showAtPosition(position);
} }
Dialogs::Entry *PinnedWidget::cornerButtonsEntry() { Data::Thread *PinnedWidget::cornerButtonsThread() {
return _history; return _history;
} }

View file

@ -123,7 +123,7 @@ public:
// CornerButtonsDelegate delegate. // CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition( void cornerButtonsShowAtPosition(
Data::MessagePosition position) override; Data::MessagePosition position) override;
Dialogs::Entry *cornerButtonsEntry() override; Data::Thread *cornerButtonsThread() override;
FullMsgId cornerButtonsCurrentId() override; FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override; bool cornerButtonsIgnoreVisibility() override;
std::optional<bool> cornerButtonsDownShown() override; std::optional<bool> cornerButtonsDownShown() override;

View file

@ -1387,8 +1387,8 @@ void RepliesWidget::cornerButtonsShowAtPosition(
showAtPosition(position); showAtPosition(position);
} }
Dialogs::Entry *RepliesWidget::cornerButtonsEntry() { Data::Thread *RepliesWidget::cornerButtonsThread() {
return _topic ? static_cast<Dialogs::Entry*>(_topic) : _history; return _topic ? static_cast<Data::Thread*>(_topic) : _history;
} }
FullMsgId RepliesWidget::cornerButtonsCurrentId() { FullMsgId RepliesWidget::cornerButtonsCurrentId() {

View file

@ -161,7 +161,7 @@ public:
// CornerButtonsDelegate delegate. // CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition( void cornerButtonsShowAtPosition(
Data::MessagePosition position) override; Data::MessagePosition position) override;
Dialogs::Entry *cornerButtonsEntry() override; Data::Thread *cornerButtonsThread() override;
FullMsgId cornerButtonsCurrentId() override; FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override; bool cornerButtonsIgnoreVisibility() override;
std::optional<bool> cornerButtonsDownShown() override; std::optional<bool> cornerButtonsDownShown() override;

View file

@ -822,7 +822,7 @@ void ScheduledWidget::cornerButtonsShowAtPosition(
showAtPosition(position); showAtPosition(position);
} }
Dialogs::Entry *ScheduledWidget::cornerButtonsEntry() { Data::Thread *ScheduledWidget::cornerButtonsThread() {
return _history; return _history;
} }

View file

@ -146,7 +146,7 @@ public:
// CornerButtonsDelegate delegate. // CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition( void cornerButtonsShowAtPosition(
Data::MessagePosition position) override; Data::MessagePosition position) override;
Dialogs::Entry *cornerButtonsEntry() override; Data::Thread *cornerButtonsThread() override;
FullMsgId cornerButtonsCurrentId() override; FullMsgId cornerButtonsCurrentId() override;
bool cornerButtonsIgnoreVisibility() override; bool cornerButtonsIgnoreVisibility() override;
std::optional<bool> cornerButtonsDownShown() override; std::optional<bool> cornerButtonsDownShown() override;

View file

@ -152,29 +152,29 @@ void SetupMenuAndShortcuts(
void SetupReadAllMenu( void SetupReadAllMenu(
not_null<Ui::RpWidget*> button, not_null<Ui::RpWidget*> button,
Fn<Dialogs::Entry*()> currentEntry, Fn<Data::Thread*()> currentThread,
const QString &text, const QString &text,
Fn<void(not_null<Dialogs::Entry*>, Fn<void()>)> sendReadRequest) { Fn<void(not_null<Data::Thread*>, Fn<void()>)> sendReadRequest) {
struct State { struct State {
base::unique_qptr<Ui::PopupMenu> menu; base::unique_qptr<Ui::PopupMenu> menu;
base::flat_set<base::weak_ptr<Dialogs::Entry>> sentForEntries; base::flat_set<base::weak_ptr<Data::Thread>> sentForEntries;
}; };
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
const auto showMenu = [=] { const auto showMenu = [=] {
const auto entry = base::make_weak(currentEntry()); const auto thread = base::make_weak(currentThread());
if (!entry) { if (!thread) {
return; return;
} }
state->menu = base::make_unique_q<Ui::PopupMenu>( state->menu = base::make_unique_q<Ui::PopupMenu>(
button, button,
st::popupMenuWithIcons); st::popupMenuWithIcons);
state->menu->addAction(text, [=] { state->menu->addAction(text, [=] {
const auto strong = entry.get(); const auto strong = thread.get();
if (!strong || !state->sentForEntries.emplace(entry).second) { if (!strong || !state->sentForEntries.emplace(thread).second) {
return; return;
} }
sendReadRequest(strong, [=] { sendReadRequest(strong, [=] {
state->sentForEntries.remove(entry); state->sentForEntries.remove(thread);
}); });
}, &st::menuIconMarkRead); }, &st::menuIconMarkRead);
state->menu->popup(QCursor::pos()); state->menu->popup(QCursor::pos());
@ -191,21 +191,19 @@ void SetupReadAllMenu(
void SetupUnreadMentionsMenu( void SetupUnreadMentionsMenu(
not_null<Ui::RpWidget*> button, not_null<Ui::RpWidget*> button,
Fn<Dialogs::Entry*()> currentEntry) { Fn<Data::Thread*()> currentThread) {
const auto text = tr::lng_context_mark_read_mentions_all(tr::now); const auto text = tr::lng_context_mark_read_mentions_all(tr::now);
const auto sendOne = [=]( const auto sendOne = [=](
base::weak_ptr<Dialogs::Entry> weakEntry, base::weak_ptr<Data::Thread> weakThread,
Fn<void()> done, Fn<void()> done,
auto resend) -> void { auto resend) -> void {
const auto entry = weakEntry.get(); const auto thread = weakThread.get();
if (!entry) { if (!thread) {
done(); done();
return; return;
} }
const auto history = entry->asHistory(); const auto peer = thread->owningHistory()->peer;
const auto topic = entry->asTopic(); const auto topic = thread->asTopic();
Assert(history || topic);
const auto peer = (history ? history : topic->history().get())->peer;
const auto rootId = topic ? topic->rootId() : 0; const auto rootId = topic ? topic->rootId() : 0;
using Flag = MTPmessages_ReadMentions::Flag; using Flag = MTPmessages_ReadMentions::Flag;
peer->session().api().request(MTPmessages_ReadMentions( peer->session().api().request(MTPmessages_ReadMentions(
@ -217,7 +215,7 @@ void SetupUnreadMentionsMenu(
peer, peer,
result); result);
if (offset > 0) { if (offset > 0) {
resend(weakEntry, done, resend); resend(weakThread, done, resend);
} else { } else {
done(); done();
peer->owner().history(peer)->clearUnreadMentionsFor(rootId); peer->owner().history(peer)->clearUnreadMentionsFor(rootId);
@ -225,30 +223,28 @@ void SetupUnreadMentionsMenu(
}).fail(done).send(); }).fail(done).send();
}; };
const auto sendRequest = [=]( const auto sendRequest = [=](
not_null<Dialogs::Entry*> entry, not_null<Data::Thread*> thread,
Fn<void()> done) { Fn<void()> done) {
sendOne(base::make_weak(entry.get()), std::move(done), sendOne); sendOne(base::make_weak(thread.get()), std::move(done), sendOne);
}; };
SetupReadAllMenu(button, currentEntry, text, sendRequest); SetupReadAllMenu(button, currentThread, text, sendRequest);
} }
void SetupUnreadReactionsMenu( void SetupUnreadReactionsMenu(
not_null<Ui::RpWidget*> button, not_null<Ui::RpWidget*> button,
Fn<Dialogs::Entry*()> currentEntry) { Fn<Data::Thread*()> currentThread) {
const auto text = tr::lng_context_mark_read_reactions_all(tr::now); const auto text = tr::lng_context_mark_read_reactions_all(tr::now);
const auto sendOne = [=]( const auto sendOne = [=](
base::weak_ptr<Dialogs::Entry> weakEntry, base::weak_ptr<Data::Thread> weakThread,
Fn<void()> done, Fn<void()> done,
auto resend) -> void { auto resend) -> void {
const auto entry = weakEntry.get(); const auto thread = weakThread.get();
if (!entry) { if (!thread) {
done(); done();
return; return;
} }
const auto history = entry->asHistory(); const auto topic = thread->asTopic();
const auto topic = entry->asTopic(); const auto peer = thread->owningHistory()->peer;
Assert(history || topic);
const auto peer = (history ? history : topic->history().get())->peer;
const auto rootId = topic ? topic->rootId() : 0; const auto rootId = topic ? topic->rootId() : 0;
using Flag = MTPmessages_ReadReactions::Flag; using Flag = MTPmessages_ReadReactions::Flag;
peer->session().api().request(MTPmessages_ReadReactions( peer->session().api().request(MTPmessages_ReadReactions(
@ -260,7 +256,7 @@ void SetupUnreadReactionsMenu(
peer, peer,
result); result);
if (offset > 0) { if (offset > 0) {
resend(weakEntry, done, resend); resend(weakThread, done, resend);
} else { } else {
done(); done();
peer->owner().history(peer)->clearUnreadReactionsFor(rootId); peer->owner().history(peer)->clearUnreadReactionsFor(rootId);
@ -268,11 +264,11 @@ void SetupUnreadReactionsMenu(
}).fail(done).send(); }).fail(done).send();
}; };
const auto sendRequest = [=]( const auto sendRequest = [=](
not_null<Dialogs::Entry*> entry, not_null<Data::Thread*> thread,
Fn<void()> done) { Fn<void()> done) {
sendOne(base::make_weak(entry.get()), std::move(done), sendOne); sendOne(base::make_weak(thread.get()), std::move(done), sendOne);
}; };
SetupReadAllMenu(button, currentEntry, text, sendRequest); SetupReadAllMenu(button, currentThread, text, sendRequest);
} }
} // namespace SendMenu } // namespace SendMenu

View file

@ -16,9 +16,9 @@ class PopupMenu;
class RpWidget; class RpWidget;
} // namespace Ui } // namespace Ui
namespace Dialogs { namespace Data {
class Entry; class Thread;
} // namespace Dialogs } // namespace Data
namespace SendMenu { namespace SendMenu {
@ -55,10 +55,10 @@ void SetupMenuAndShortcuts(
void SetupUnreadMentionsMenu( void SetupUnreadMentionsMenu(
not_null<Ui::RpWidget*> button, not_null<Ui::RpWidget*> button,
Fn<Dialogs::Entry*()> currentEntry); Fn<Data::Thread*()> currentThread);
void SetupUnreadReactionsMenu( void SetupUnreadReactionsMenu(
not_null<Ui::RpWidget*> button, not_null<Ui::RpWidget*> button,
Fn<Dialogs::Entry*()> currentEntry); Fn<Data::Thread*()> currentThread);
} // namespace SendMenu } // namespace SendMenu

View file

@ -81,18 +81,18 @@ QString TextWithPermanentSpoiler(const TextWithEntities &textWithEntities) {
struct System::Waiter { struct System::Waiter {
NotificationInHistoryKey key; NotificationInHistoryKey key;
UserData *reactionSender = nullptr; UserData *reactionSender = nullptr;
ItemNotificationType type = ItemNotificationType::Message; Data::ItemNotificationType type = Data::ItemNotificationType::Message;
crl::time when = 0; crl::time when = 0;
}; };
System::NotificationInHistoryKey::NotificationInHistoryKey( System::NotificationInHistoryKey::NotificationInHistoryKey(
ItemNotification notification) Data::ItemNotification notification)
: NotificationInHistoryKey(notification.item->id, notification.type) { : NotificationInHistoryKey(notification.item->id, notification.type) {
} }
System::NotificationInHistoryKey::NotificationInHistoryKey( System::NotificationInHistoryKey::NotificationInHistoryKey(
MsgId messageId, MsgId messageId,
ItemNotificationType type) Data::ItemNotificationType type)
: messageId(messageId) : messageId(messageId)
, type(type) { , type(type) {
} }
@ -161,13 +161,13 @@ bool System::skipReactionNotification(not_null<HistoryItem*> item) const {
} }
System::SkipState System::skipNotification( System::SkipState System::skipNotification(
ItemNotification notification) const { Data::ItemNotification notification) const {
const auto item = notification.item; const auto item = notification.item;
const auto type = notification.type; const auto type = notification.type;
const auto messageNotification = (type == ItemNotificationType::Message); const auto messageType = (type == Data::ItemNotificationType::Message);
if (!item->history()->currentNotification() if (!item->thread()->currentNotification()
|| (messageNotification && item->skipNotification()) || (messageType && item->skipNotification())
|| (type == ItemNotificationType::Reaction || (type == Data::ItemNotificationType::Reaction
&& skipReactionNotification(item))) { && skipReactionNotification(item))) {
return { SkipState::Skip }; return { SkipState::Skip };
} }
@ -175,27 +175,27 @@ System::SkipState System::skipNotification(
} }
System::SkipState System::computeSkipState( System::SkipState System::computeSkipState(
ItemNotification notification) const { Data::ItemNotification notification) const {
const auto type = notification.type; const auto type = notification.type;
const auto item = notification.item; const auto item = notification.item;
const auto history = item->history(); const auto history = item->history();
const auto messageNotification = (type == ItemNotificationType::Message); const auto messageType = (type == Data::ItemNotificationType::Message);
const auto withSilent = [&]( const auto withSilent = [&](
SkipState::Value value, SkipState::Value value,
bool forceSilent = false) { bool forceSilent = false) {
return SkipState{ return SkipState{
.value = value, .value = value,
.silent = (forceSilent .silent = (forceSilent
|| !messageNotification || !messageType
|| item->isSilent() || item->isSilent()
|| history->owner().notifySettings().sound( || history->owner().notifySettings().sound(
history->peer).none), history->peer).none),
}; };
}; };
const auto showForMuted = messageNotification const auto showForMuted = messageType
&& item->out() && item->out()
&& item->isFromScheduled(); && item->isFromScheduled();
const auto notifyBy = messageNotification const auto notifyBy = messageType
? item->specialNotificationPeer() ? item->specialNotificationPeer()
: notification.reactionSender; : notification.reactionSender;
if (Core::Quitting()) { if (Core::Quitting()) {
@ -205,7 +205,7 @@ System::SkipState System::computeSkipState(
return { SkipState::Skip }; return { SkipState::Skip };
} }
if (messageNotification) { if (messageType) {
history->owner().notifySettings().request( history->owner().notifySettings().request(
history->peer); history->peer);
} else if (notifyBy->blockStatus() == PeerData::BlockStatus::Unknown) { } else if (notifyBy->blockStatus() == PeerData::BlockStatus::Unknown) {
@ -215,10 +215,10 @@ System::SkipState System::computeSkipState(
history->owner().notifySettings().request(notifyBy); history->owner().notifySettings().request(notifyBy);
} }
if (messageNotification if (messageType
&& history->owner().notifySettings().muteUnknown(history->peer)) { && history->owner().notifySettings().muteUnknown(history->peer)) {
return { SkipState::Unknown }; return { SkipState::Unknown };
} else if (messageNotification } else if (messageType
&& !history->owner().notifySettings().isMuted(history->peer)) { && !history->owner().notifySettings().isMuted(history->peer)) {
return withSilent(SkipState::DontSkip); return withSilent(SkipState::DontSkip);
} else if (!notifyBy) { } else if (!notifyBy) {
@ -226,11 +226,11 @@ System::SkipState System::computeSkipState(
showForMuted ? SkipState::DontSkip : SkipState::Skip, showForMuted ? SkipState::DontSkip : SkipState::Skip,
showForMuted); showForMuted);
} else if (history->owner().notifySettings().muteUnknown(notifyBy) } else if (history->owner().notifySettings().muteUnknown(notifyBy)
|| (!messageNotification || (!messageType
&& notifyBy->blockStatus() == PeerData::BlockStatus::Unknown)) { && notifyBy->blockStatus() == PeerData::BlockStatus::Unknown)) {
return withSilent(SkipState::Unknown); return withSilent(SkipState::Unknown);
} else if (!history->owner().notifySettings().isMuted(notifyBy) } else if (!history->owner().notifySettings().isMuted(notifyBy)
&& (messageNotification || !notifyBy->isBlocked())) { && (messageType || !notifyBy->isBlocked())) {
return withSilent(SkipState::DontSkip); return withSilent(SkipState::DontSkip);
} else { } else {
return withSilent( return withSilent(
@ -261,7 +261,7 @@ System::Timing System::countTiming(
}; };
} }
void System::schedule(ItemNotification notification) { void System::schedule(Data::ItemNotification notification) {
Expects(_manager != nullptr); Expects(_manager != nullptr);
const auto item = notification.item; const auto item = notification.item;
@ -275,13 +275,13 @@ void System::schedule(ItemNotification notification) {
const auto ready = (skip.value != SkipState::Unknown) const auto ready = (skip.value != SkipState::Unknown)
&& item->notificationReady(); && item->notificationReady();
const auto minimalDelay = (type == ItemNotificationType::Reaction) const auto minimalDelay = (type == Data::ItemNotificationType::Reaction)
? kMinimalDelay ? kMinimalDelay
: item->Has<HistoryMessageForwarded>() : item->Has<HistoryMessageForwarded>()
? kMinimalForwardDelay ? kMinimalForwardDelay
: kMinimalDelay; : kMinimalDelay;
const auto timing = countTiming(history, minimalDelay); const auto timing = countTiming(history, minimalDelay);
const auto notifyBy = (type == ItemNotificationType::Message) const auto notifyBy = (type == Data::ItemNotificationType::Message)
? item->specialNotificationPeer() ? item->specialNotificationPeer()
: notification.reactionSender; : notification.reactionSender;
if (!skip.silent) { if (!skip.silent) {
@ -320,8 +320,8 @@ void System::clearAll() {
_manager->clearAll(); _manager->clearAll();
} }
for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) { for (const auto &[thread, _] : _whenMaps) {
i->first->clearNotifications(); thread->clearNotifications();
} }
_whenMaps.clear(); _whenMaps.clear();
_whenAlerts.clear(); _whenAlerts.clear();
@ -334,52 +334,33 @@ void System::clearFromTopic(not_null<Data::ForumTopic*> topic) {
_manager->clearFromTopic(topic); _manager->clearFromTopic(topic);
} }
// #TODO forum notifications topic->clearNotifications();
//topic->clearNotifications(); _whenMaps.remove(topic);
//_whenMaps.remove(topic); _whenAlerts.remove(topic);
//_whenAlerts.remove(topic); _waiters.remove(topic);
//_waiters.remove(topic); _settingWaiters.remove(topic);
//_settingWaiters.remove(topic);
_waitTimer.cancel(); _waitTimer.cancel();
showNext(); showNext();
} }
void System::clearFromHistory(not_null<History*> history) { void System::clearForThreadIf(Fn<bool(not_null<Data::Thread*>)> predicate) {
if (_manager) {
_manager->clearFromHistory(history);
}
history->clearNotifications();
_whenMaps.remove(history);
_whenAlerts.remove(history);
_waiters.remove(history);
_settingWaiters.remove(history);
_waitTimer.cancel();
showNext();
}
void System::clearFromSession(not_null<Main::Session*> session) {
if (_manager) {
_manager->clearFromSession(session);
}
for (auto i = _whenMaps.begin(); i != _whenMaps.end();) { for (auto i = _whenMaps.begin(); i != _whenMaps.end();) {
const auto history = i->first; const auto thread = i->first;
if (&history->session() != session) { if (!predicate(thread)) {
++i; ++i;
continue; continue;
} }
history->clearNotifications();
i = _whenMaps.erase(i); i = _whenMaps.erase(i);
_whenAlerts.remove(history);
_waiters.remove(history); thread->clearNotifications();
_settingWaiters.remove(history); _whenAlerts.remove(thread);
_waiters.remove(thread);
_settingWaiters.remove(thread);
} }
const auto clearFrom = [&](auto &map) { const auto clearFrom = [&](auto &map) {
for (auto i = map.begin(); i != map.end();) { for (auto i = map.begin(); i != map.end();) {
if (&i->first->session() == session) { if (predicate(i->first)) {
i = map.erase(i); i = map.erase(i);
} else { } else {
++i; ++i;
@ -389,6 +370,27 @@ void System::clearFromSession(not_null<Main::Session*> session) {
clearFrom(_whenAlerts); clearFrom(_whenAlerts);
clearFrom(_waiters); clearFrom(_waiters);
clearFrom(_settingWaiters); clearFrom(_settingWaiters);
_waitTimer.cancel();
showNext();
}
void System::clearFromHistory(not_null<History*> history) {
if (_manager) {
_manager->clearFromHistory(history);
}
clearForThreadIf([&](not_null<Data::Thread*> thread) {
return (thread->owningHistory() == history);
});
}
void System::clearFromSession(not_null<Main::Session*> session) {
if (_manager) {
_manager->clearFromSession(session);
}
clearForThreadIf([&](not_null<Data::Thread*> thread) {
return (&thread->session() == session);
});
} }
void System::clearIncomingFromHistory(not_null<History*> history) { void System::clearIncomingFromHistory(not_null<History*> history) {
@ -403,9 +405,8 @@ void System::clearIncomingFromTopic(not_null<Data::ForumTopic*> topic) {
if (_manager) { if (_manager) {
_manager->clearFromTopic(topic); _manager->clearFromTopic(topic);
} }
// #TODO forum notifications topic->clearIncomingNotifications();
//topic->clearIncomingNotifications(); _whenAlerts.remove(topic);
//_whenAlerts.remove(topic);
} }
void System::clearFromItem(not_null<HistoryItem*> item) { void System::clearFromItem(not_null<HistoryItem*> item) {
@ -428,10 +429,10 @@ void System::clearAllFast() {
void System::checkDelayed() { void System::checkDelayed() {
for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) { for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) {
const auto remove = [&] { const auto remove = [&] {
const auto history = i->first; const auto thread = i->first;
const auto peer = history->peer; const auto peer = thread->owningHistory()->peer;
const auto fullId = FullMsgId(peer->id, i->second.key.messageId); const auto fullId = FullMsgId(peer->id, i->second.key.messageId);
const auto item = peer->owner().message(fullId); const auto item = thread->owner().message(fullId);
if (!item) { if (!item) {
return true; return true;
} }
@ -503,7 +504,7 @@ void System::showNext() {
auto alertPeer = (PeerData*)nullptr; auto alertPeer = (PeerData*)nullptr;
for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) { for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) {
while (!i->second.empty() && i->second.begin()->first <= ms) { while (!i->second.empty() && i->second.begin()->first <= ms) {
const auto peer = i->first->peer; const auto peer = i->first->owningHistory()->peer;
const auto &notifySettings = peer->owner().notifySettings(); const auto &notifySettings = peer->owner().notifySettings();
const auto peerUnknown = notifySettings.muteUnknown(peer); const auto peerUnknown = notifySettings.muteUnknown(peer);
const auto peerAlert = !peerUnknown const auto peerAlert = !peerUnknown
@ -559,15 +560,15 @@ void System::showNext() {
while (true) { while (true) {
auto next = 0LL; auto next = 0LL;
auto notify = std::optional<ItemNotification>(); auto notify = std::optional<Data::ItemNotification>();
auto notifyHistory = (History*)nullptr; auto notifyThread = (Data::Thread*)nullptr;
for (auto i = _waiters.begin(); i != _waiters.end();) { for (auto i = _waiters.begin(); i != _waiters.end();) {
const auto history = i->first; const auto thread = i->first;
auto current = history->currentNotification(); auto current = thread->currentNotification();
if (current && current->item->id != i->second.key.messageId) { if (current && current->item->id != i->second.key.messageId) {
auto j = _whenMaps.find(history); auto j = _whenMaps.find(thread);
if (j == _whenMaps.end()) { if (j == _whenMaps.end()) {
history->clearNotifications(); thread->clearNotifications();
i = _waiters.erase(i); i = _waiters.erase(i);
continue; continue;
} }
@ -578,12 +579,12 @@ void System::showNext() {
i->second.when = k->second; i->second.when = k->second;
break; break;
} }
history->skipNotification(); thread->skipNotification();
current = history->currentNotification(); current = thread->currentNotification();
} while (current); } while (current);
} }
if (!current) { if (!current) {
_whenMaps.remove(history); _whenMaps.remove(thread);
i = _waiters.erase(i); i = _waiters.erase(i);
continue; continue;
} }
@ -591,7 +592,7 @@ void System::showNext() {
if (!notify || next > when) { if (!notify || next > when) {
next = when; next = when;
notify = current, notify = current,
notifyHistory = history; notifyThread = thread;
} }
++i; ++i;
} }
@ -606,11 +607,11 @@ void System::showNext() {
break; break;
} }
const auto notifyItem = notify->item; const auto notifyItem = notify->item;
const auto messageNotification = (notify->type const auto messageType = (notify->type
== ItemNotificationType::Message); == Data::ItemNotificationType::Message);
const auto isForwarded = messageNotification const auto isForwarded = messageType
&& notifyItem->Has<HistoryMessageForwarded>(); && notifyItem->Has<HistoryMessageForwarded>();
const auto isAlbum = messageNotification const auto isAlbum = messageType
&& notifyItem->groupId(); && notifyItem->groupId();
// Forwarded and album notify grouping. // Forwarded and album notify grouping.
@ -619,15 +620,15 @@ void System::showNext() {
: nullptr; : nullptr;
auto forwardedCount = isForwarded ? 1 : 0; auto forwardedCount = isForwarded ? 1 : 0;
const auto history = notifyItem->history(); const auto thread = notifyItem->thread();
const auto j = _whenMaps.find(history); const auto j = _whenMaps.find(thread);
if (j == _whenMaps.cend()) { if (j == _whenMaps.cend()) {
history->clearNotifications(); thread->clearNotifications();
} else { } else {
while (true) { while (true) {
auto nextNotify = std::optional<ItemNotification>(); auto nextNotify = std::optional<Data::ItemNotification>();
history->skipNotification(); thread->skipNotification();
if (!history->hasNotification()) { if (!thread->hasNotification()) {
break; break;
} }
@ -637,25 +638,26 @@ void System::showNext() {
}); });
do { do {
const auto k = j->second.find( const auto k = j->second.find(
history->currentNotification()); thread->currentNotification());
if (k != j->second.cend()) { if (k != j->second.cend()) {
nextNotify = history->currentNotification(); nextNotify = thread->currentNotification();
_waiters.emplace(notifyHistory, Waiter{ _waiters.emplace(notifyThread, Waiter{
.key = k->first, .key = k->first,
.when = k->second .when = k->second
}); });
break; break;
} }
history->skipNotification(); thread->skipNotification();
} while (history->hasNotification()); } while (thread->hasNotification());
if (!nextNotify || !groupedItem) { if (!nextNotify || !groupedItem) {
break; break;
} }
const auto nextMessageNotification const auto nextMessageNotification
= (nextNotify->type = (nextNotify->type
== ItemNotificationType::Message); == Data::ItemNotificationType::Message);
const auto canNextBeGrouped = nextMessageNotification const auto canNextBeGrouped = nextMessageNotification
&& ((isForwarded && nextNotify->item->Has<HistoryMessageForwarded>()) && ((isForwarded
&& nextNotify->item->Has<HistoryMessageForwarded>())
|| (isAlbum && nextNotify->item->groupId())); || (isAlbum && nextNotify->item->groupId()));
const auto nextItem = canNextBeGrouped const auto nextItem = canNextBeGrouped
? nextNotify->item.get() ? nextNotify->item.get()
@ -706,7 +708,7 @@ void System::showNext() {
// to show the previous notification. // to show the previous notification.
showGrouped(); showGrouped();
const auto reactionNotification const auto reactionNotification
= (notify->type == ItemNotificationType::Reaction); = (notify->type == Data::ItemNotificationType::Reaction);
const auto reaction = reactionNotification const auto reaction = reactionNotification
? notify->item->lookupUnreadReaction(notify->reactionSender) ? notify->item->lookupUnreadReaction(notify->reactionSender)
: Data::ReactionId(); : Data::ReactionId();
@ -720,9 +722,9 @@ void System::showNext() {
} }
} }
if (!history->hasNotification()) { if (!thread->hasNotification()) {
_waiters.remove(history); _waiters.remove(thread);
_whenMaps.remove(history); _whenMaps.remove(thread);
} }
} }
if (nextAlert) { if (nextAlert) {
@ -785,7 +787,7 @@ void System::playSound(not_null<Main::Session*> session, DocumentId id) {
Manager::DisplayOptions Manager::getNotificationOptions( Manager::DisplayOptions Manager::getNotificationOptions(
HistoryItem *item, HistoryItem *item,
ItemNotificationType type) const { Data::ItemNotificationType type) const {
const auto hideEverything = Core::App().passcodeLocked() const auto hideEverything = Core::App().passcodeLocked()
|| forceHideDetails(); || forceHideDetails();
const auto view = Core::App().settings().notifyView(); const auto view = Core::App().settings().notifyView();
@ -796,7 +798,7 @@ Manager::DisplayOptions Manager::getNotificationOptions(
result.hideMessageText = hideEverything result.hideMessageText = hideEverything
|| (view > Core::Settings::NotifyView::ShowPreview); || (view > Core::Settings::NotifyView::ShowPreview);
result.hideMarkAsRead = result.hideMessageText result.hideMarkAsRead = result.hideMessageText
|| (type != ItemNotificationType::Message) || (type != Data::ItemNotificationType::Message)
|| !item || !item
|| ((item->out() || item->history()->peer->isSelf()) || ((item->out() || item->history()->peer->isSelf())
&& item->isFromScheduled()); && item->isFromScheduled());
@ -1054,8 +1056,8 @@ void NativeManager::doShowNotification(NotificationFields &&fields) {
const auto options = getNotificationOptions( const auto options = getNotificationOptions(
fields.item, fields.item,
(fields.reactionFrom (fields.reactionFrom
? ItemNotificationType::Reaction ? Data::ItemNotificationType::Reaction
: ItemNotificationType::Message)); : Data::ItemNotificationType::Message));
const auto item = fields.item; const auto item = fields.item;
const auto peer = item->history()->peer; const auto peer = item->history()->peer;
const auto reactionFrom = fields.reactionFrom; const auto reactionFrom = fields.reactionFrom;

View file

@ -12,13 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h" #include "base/timer.h"
class History; class History;
struct ItemNotification;
enum class ItemNotificationType;
namespace Data { namespace Data {
class Session; class Session;
class CloudImageView; class CloudImageView;
class ForumTopic; class ForumTopic;
class Thread;
struct ItemNotification;
enum class ItemNotificationType;
} // namespace Data } // namespace Data
namespace Main { namespace Main {
@ -31,17 +32,15 @@ class Manager;
} // namespace Notifications } // namespace Notifications
} // namespace Platform } // namespace Platform
namespace Media { namespace Media::Audio {
namespace Audio {
class Track; class Track;
} // namespace Audio } // namespace Media::Audio
} // namespace Media
namespace Window { namespace Window {
class SessionController; class SessionController;
} // namespace Window
namespace Notifications { namespace Window::Notifications {
enum class ManagerType { enum class ManagerType {
Dummy, Dummy,
@ -62,19 +61,17 @@ enum class ChangeType {
DemoIsHidden, DemoIsHidden,
}; };
} // namespace Notifications } // namespace Window::Notifications
} // namespace Window
namespace base { namespace base {
template <> template <>
struct custom_is_fast_copy_type<Window::Notifications::ChangeType> : public std::true_type { struct custom_is_fast_copy_type<Window::Notifications::ChangeType> : std::true_type {
}; };
} // namespace base } // namespace base
namespace Window { namespace Window::Notifications {
namespace Notifications {
class Manager; class Manager;
@ -90,7 +87,7 @@ public:
[[nodiscard]] ManagerType managerType() const; [[nodiscard]] ManagerType managerType() const;
void checkDelayed(); void checkDelayed();
void schedule(ItemNotification notification); void schedule(Data::ItemNotification notification);
void clearFromTopic(not_null<Data::ForumTopic*> topic); void clearFromTopic(not_null<Data::ForumTopic*> topic);
void clearFromHistory(not_null<History*> history); void clearFromHistory(not_null<History*> history);
void clearIncomingFromTopic(not_null<Data::ForumTopic*> topic); void clearIncomingFromTopic(not_null<Data::ForumTopic*> topic);
@ -123,18 +120,17 @@ private:
bool silent = false; bool silent = false;
}; };
struct NotificationInHistoryKey { struct NotificationInHistoryKey {
NotificationInHistoryKey(ItemNotification notification); NotificationInHistoryKey(Data::ItemNotification notification);
NotificationInHistoryKey(MsgId messageId, ItemNotificationType type); NotificationInHistoryKey(
MsgId messageId,
Data::ItemNotificationType type);
MsgId messageId = 0; MsgId messageId = 0;
ItemNotificationType type = ItemNotificationType(); Data::ItemNotificationType type = Data::ItemNotificationType();
friend inline bool operator<( friend inline auto operator<=>(
NotificationInHistoryKey a, NotificationInHistoryKey a,
NotificationInHistoryKey b) { NotificationInHistoryKey b) = default;
return std::pair(a.messageId, a.type)
< std::pair(b.messageId, b.type);
}
}; };
struct Timing { struct Timing {
crl::time delay = 0; crl::time delay = 0;
@ -152,10 +148,12 @@ private:
} }
}; };
void clearForThreadIf(Fn<bool(not_null<Data::Thread*>)> predicate);
[[nodiscard]] SkipState skipNotification( [[nodiscard]] SkipState skipNotification(
ItemNotification notification) const; Data::ItemNotification notification) const;
[[nodiscard]] SkipState computeSkipState( [[nodiscard]] SkipState computeSkipState(
ItemNotification notification) const; Data::ItemNotification notification) const;
[[nodiscard]] Timing countTiming( [[nodiscard]] Timing countTiming(
not_null<History*> history, not_null<History*> history,
crl::time minimalDelay) const; crl::time minimalDelay) const;
@ -170,16 +168,16 @@ private:
DocumentId id); DocumentId id);
base::flat_map< base::flat_map<
not_null<History*>, not_null<Data::Thread*>,
base::flat_map<NotificationInHistoryKey, crl::time>> _whenMaps; base::flat_map<NotificationInHistoryKey, crl::time>> _whenMaps;
base::flat_map<not_null<History*>, Waiter> _waiters; base::flat_map<not_null<Data::Thread*>, Waiter> _waiters;
base::flat_map<not_null<History*>, Waiter> _settingWaiters; base::flat_map<not_null<Data::Thread*>, Waiter> _settingWaiters;
base::Timer _waitTimer; base::Timer _waitTimer;
base::Timer _waitForAllGroupedTimer; base::Timer _waitForAllGroupedTimer;
base::flat_map< base::flat_map<
not_null<History*>, not_null<Data::Thread*>,
base::flat_map<crl::time, PeerData*>> _whenAlerts; base::flat_map<crl::time, PeerData*>> _whenAlerts;
mutable base::flat_map< mutable base::flat_map<
@ -270,7 +268,7 @@ public:
}; };
[[nodiscard]] DisplayOptions getNotificationOptions( [[nodiscard]] DisplayOptions getNotificationOptions(
HistoryItem *item, HistoryItem *item,
ItemNotificationType type) const; Data::ItemNotificationType type) const;
[[nodiscard]] static TextWithEntities ComposeReactionEmoji( [[nodiscard]] static TextWithEntities ComposeReactionEmoji(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const Data::ReactionId &reaction); const Data::ReactionId &reaction);
@ -405,7 +403,6 @@ protected:
}; };
QString WrapFromScheduled(const QString &text); [[nodiscard]] QString WrapFromScheduled(const QString &text);
} // namespace Notifications } // namespace Window::Notifications
} // namespace Window

View file

@ -823,8 +823,8 @@ void Notification::updateNotifyDisplay() {
const auto options = manager()->getNotificationOptions( const auto options = manager()->getNotificationOptions(
_item, _item,
(_reaction.empty() (_reaction.empty()
? ItemNotificationType::Message ? Data::ItemNotificationType::Message
: ItemNotificationType::Reaction)); : Data::ItemNotificationType::Reaction));
_hideReplyButton = options.hideReplyButton; _hideReplyButton = options.hideReplyButton;
int32 w = width(), h = height(); int32 w = width(), h = height();
@ -1118,7 +1118,9 @@ bool Notification::unlinkSession(not_null<Main::Session*> session) {
} }
void Notification::enterEventHook(QEnterEvent *e) { void Notification::enterEventHook(QEnterEvent *e) {
if (!_history) return; if (!_history) {
return;
}
manager()->stopAllHiding(); manager()->stopAllHiding();
if (!_replyArea && canReply()) { if (!_replyArea && canReply()) {
toggleActionButtons(true); toggleActionButtons(true);
@ -1126,7 +1128,9 @@ void Notification::enterEventHook(QEnterEvent *e) {
} }
void Notification::leaveEventHook(QEvent *e) { void Notification::leaveEventHook(QEvent *e) {
if (!_history) return; if (!_history) {
return;
}
manager()->startAllHiding(); manager()->startAllHiding();
toggleActionButtons(false); toggleActionButtons(false);
} }