mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 21:27:07 +02:00
Extract Data::Thread common for History / ForumTopic.
This commit is contained in:
parent
9fccdf21cc
commit
b8bdca8921
38 changed files with 675 additions and 627 deletions
|
@ -24,39 +24,27 @@ constexpr auto kPreloadIfLess = 5;
|
|||
constexpr auto kFirstRequestLimit = 10;
|
||||
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
|
||||
|
||||
UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
|
||||
}
|
||||
|
||||
bool UnreadThings::trackMentions(DialogsEntry *entry) const {
|
||||
const auto peer = entry ? ResolveHistory(entry)->peer.get() : nullptr;
|
||||
bool UnreadThings::trackMentions(Data::Thread *thread) const {
|
||||
const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr;
|
||||
return peer && (peer->isChat() || peer->isMegagroup());
|
||||
}
|
||||
|
||||
bool UnreadThings::trackReactions(DialogsEntry *entry) const {
|
||||
const auto peer = entry ? ResolveHistory(entry)->peer.get() : nullptr;
|
||||
bool UnreadThings::trackReactions(Data::Thread *thread) const {
|
||||
const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr;
|
||||
return peer && (peer->isChat() || peer->isMegagroup());
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnough(DialogsEntry *entry) {
|
||||
if (trackMentions(entry)) {
|
||||
preloadEnoughMentions(entry);
|
||||
void UnreadThings::preloadEnough(Data::Thread *thread) {
|
||||
if (trackMentions(thread)) {
|
||||
preloadEnoughMentions(thread);
|
||||
}
|
||||
if (trackReactions(entry)) {
|
||||
preloadEnoughReactions(entry);
|
||||
if (trackReactions(thread)) {
|
||||
preloadEnoughReactions(thread);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,48 +63,48 @@ void UnreadThings::mediaAndMentionsRead(
|
|||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughMentions(not_null<DialogsEntry*> entry) {
|
||||
const auto fullCount = entry->unreadMentions().count();
|
||||
const auto loadedCount = entry->unreadMentions().loadedCount();
|
||||
void UnreadThings::preloadEnoughMentions(not_null<Data::Thread*> thread) {
|
||||
const auto fullCount = thread->unreadMentions().count();
|
||||
const auto loadedCount = thread->unreadMentions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestMentions(entry, loadedCount);
|
||||
requestMentions(thread, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughReactions(not_null<DialogsEntry*> entry) {
|
||||
const auto fullCount = entry->unreadReactions().count();
|
||||
const auto loadedCount = entry->unreadReactions().loadedCount();
|
||||
void UnreadThings::preloadEnoughReactions(not_null<Data::Thread*> thread) {
|
||||
const auto fullCount = thread->unreadReactions().count();
|
||||
const auto loadedCount = thread->unreadReactions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestReactions(entry, loadedCount);
|
||||
requestReactions(thread, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::cancelRequests(not_null<DialogsEntry*> entry) {
|
||||
if (const auto requestId = _mentionsRequests.take(entry)) {
|
||||
void UnreadThings::cancelRequests(not_null<Data::Thread*> thread) {
|
||||
if (const auto requestId = _mentionsRequests.take(thread)) {
|
||||
_api->request(*requestId).cancel();
|
||||
}
|
||||
if (const auto requestId = _reactionsRequests.take(entry)) {
|
||||
if (const auto requestId = _reactionsRequests.take(thread)) {
|
||||
_api->request(*requestId).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::requestMentions(
|
||||
not_null<DialogsEntry*> entry,
|
||||
not_null<Data::Thread*> thread,
|
||||
int loaded) {
|
||||
if (_mentionsRequests.contains(entry)) {
|
||||
if (_mentionsRequests.contains(thread)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = std::max(
|
||||
entry->unreadMentions().maxLoaded(),
|
||||
thread->unreadMentions().maxLoaded(),
|
||||
MsgId(1));
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto history = ResolveHistory(entry);
|
||||
const auto topic = entry->asTopic();
|
||||
const auto history = thread->owningHistory();
|
||||
const auto topic = thread->asTopic();
|
||||
using Flag = MTPmessages_GetUnreadMentions::Flag;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadMentions(
|
||||
MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
|
||||
|
@ -128,29 +116,29 @@ void UnreadThings::requestMentions(
|
|||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_mentionsRequests.remove(entry);
|
||||
entry->unreadMentions().addSlice(result, loaded);
|
||||
_mentionsRequests.remove(thread);
|
||||
thread->unreadMentions().addSlice(result, loaded);
|
||||
}).fail([=] {
|
||||
_mentionsRequests.remove(entry);
|
||||
_mentionsRequests.remove(thread);
|
||||
}).send();
|
||||
_mentionsRequests.emplace(entry, requestId);
|
||||
_mentionsRequests.emplace(thread, requestId);
|
||||
}
|
||||
|
||||
void UnreadThings::requestReactions(
|
||||
not_null<DialogsEntry*> entry,
|
||||
not_null<Data::Thread*> thread,
|
||||
int loaded) {
|
||||
if (_reactionsRequests.contains(entry)) {
|
||||
if (_reactionsRequests.contains(thread)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = loaded
|
||||
? std::max(entry->unreadReactions().maxLoaded(), MsgId(1))
|
||||
? std::max(thread->unreadReactions().maxLoaded(), MsgId(1))
|
||||
: MsgId(1);
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto history = ResolveHistory(entry);
|
||||
const auto topic = entry->asTopic();
|
||||
const auto history = thread->owningHistory();
|
||||
const auto topic = thread->asTopic();
|
||||
using Flag = MTPmessages_GetUnreadReactions::Flag;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
|
||||
MTP_flags(topic ? Flag::f_top_msg_id : Flag()),
|
||||
|
@ -162,12 +150,12 @@ void UnreadThings::requestReactions(
|
|||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_reactionsRequests.remove(entry);
|
||||
entry->unreadReactions().addSlice(result, loaded);
|
||||
_reactionsRequests.remove(thread);
|
||||
thread->unreadReactions().addSlice(result, loaded);
|
||||
}).fail([=] {
|
||||
_reactionsRequests.remove(entry);
|
||||
_reactionsRequests.remove(thread);
|
||||
}).send();
|
||||
_reactionsRequests.emplace(entry, requestId);
|
||||
_reactionsRequests.emplace(thread, requestId);
|
||||
}
|
||||
|
||||
} // namespace UnreadThings
|
||||
|
|
|
@ -11,40 +11,38 @@ class ApiWrap;
|
|||
class PeerData;
|
||||
class ChannelData;
|
||||
|
||||
namespace Dialogs {
|
||||
class Entry;
|
||||
} // namespace Dialogs
|
||||
namespace Data {
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
class UnreadThings final {
|
||||
public:
|
||||
using DialogsEntry = Dialogs::Entry;
|
||||
|
||||
explicit UnreadThings(not_null<ApiWrap*> api);
|
||||
|
||||
[[nodiscard]] bool trackMentions(DialogsEntry *entry) const;
|
||||
[[nodiscard]] bool trackReactions(DialogsEntry *entry) const;
|
||||
[[nodiscard]] bool trackMentions(Data::Thread *thread) const;
|
||||
[[nodiscard]] bool trackReactions(Data::Thread *thread) const;
|
||||
|
||||
void preloadEnough(DialogsEntry *entry);
|
||||
void preloadEnough(Data::Thread *thread);
|
||||
|
||||
void mediaAndMentionsRead(
|
||||
const base::flat_set<MsgId> &readIds,
|
||||
ChannelData *channel = nullptr);
|
||||
|
||||
void cancelRequests(not_null<DialogsEntry*> entry);
|
||||
void cancelRequests(not_null<Data::Thread*> thread);
|
||||
|
||||
private:
|
||||
void preloadEnoughMentions(not_null<DialogsEntry*> entry);
|
||||
void preloadEnoughReactions(not_null<DialogsEntry*> entry);
|
||||
void preloadEnoughMentions(not_null<Data::Thread*> thread);
|
||||
void preloadEnoughReactions(not_null<Data::Thread*> thread);
|
||||
|
||||
void requestMentions(not_null<DialogsEntry*> entry, int loaded);
|
||||
void requestReactions(not_null<DialogsEntry*> entry, int loaded);
|
||||
void requestMentions(not_null<Data::Thread*> thread, int loaded);
|
||||
void requestReactions(not_null<Data::Thread*> thread, int loaded);
|
||||
|
||||
const not_null<ApiWrap*> _api;
|
||||
|
||||
base::flat_map<not_null<DialogsEntry*>, mtpRequestId> _mentionsRequests;
|
||||
base::flat_map<not_null<DialogsEntry*>, mtpRequestId> _reactionsRequests;
|
||||
base::flat_map<not_null<Data::Thread*>, mtpRequestId> _mentionsRequests;
|
||||
base::flat_map<not_null<Data::Thread*>, mtpRequestId> _reactionsRequests;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ void Forum::applyReceivedTopics(
|
|||
const auto raw = creating
|
||||
? _topics.emplace(
|
||||
rootId,
|
||||
std::make_unique<ForumTopic>(_history, rootId)
|
||||
std::make_unique<ForumTopic>(this, rootId)
|
||||
).first->second.get()
|
||||
: i->second.get();
|
||||
raw->applyTopic(topic);
|
||||
|
@ -194,7 +194,7 @@ void Forum::applyTopicAdded(
|
|||
? i->second.get()
|
||||
: _topics.emplace(
|
||||
rootId,
|
||||
std::make_unique<ForumTopic>(_history, rootId)
|
||||
std::make_unique<ForumTopic>(this, rootId)
|
||||
).first->second.get();
|
||||
raw->applyTitle(title);
|
||||
raw->applyColorId(colorId);
|
||||
|
@ -262,11 +262,13 @@ void Forum::clearAllUnreadReactions() {
|
|||
}
|
||||
|
||||
ForumTopic *Forum::topicFor(not_null<const HistoryItem*> item) {
|
||||
const auto maybe = topicFor(item->replyToTop());
|
||||
return maybe ? maybe : topicFor(item->topicRootId());
|
||||
return topicFor(item->topicRootId());
|
||||
}
|
||||
|
||||
ForumTopic *Forum::topicFor(MsgId rootId) {
|
||||
if (!rootId) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto i = _topics.find(rootId);
|
||||
return (i != end(_topics)) ? i->second.get() : nullptr;
|
||||
}
|
||||
|
|
|
@ -137,13 +137,14 @@ QImage ForumTopicIconFrame(
|
|||
return background;
|
||||
}
|
||||
|
||||
ForumTopic::ForumTopic(not_null<History*> history, MsgId rootId)
|
||||
: Entry(&history->owner(), Type::ForumTopic)
|
||||
, _forum(history->peer->forum())
|
||||
ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
||||
: Thread(&forum->history()->owner(), Type::ForumTopic)
|
||||
, _forum(forum)
|
||||
, _list(_forum->topicsList())
|
||||
, _replies(std::make_shared<RepliesList>(history, rootId))
|
||||
, _rootId(rootId)
|
||||
, _flags(owner().notifySettings().isMuted(this) ? Flag::Muted : Flag(0)) {
|
||||
, _replies(std::make_shared<RepliesList>(history(), rootId))
|
||||
, _rootId(rootId) {
|
||||
Thread::setMuted(owner().notifySettings().isMuted(this));
|
||||
|
||||
_replies->unreadCountValue(
|
||||
) | rpl::combine_previous(
|
||||
) | rpl::filter([=] {
|
||||
|
@ -486,13 +487,9 @@ int ForumTopic::unreadCountForBadge() const {
|
|||
return (!result && unreadMark()) ? 1 : result;
|
||||
}
|
||||
|
||||
bool ForumTopic::muted() const {
|
||||
return (_flags & Flag::Muted);
|
||||
}
|
||||
|
||||
bool ForumTopic::changeMuted(bool muted) {
|
||||
void ForumTopic::setMuted(bool muted) {
|
||||
if (this->muted() == muted) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
const auto refresher = gsl::finally([&] {
|
||||
if (inChatList()) {
|
||||
|
@ -504,12 +501,7 @@ bool ForumTopic::changeMuted(bool muted) {
|
|||
});
|
||||
const auto notify = (unreadCountForBadge() > 0);
|
||||
const auto notifier = unreadStateChangeNotifier(notify);
|
||||
if (muted) {
|
||||
_flags |= Flag::Muted;
|
||||
} else {
|
||||
_flags &= ~Flag::Muted;
|
||||
}
|
||||
return true;
|
||||
Thread::setMuted(muted);
|
||||
}
|
||||
|
||||
bool ForumTopic::unreadCountKnown() const {
|
||||
|
@ -528,15 +520,7 @@ void ForumTopic::setUnreadMark(bool unread) {
|
|||
session().changes().topicUpdated(this, UpdateFlag::UnreadView);
|
||||
});
|
||||
const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
|
||||
if (unread) {
|
||||
_flags |= Flag::UnreadMark;
|
||||
} else {
|
||||
_flags &= ~Flag::UnreadMark;
|
||||
}
|
||||
}
|
||||
|
||||
bool ForumTopic::unreadMark() const {
|
||||
return (_flags & Flag::UnreadMark);
|
||||
Thread::setUnreadMark(unread);
|
||||
}
|
||||
|
||||
int ForumTopic::chatListUnreadCount() const {
|
||||
|
|
|
@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/notify/data_peer_notify_settings.h"
|
||||
#include "base/flags.h"
|
||||
|
||||
|
@ -42,13 +41,14 @@ class Forum;
|
|||
const QString &title,
|
||||
const style::ForumTopicIcon &st);
|
||||
|
||||
class ForumTopic final : public Dialogs::Entry {
|
||||
class ForumTopic final : public Data::Thread {
|
||||
public:
|
||||
ForumTopic(not_null<History*> history, MsgId rootId);
|
||||
ForumTopic(not_null<Forum*> forum, MsgId rootId);
|
||||
~ForumTopic();
|
||||
|
||||
ForumTopic(const ForumTopic &) = delete;
|
||||
ForumTopic &operator=(const ForumTopic &) = delete;
|
||||
not_null<History*> owningHistory() override {
|
||||
return history();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<RepliesList> replies() const;
|
||||
[[nodiscard]] not_null<ChannelData*> channel() const;
|
||||
|
@ -108,22 +108,11 @@ public:
|
|||
[[nodiscard]] bool unreadCountKnown() const;
|
||||
|
||||
[[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
|
||||
[[nodiscard]] bool muted() const;
|
||||
bool changeMuted(bool muted);
|
||||
|
||||
void setUnreadMark(bool unread);
|
||||
[[nodiscard]] bool unreadMark() const;
|
||||
|
||||
Ui::Text::String cloudDraftTextCache;
|
||||
Dialogs::Ui::MessageView lastItemDialogsView;
|
||||
void setMuted(bool muted) override;
|
||||
void setUnreadMark(bool unread) override;
|
||||
|
||||
private:
|
||||
enum class Flag : uchar {
|
||||
UnreadMark = (1 << 0),
|
||||
Muted = (1 << 1),
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||
|
||||
void indexTitleParts();
|
||||
void validateDefaultIcon() const;
|
||||
void applyTopicTopMessage(MsgId topMessageId);
|
||||
|
@ -159,7 +148,6 @@ private:
|
|||
std::optional<HistoryItem*> _lastServerMessage;
|
||||
std::optional<HistoryItem*> _chatListMessage;
|
||||
base::flat_set<FullMsgId> _requestedGroups;
|
||||
base::flags<Flag> _flags; // Initializer accesses _notify, be careful.
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
|
|
|
@ -901,14 +901,18 @@ Data::Forum *PeerData::forum() const {
|
|||
|
||||
Data::ForumTopic *PeerData::forumTopicFor(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
if (const auto forum = this->forum()) {
|
||||
return forum->topicFor(item);
|
||||
if (const auto rootId = item->topicRootId()) {
|
||||
if (const auto forum = this->forum()) {
|
||||
return forum->topicFor(rootId);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const {
|
||||
if (const auto forum = this->forum()) {
|
||||
if (!rootId) {
|
||||
return nullptr;
|
||||
} else if (const auto forum = this->forum()) {
|
||||
return forum->topicFor(rootId);
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
@ -1496,9 +1496,14 @@ void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
|
|||
}
|
||||
}
|
||||
const auto history = item->history();
|
||||
if (history->lastItemDialogsView.dependsOn(item)) {
|
||||
if (history->lastItemDialogsView().dependsOn(item)) {
|
||||
history->updateChatListEntry();
|
||||
}
|
||||
if (const auto topic = item->topic()) {
|
||||
if (topic->lastItemDialogsView().dependsOn(item)) {
|
||||
topic->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {
|
||||
|
|
133
Telegram/SourceFiles/data/data_thread.cpp
Normal file
133
Telegram/SourceFiles/data/data_thread.cpp
Normal 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
|
112
Telegram/SourceFiles/data/data_thread.h
Normal file
112
Telegram/SourceFiles/data/data_thread.h
Normal 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
|
|
@ -189,7 +189,7 @@ void NotifySettings::defaultUpdate(
|
|||
void NotifySettings::updateLocal(not_null<Data::ForumTopic*> topic) {
|
||||
auto changesIn = crl::time(0);
|
||||
const auto muted = isMuted(topic, &changesIn);
|
||||
topic->changeMuted(muted);
|
||||
topic->setMuted(muted);
|
||||
if (muted) {
|
||||
auto &lifetime = _mutedTopics.emplace(
|
||||
topic,
|
||||
|
@ -209,7 +209,9 @@ void NotifySettings::updateLocal(not_null<PeerData*> peer) {
|
|||
const auto history = _owner->historyLoaded(peer->id);
|
||||
auto changesIn = crl::time(0);
|
||||
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.
|
||||
} else {
|
||||
peer->session().changes().peerUpdated(
|
||||
|
@ -316,18 +318,15 @@ void NotifySettings::unmuteByFinished() {
|
|||
const auto history = _owner->historyLoaded((*i)->id);
|
||||
auto changesIn = crl::time(0);
|
||||
const auto muted = isMuted(*i, &changesIn);
|
||||
if (history) {
|
||||
history->setMuted(muted);
|
||||
}
|
||||
if (muted) {
|
||||
if (history) {
|
||||
history->changeMuted(true);
|
||||
}
|
||||
if (!changesInMin || changesInMin > changesIn) {
|
||||
changesInMin = changesIn;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
if (history) {
|
||||
history->changeMuted(false);
|
||||
}
|
||||
i = _mutedPeers.erase(i);
|
||||
}
|
||||
}
|
||||
|
@ -335,14 +334,13 @@ void NotifySettings::unmuteByFinished() {
|
|||
auto changesIn = crl::time(0);
|
||||
const auto topic = i->first;
|
||||
const auto muted = isMuted(topic, &changesIn);
|
||||
topic->setMuted(muted);
|
||||
if (muted) {
|
||||
topic->changeMuted(true);
|
||||
if (!changesInMin || changesInMin > changesIn) {
|
||||
changesInMin = changesIn;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
topic->changeMuted(false);
|
||||
i = _mutedTopics.erase(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/text/text_options.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "styles/style_dialogs.h" // st::dialogsTextWidthMin
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -70,6 +69,12 @@ Data::Folder *Entry::asFolder() {
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
Data::Thread *Entry::asThread() {
|
||||
return (_type == Type::History || _type == Type::ForumTopic)
|
||||
? static_cast<Data::Thread*>(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
Data::ForumTopic *Entry::asTopic() {
|
||||
return (_type == Type::ForumTopic)
|
||||
? static_cast<Data::ForumTopic*>(this)
|
||||
|
@ -104,41 +109,6 @@ void Entry::cachePinnedIndex(FilterId filterId, int 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 {
|
||||
return inChatList() || shouldBeInChatList();
|
||||
}
|
||||
|
@ -220,42 +190,6 @@ TimeId Entry::adjustedChatListTimeId() const {
|
|||
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() {
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "dialogs/dialogs_key.h"
|
||||
#include "ui/unread_badge.h"
|
||||
|
||||
class HistoryItem;
|
||||
class UserData;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
@ -23,13 +26,6 @@ class ForumTopic;
|
|||
class CloudImageView;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryUnreadThings {
|
||||
enum class AddType;
|
||||
struct All;
|
||||
class Proxy;
|
||||
class ConstProxy;
|
||||
} // namespace HistoryUnreadThings
|
||||
|
||||
namespace Ui {
|
||||
} // namespace Ui
|
||||
|
||||
|
@ -113,8 +109,6 @@ public:
|
|||
ForumTopic,
|
||||
};
|
||||
Entry(not_null<Data::Session*> owner, Type type);
|
||||
Entry(const Entry &other) = delete;
|
||||
Entry &operator=(const Entry &other) = delete;
|
||||
virtual ~Entry();
|
||||
|
||||
[[nodiscard]] Data::Session &owner() const;
|
||||
|
@ -122,6 +116,7 @@ public:
|
|||
|
||||
History *asHistory();
|
||||
Data::Folder *asFolder();
|
||||
Data::Thread *asThread();
|
||||
Data::ForumTopic *asTopic();
|
||||
|
||||
PositionChange adjustByPosInChatList(
|
||||
|
@ -149,7 +144,6 @@ public:
|
|||
return lookupPinnedIndex(filterId) != 0;
|
||||
}
|
||||
void cachePinnedIndex(FilterId filterId, int index);
|
||||
[[nodiscard]] bool isTopPromoted() const;
|
||||
[[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const {
|
||||
return filterId
|
||||
? computeSortPosition(filterId)
|
||||
|
@ -161,12 +155,6 @@ public:
|
|||
bool needUpdateInChatList() 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;
|
||||
static constexpr auto kArchiveFixOnTopIndex = 1;
|
||||
static constexpr auto kTopPromotionFixOnTopIndex = 2;
|
||||
|
@ -220,18 +208,7 @@ protected:
|
|||
|
||||
[[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:
|
||||
enum class Flag : uchar {
|
||||
IsTopPromoted = 0x01,
|
||||
UnreadThingsKnown = 0x02,
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||
|
||||
virtual void changedChatListPinHook();
|
||||
void pinnedIndexChanged(FilterId filterId, int was, int now);
|
||||
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
||||
|
@ -247,12 +224,10 @@ private:
|
|||
uint64 _sortKeyInChatList = 0;
|
||||
uint64 _sortKeyByDate = 0;
|
||||
base::flat_map<FilterId, int> _pinnedIndex;
|
||||
std::unique_ptr<HistoryUnreadThings::All> _unreadThings;
|
||||
mutable Ui::PeerBadge _chatListBadge;
|
||||
mutable Ui::Text::String _chatListNameText;
|
||||
mutable int _chatListNameVersion = 0;
|
||||
TimeId _timeId = 0;
|
||||
base::flags<Flag> _flags;
|
||||
const Type _type;
|
||||
|
||||
};
|
||||
|
|
|
@ -12,12 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
|
||||
namespace Dialogs {
|
||||
namespace {
|
||||
|
||||
using Folder = Data::Folder;
|
||||
using ForumTopic = Data::ForumTopic;
|
||||
|
||||
} // namespace
|
||||
|
||||
Key::Key(History *history) : _value(history) {
|
||||
}
|
||||
|
@ -47,14 +41,18 @@ History *Key::history() const {
|
|||
return _value ? _value->asHistory() : nullptr;
|
||||
}
|
||||
|
||||
Folder *Key::folder() const {
|
||||
Data::Folder *Key::folder() const {
|
||||
return _value ? _value->asFolder() : nullptr;
|
||||
}
|
||||
|
||||
ForumTopic *Key::topic() const {
|
||||
Data::ForumTopic *Key::topic() const {
|
||||
return _value ? _value->asTopic() : nullptr;
|
||||
}
|
||||
|
||||
Data::Thread *Key::thread() const {
|
||||
return _value ? _value->asThread() : nullptr;
|
||||
}
|
||||
|
||||
History *Key::parentHistory() const {
|
||||
if (const auto result = history()) {
|
||||
return result;
|
||||
|
|
|
@ -11,6 +11,7 @@ class History;
|
|||
class PeerData;
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
class Folder;
|
||||
class ForumTopic;
|
||||
} // namespace Data
|
||||
|
@ -40,6 +41,7 @@ public:
|
|||
History *history() const;
|
||||
Data::Folder *folder() const;
|
||||
Data::ForumTopic *topic() const;
|
||||
Data::Thread *thread() const;
|
||||
History *parentHistory() const;
|
||||
PeerData *peer() const;
|
||||
|
||||
|
|
|
@ -96,6 +96,9 @@ public:
|
|||
[[nodiscard]] Data::ForumTopic *topic() const {
|
||||
return _id.topic();
|
||||
}
|
||||
[[nodiscard]] Data::Thread *thread() const {
|
||||
return _id.thread();
|
||||
}
|
||||
[[nodiscard]] not_null<Entry*> entry() const {
|
||||
return _id.entry();
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_channel.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_peer_values.h"
|
||||
|
||||
namespace Dialogs::Ui {
|
||||
|
@ -346,7 +345,7 @@ void PaintRow(
|
|||
row->paintRipple(p, 0, 0, context.width, &ripple->c);
|
||||
|
||||
const auto history = chat.history();
|
||||
const auto topic = chat.topic();
|
||||
const auto thread = chat.thread();
|
||||
|
||||
if (flags & Flag::SavedMessages) {
|
||||
EmptyUserpic::PaintSavedMessages(
|
||||
|
@ -418,8 +417,8 @@ void PaintRow(
|
|||
if (promoted && !history->topPromotionMessage().isEmpty()) {
|
||||
auto availableWidth = namewidth;
|
||||
p.setFont(st::dialogsTextFont);
|
||||
if (history->cloudDraftTextCache.isEmpty()) {
|
||||
history->cloudDraftTextCache.setText(
|
||||
if (history->cloudDraftTextCache().isEmpty()) {
|
||||
history->cloudDraftTextCache().setText(
|
||||
st::dialogsTextStyle,
|
||||
history->topPromotionMessage(),
|
||||
DialogTextOptions());
|
||||
|
@ -429,7 +428,7 @@ void PaintRow(
|
|||
: context.selected
|
||||
? st::dialogsTextFgOver
|
||||
: st::dialogsTextFg);
|
||||
history->cloudDraftTextCache.draw(p, {
|
||||
history->cloudDraftTextCache().draw(p, {
|
||||
.position = { nameleft, texttop },
|
||||
.availableWidth = availableWidth,
|
||||
.spoiler = Text::DefaultSpoilerCache(),
|
||||
|
@ -475,7 +474,7 @@ void PaintRow(
|
|||
context.width,
|
||||
color,
|
||||
context.paused)) {
|
||||
if (history->cloudDraftTextCache.isEmpty()) {
|
||||
if (history->cloudDraftTextCache().isEmpty()) {
|
||||
using namespace TextUtilities;
|
||||
auto draftWrapped = Text::PlainLink(
|
||||
tr::lng_dialogs_text_from_wrapped(
|
||||
|
@ -500,7 +499,7 @@ void PaintRow(
|
|||
.session = &history->session(),
|
||||
.customEmojiRepaint = customEmojiRepaint,
|
||||
};
|
||||
history->cloudDraftTextCache.setMarkedText(
|
||||
history->cloudDraftTextCache().setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
draftText,
|
||||
DialogTextOptions(),
|
||||
|
@ -511,7 +510,7 @@ void PaintRow(
|
|||
: context.selected
|
||||
? st::dialogsTextFgOver
|
||||
: st::dialogsTextFg);
|
||||
history->cloudDraftTextCache.draw(p, {
|
||||
history->cloudDraftTextCache().draw(p, {
|
||||
.position = { nameleft, texttop },
|
||||
.availableWidth = availableWidth,
|
||||
.palette = &(supportMode
|
||||
|
@ -562,7 +561,7 @@ void PaintRow(
|
|||
// Empty history
|
||||
}
|
||||
} else if (!item->isEmpty()) {
|
||||
if ((history || topic) && !promoted) {
|
||||
if (thread && !promoted) {
|
||||
PaintRowDate(p, date, rectForName, context);
|
||||
}
|
||||
|
||||
|
@ -886,7 +885,7 @@ void RowPainter::Paint(
|
|||
const PaintContext &context) {
|
||||
const auto entry = row->entry();
|
||||
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 unreadCount = entry->chatListUnreadCount();
|
||||
const auto unreadMark = entry->chatListUnreadMark();
|
||||
|
@ -916,12 +915,11 @@ void RowPainter::Paint(
|
|||
? base::unixtime::parse(cloudDraft->date)
|
||||
: QDateTime();
|
||||
}();
|
||||
const auto displayMentionBadge = (history
|
||||
&& history->unreadMentions().has())
|
||||
|| (topic && topic->unreadMentions().has());
|
||||
const auto displayMentionBadge = thread
|
||||
&& thread->unreadMentions().has();
|
||||
const auto displayReactionBadge = !displayMentionBadge
|
||||
&& ((history && history->unreadReactions().has())
|
||||
|| (topic && topic->unreadReactions().has()));
|
||||
&& thread
|
||||
&& thread->unreadReactions().has();
|
||||
const auto mentionOrReactionMuted = (entry->folder() != nullptr)
|
||||
|| (!displayMentionBadge && unreadMuted);
|
||||
const auto displayUnreadCounter = [&] {
|
||||
|
@ -991,10 +989,8 @@ void RowPainter::Paint(
|
|||
: false;
|
||||
const auto view = actionWasPainted
|
||||
? nullptr
|
||||
: history
|
||||
? &history->lastItemDialogsView
|
||||
: topic
|
||||
? &topic->lastItemDialogsView
|
||||
: thread
|
||||
? &thread->lastItemDialogsView()
|
||||
: nullptr;
|
||||
if (const auto folder = row->folder()) {
|
||||
PaintListEntryText(p, row, context, rect);
|
||||
|
|
|
@ -70,13 +70,13 @@ using UpdateFlag = Data::HistoryUpdate::Flag;
|
|||
} // namespace
|
||||
|
||||
History::History(not_null<Data::Session*> owner, PeerId peerId)
|
||||
: Entry(owner, Type::History)
|
||||
: Thread(owner, Type::History)
|
||||
, peer(owner->peer(peerId))
|
||||
, cloudDraftTextCache(st::dialogsTextWidthMin)
|
||||
, _delegateMixin(HistoryInner::DelegateMixin())
|
||||
, _flags(owner->notifySettings().isMuted(peer) ? Flag::Muted : Flag(0))
|
||||
, _chatListNameSortKey(owner->nameSortKey(peer->name()))
|
||||
, _sendActionPainter(this) {
|
||||
Thread::setMuted(owner->notifySettings().isMuted(peer));
|
||||
|
||||
if (const auto user = peer->asUser()) {
|
||||
if (user->isBot()) {
|
||||
_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() {
|
||||
if (lastKeyboardId) {
|
||||
if (lastKeyboardId == lastKeyboardHiddenId) {
|
||||
|
@ -100,38 +102,6 @@ int History::height() const {
|
|||
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 {
|
||||
return _flags & Flag::HasPendingResizedItems;
|
||||
}
|
||||
|
@ -182,7 +152,7 @@ void History::checkChatListMessageRemoved(not_null<HistoryItem*> item) {
|
|||
}
|
||||
|
||||
void History::itemVanished(not_null<HistoryItem*> item) {
|
||||
removeNotification(item);
|
||||
item->thread()->removeNotification(item);
|
||||
if (lastKeyboardId == item->id) {
|
||||
clearLastKeyboard();
|
||||
}
|
||||
|
@ -255,7 +225,7 @@ void History::setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft)
|
|||
}
|
||||
const auto changingCloudDraft = (key == Data::DraftKey::Cloud());
|
||||
if (changingCloudDraft) {
|
||||
cloudDraftTextCache.clear();
|
||||
cloudDraftTextCache().clear();
|
||||
}
|
||||
if (draft) {
|
||||
_drafts[key] = std::move(draft);
|
||||
|
@ -283,7 +253,7 @@ void History::clearDrafts() {
|
|||
const auto changingCloudDraft = _drafts.contains(Data::DraftKey::Cloud());
|
||||
_drafts.clear();
|
||||
if (changingCloudDraft) {
|
||||
cloudDraftTextCache.clear();
|
||||
cloudDraftTextCache().clear();
|
||||
updateChatListSortPosition();
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +284,7 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) {
|
|||
existing->date = base::unixtime::now();
|
||||
}
|
||||
|
||||
cloudDraftTextCache.clear();
|
||||
cloudDraftTextCache().clear();
|
||||
updateChatListSortPosition();
|
||||
|
||||
return cloudDraft();
|
||||
|
@ -1148,12 +1118,12 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
|||
from->madeAction(item->date());
|
||||
}
|
||||
item->contributeToSlowmode();
|
||||
auto notification = ItemNotification{
|
||||
auto notification = Data::ItemNotification{
|
||||
.item = item,
|
||||
.type = ItemNotificationType::Message,
|
||||
.type = Data::ItemNotificationType::Message,
|
||||
};
|
||||
if (item->showNotification()) {
|
||||
pushNotification(notification);
|
||||
item->thread()->pushNotification(notification);
|
||||
}
|
||||
owner().notifyNewItemAdded(item);
|
||||
const auto stillShow = item->showNotification(); // Could be read already.
|
||||
|
@ -1732,7 +1702,7 @@ void History::setUnreadMark(bool unread) {
|
|||
if (clearUnreadOnClientSide()) {
|
||||
unread = false;
|
||||
}
|
||||
if (_unreadMark == unread) {
|
||||
if (unreadMark() == unread) {
|
||||
return;
|
||||
}
|
||||
const auto noUnreadMessages = !unreadCount();
|
||||
|
@ -1744,11 +1714,7 @@ void History::setUnreadMark(bool unread) {
|
|||
session().changes().historyUpdated(this, UpdateFlag::UnreadView);
|
||||
});
|
||||
const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
|
||||
_unreadMark = unread;
|
||||
}
|
||||
|
||||
bool History::unreadMark() const {
|
||||
return _unreadMark;
|
||||
Thread::setUnreadMark(unread);
|
||||
}
|
||||
|
||||
void History::setFakeUnreadWhileOpened(bool enabled) {
|
||||
|
@ -1768,13 +1734,9 @@ void History::setFakeUnreadWhileOpened(bool enabled) {
|
|||
return _fakeUnreadWhileOpened;
|
||||
}
|
||||
|
||||
bool History::muted() const {
|
||||
return (_flags & Flag::Muted);
|
||||
}
|
||||
|
||||
bool History::changeMuted(bool muted) {
|
||||
void History::setMuted(bool muted) {
|
||||
if (this->muted() == muted) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
const auto refresher = gsl::finally([&] {
|
||||
if (inChatList()) {
|
||||
|
@ -1787,12 +1749,7 @@ bool History::changeMuted(bool muted) {
|
|||
});
|
||||
const auto notify = (unreadCountForBadge() > 0);
|
||||
const auto notifier = unreadStateChangeNotifier(notify);
|
||||
if (muted) {
|
||||
_flags |= Flag::Muted;
|
||||
} else {
|
||||
_flags &= ~Flag::Muted;
|
||||
}
|
||||
return true;
|
||||
Thread::setMuted(muted);
|
||||
}
|
||||
|
||||
void History::getNextFirstUnreadMessage() {
|
||||
|
@ -2082,7 +2039,7 @@ bool History::chatListMutedBadge() const {
|
|||
Dialogs::UnreadState History::chatListUnreadState() const {
|
||||
auto result = Dialogs::UnreadState();
|
||||
const auto count = _unreadCount.value_or(0);
|
||||
const auto mark = !count && _unreadMark;
|
||||
const auto mark = !count && unreadMark();
|
||||
const auto muted = this->muted();
|
||||
result.messages = count;
|
||||
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 {
|
||||
return _loadedAtBottom;
|
||||
}
|
||||
|
@ -2697,9 +2639,9 @@ void History::cacheTopPromotion(
|
|||
if (topPromotionType() != type || _topPromotedMessage != message) {
|
||||
_topPromotedType = type;
|
||||
_topPromotedMessage = message;
|
||||
cloudDraftTextCache.clear();
|
||||
cloudDraftTextCache().clear();
|
||||
} else if (changed) {
|
||||
cloudDraftTextCache.clear();
|
||||
cloudDraftTextCache().clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3169,7 +3111,7 @@ void History::clear(ClearType type) {
|
|||
for (const auto &item : local) {
|
||||
item->destroy();
|
||||
}
|
||||
_notifications.clear();
|
||||
clearNotifications();
|
||||
owner().notifyHistoryCleared(this);
|
||||
if (unreadCountKnown()) {
|
||||
setUnreadCount(0);
|
||||
|
@ -3265,7 +3207,24 @@ void History::setHasPinnedMessages(bool has) {
|
|||
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)
|
||||
: _history(history) {
|
||||
|
|
|
@ -10,8 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_types.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "history/view/history_view_send_action.h"
|
||||
#include "base/observer.h"
|
||||
#include "base/timer.h"
|
||||
|
@ -71,31 +70,17 @@ enum class NewMessageType {
|
|||
Existing,
|
||||
};
|
||||
|
||||
enum class ItemNotificationType {
|
||||
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 {
|
||||
class History final : public Data::Thread {
|
||||
public:
|
||||
using Element = HistoryView::Element;
|
||||
|
||||
History(not_null<Data::Session*> owner, PeerId peerId);
|
||||
History(const History &) = delete;
|
||||
History &operator=(const History &) = delete;
|
||||
~History();
|
||||
|
||||
not_null<History*> owningHistory() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto delegateMixin() const
|
||||
-> not_null<HistoryMainElementDelegateMixin*> {
|
||||
return _delegateMixin.get();
|
||||
|
@ -257,21 +242,17 @@ public:
|
|||
[[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const;
|
||||
|
||||
void setUnreadCount(int newUnreadCount);
|
||||
void setUnreadMark(bool unread);
|
||||
[[nodiscard]] bool unreadMark() const;
|
||||
void setUnreadMark(bool unread) override;
|
||||
void setFakeUnreadWhileOpened(bool enabled);
|
||||
[[nodiscard]] bool fakeUnreadWhileOpened() const;
|
||||
[[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
|
||||
[[nodiscard]] bool muted() const;
|
||||
bool changeMuted(bool muted);
|
||||
void setMuted(bool muted) override;
|
||||
void addUnreadBar();
|
||||
void destroyUnreadBar();
|
||||
[[nodiscard]] Element *unreadBar() const;
|
||||
void calculateFirstUnreadMessage();
|
||||
void unsetFirstUnreadMessage();
|
||||
[[nodiscard]] Element *firstUnreadMessage() const;
|
||||
void clearNotifications();
|
||||
void clearIncomingNotifications();
|
||||
|
||||
[[nodiscard]] bool loadedAtBottom() const; // last message is in the list
|
||||
void setNotLoadedAtBottom();
|
||||
|
@ -315,12 +296,6 @@ public:
|
|||
void itemRemoved(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;
|
||||
void setHasPendingResizedItems();
|
||||
|
||||
|
@ -441,11 +416,13 @@ public:
|
|||
[[nodiscard]] bool hasPinnedMessages() const;
|
||||
void setHasPinnedMessages(bool has);
|
||||
|
||||
[[nodiscard]] bool isTopPromoted() const;
|
||||
|
||||
const not_null<PeerData*> peer;
|
||||
|
||||
// Still public data.
|
||||
std::deque<std::unique_ptr<HistoryBlock>> blocks;
|
||||
|
||||
not_null<PeerData*> peer;
|
||||
|
||||
// we save the last showAtMsgId to restore the state when switching
|
||||
// between different conversation histories
|
||||
MsgId showAtMsgId = ShowAtUnreadMsgId;
|
||||
|
@ -464,21 +441,20 @@ public:
|
|||
|
||||
mtpRequestId sendRequestId = 0;
|
||||
|
||||
Ui::Text::String cloudDraftTextCache;
|
||||
Dialogs::Ui::MessageView lastItemDialogsView;
|
||||
|
||||
private:
|
||||
friend class HistoryBlock;
|
||||
|
||||
enum class Flag : uchar {
|
||||
HasPendingResizedItems = (1 << 0),
|
||||
Muted = (1 << 1),
|
||||
IsTopPromoted = (1 << 1),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) {
|
||||
return true;
|
||||
};
|
||||
|
||||
void cacheTopPromoted(bool promoted);
|
||||
|
||||
// when this item is destroyed scrollTopItem just points to the next one
|
||||
// and scrollTopOffset remains the same
|
||||
// if we are at the bottom of the window scrollTopItem == nullptr and
|
||||
|
@ -530,7 +506,6 @@ private:
|
|||
void mainViewRemoved(
|
||||
not_null<HistoryBlock*> block,
|
||||
not_null<Element*> view);
|
||||
void removeNotification(not_null<HistoryItem*> item);
|
||||
|
||||
TimeId adjustedChatListTimeId() const override;
|
||||
void changedChatListPinHook() override;
|
||||
|
@ -614,7 +589,6 @@ private:
|
|||
|
||||
QString _chatListNameSortKey;
|
||||
|
||||
bool _unreadMark = false;
|
||||
bool _fakeUnreadWhileOpened = false;
|
||||
bool _hasPinnedMessages = false;
|
||||
|
||||
|
@ -637,8 +611,6 @@ private:
|
|||
|
||||
HistoryView::SendActionPainter _sendActionPainter;
|
||||
|
||||
std::deque<ItemNotification> _notifications;
|
||||
|
||||
};
|
||||
|
||||
class HistoryBlock {
|
||||
|
|
|
@ -2746,6 +2746,7 @@ void HistoryInner::checkHistoryActivation() {
|
|||
adjustCurrent(_visibleAreaBottom);
|
||||
if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) {
|
||||
// Clear possible message notifications.
|
||||
// Side-effect: Also clears all notifications from forum topics.
|
||||
Core::App().notifications().clearFromHistory(_history);
|
||||
}
|
||||
if (_curHistory != _history || _history->isEmpty()) {
|
||||
|
|
|
@ -227,12 +227,12 @@ void CheckReactionNotificationSchedule(
|
|||
if (user->blockStatus() == Status::Unknown) {
|
||||
user->updateFull();
|
||||
}
|
||||
const auto notification = ItemNotification{
|
||||
const auto notification = Data::ItemNotification{
|
||||
.item = item,
|
||||
.reactionSender = user,
|
||||
.type = ItemNotificationType::Reaction,
|
||||
.type = Data::ItemNotificationType::Reaction,
|
||||
};
|
||||
item->history()->pushNotification(notification);
|
||||
item->thread()->pushNotification(notification);
|
||||
Core::App().notifications().schedule(notification);
|
||||
return;
|
||||
}
|
||||
|
@ -374,9 +374,9 @@ void HistoryItem::invalidateChatListEntry() {
|
|||
history()->session().changes().messageUpdated(
|
||||
this,
|
||||
Data::MessageUpdate::Flag::DialogRowRefresh);
|
||||
history()->lastItemDialogsView.itemInvalidated(this);
|
||||
history()->lastItemDialogsView().itemInvalidated(this);
|
||||
if (const auto topic = this->topic()) {
|
||||
topic->lastItemDialogsView.itemInvalidated(this);
|
||||
topic->lastItemDialogsView().itemInvalidated(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -620,9 +620,20 @@ void HistoryItem::destroy() {
|
|||
_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 {
|
||||
const auto forum = _history->peer->forum();
|
||||
return forum ? forum->topicFor(this) : nullptr;
|
||||
if (const auto rootId = topicRootId()) {
|
||||
if (const auto forum = _history->peer->forum()) {
|
||||
return forum->topicFor(rootId);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HistoryItem::refreshMainView() {
|
||||
|
|
|
@ -46,6 +46,7 @@ class Media;
|
|||
struct MessageReaction;
|
||||
class MessageReactions;
|
||||
class ForumTopic;
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
|
@ -122,6 +123,7 @@ public:
|
|||
const QString &label,
|
||||
const TextWithEntities &content);
|
||||
|
||||
[[nodiscard]] not_null<Data::Thread*> thread() const;
|
||||
[[nodiscard]] not_null<History*> history() const {
|
||||
return _history;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ template <typename Update>
|
|||
|
||||
void Proxy::setCount(int count) {
|
||||
if (!_known) {
|
||||
_entry->setUnreadThingsKnown();
|
||||
_thread->setUnreadThingsKnown();
|
||||
}
|
||||
if (!_data) {
|
||||
if (!count) {
|
||||
|
@ -69,16 +69,16 @@ void Proxy::setCount(int count) {
|
|||
const auto has = (count > 0);
|
||||
if (has != had) {
|
||||
if (_type == Type::Mentions) {
|
||||
if (const auto history = _entry->asHistory()) {
|
||||
_entry->owner().chatsFilters().refreshHistory(history);
|
||||
if (const auto history = _thread->asHistory()) {
|
||||
_thread->owner().chatsFilters().refreshHistory(history);
|
||||
}
|
||||
}
|
||||
_entry->updateChatListEntry();
|
||||
_thread->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
|
||||
bool Proxy::add(MsgId msgId, AddType type) {
|
||||
if (const auto history = _entry->asHistory()) {
|
||||
if (const auto history = _thread->asHistory()) {
|
||||
if (history->peer->isBroadcast()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ void Proxy::addSlice(const MTPmessages_Messages &slice, int alreadyLoaded) {
|
|||
if (!alreadyLoaded && _data) {
|
||||
resolveList().clear();
|
||||
}
|
||||
const auto history = resolveHistory();
|
||||
const auto history = _thread->owningHistory();
|
||||
auto fullCount = slice.match([&](
|
||||
const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: received messages.messagesNotModified! "
|
||||
|
@ -150,7 +150,7 @@ void Proxy::addSlice(const MTPmessages_Messages &slice, int alreadyLoaded) {
|
|||
return data.vcount().v;
|
||||
});
|
||||
|
||||
auto &owner = _entry->owner();
|
||||
auto &owner = _thread->owner();
|
||||
const auto messages = slice.match([&](
|
||||
const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: received messages.messagesNotModified! "
|
||||
|
@ -203,7 +203,7 @@ void Proxy::checkAdd(MsgId msgId, bool resolved) {
|
|||
if (!list.loadedCount() || list.maxLoaded() <= msgId) {
|
||||
return;
|
||||
}
|
||||
const auto history = resolveHistory();
|
||||
const auto history = _thread->owningHistory();
|
||||
const auto peer = history->peer;
|
||||
const auto item = peer->owner().message(peer, msgId);
|
||||
if (item && item->hasUnreadReaction()) {
|
||||
|
@ -216,11 +216,11 @@ void Proxy::checkAdd(MsgId msgId, bool resolved) {
|
|||
}
|
||||
|
||||
void Proxy::notifyUpdated() {
|
||||
if (const auto history = _entry->asHistory()) {
|
||||
if (const auto history = _thread->asHistory()) {
|
||||
history->session().changes().historyUpdated(
|
||||
history,
|
||||
HistoryUpdateFlag(_type));
|
||||
} else if (const auto topic = _entry->asTopic()) {
|
||||
} else if (const auto topic = _thread->asTopic()) {
|
||||
topic->session().changes().topicUpdated(
|
||||
topic,
|
||||
TopicUpdateFlag(_type));
|
||||
|
@ -245,9 +245,4 @@ List &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
|
||||
|
|
|
@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class History;
|
||||
|
||||
namespace Dialogs {
|
||||
class Entry;
|
||||
namespace Data {
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryUnreadThings {
|
||||
|
@ -108,7 +108,7 @@ private:
|
|||
class Proxy final : public ConstProxy {
|
||||
public:
|
||||
Proxy(
|
||||
not_null<Dialogs::Entry*> entry,
|
||||
not_null<Data::Thread*> thread,
|
||||
std::unique_ptr<All> &data,
|
||||
Type type,
|
||||
bool known)
|
||||
|
@ -119,7 +119,7 @@ public:
|
|||
? &data->mentions
|
||||
: &data->reactions),
|
||||
known)
|
||||
, _entry(entry)
|
||||
, _thread(thread)
|
||||
, _data(data)
|
||||
, _type(type)
|
||||
, _known(known) {
|
||||
|
@ -138,9 +138,8 @@ private:
|
|||
void createData();
|
||||
void notifyUpdated();
|
||||
[[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;
|
||||
Type _type = Type::Mentions;
|
||||
bool _known = false;
|
||||
|
|
|
@ -2865,6 +2865,7 @@ void HistoryWidget::newItemAdded(not_null<HistoryItem*> item) {
|
|||
session().data().histories().readInboxOnNewMessage(item);
|
||||
|
||||
// Also clear possible scheduled messages notifications.
|
||||
// Side-effect: Also clears all notifications from forum topics.
|
||||
Core::App().notifications().clearFromHistory(_history);
|
||||
}
|
||||
}
|
||||
|
@ -3954,7 +3955,7 @@ void HistoryWidget::cornerButtonsShowAtPosition(
|
|||
}
|
||||
}
|
||||
|
||||
Dialogs::Entry *HistoryWidget::cornerButtonsEntry() {
|
||||
Data::Thread *HistoryWidget::cornerButtonsThread() {
|
||||
return _history;
|
||||
}
|
||||
|
||||
|
|
|
@ -325,7 +325,7 @@ private:
|
|||
|
||||
void cornerButtonsShowAtPosition(
|
||||
Data::MessagePosition position) override;
|
||||
Dialogs::Entry *cornerButtonsEntry() override;
|
||||
Data::Thread *cornerButtonsThread() override;
|
||||
FullMsgId cornerButtonsCurrentId() override;
|
||||
bool cornerButtonsIgnoreVisibility() override;
|
||||
std::optional<bool> cornerButtonsDownShown() override;
|
||||
|
|
|
@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
#include "main/main_session.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "apiwrap.h"
|
||||
|
@ -56,10 +55,10 @@ CornerButtons::CornerButtons(
|
|||
filterScroll(_reactions);
|
||||
|
||||
SendMenu::SetupUnreadMentionsMenu(_mentions.widget.data(), [=] {
|
||||
return _delegate->cornerButtonsEntry();
|
||||
return _delegate->cornerButtonsThread();
|
||||
});
|
||||
SendMenu::SetupUnreadReactionsMenu(_reactions.widget.data(), [=] {
|
||||
return _delegate->cornerButtonsEntry();
|
||||
return _delegate->cornerButtonsThread();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -86,14 +85,14 @@ void CornerButtons::mentionsClick() {
|
|||
if (!history) {
|
||||
return;
|
||||
}
|
||||
const auto entry = _delegate->cornerButtonsEntry();
|
||||
const auto msgId = entry->unreadMentions().minLoaded();
|
||||
const auto thread = _delegate->cornerButtonsThread();
|
||||
const auto msgId = thread->unreadMentions().minLoaded();
|
||||
const auto already = (_delegate->cornerButtonsCurrentId().msg == msgId);
|
||||
|
||||
// Mark mention voice/video message as read.
|
||||
// See https://github.com/telegramdesktop/tdesktop/issues/5623
|
||||
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 document = media->document()) {
|
||||
if (!media->webpage()
|
||||
|
@ -113,8 +112,8 @@ void CornerButtons::reactionsClick() {
|
|||
if (!history) {
|
||||
return;
|
||||
}
|
||||
const auto entry = _delegate->cornerButtonsEntry();
|
||||
showAt(entry->unreadReactions().minLoaded());
|
||||
const auto thread = _delegate->cornerButtonsThread();
|
||||
showAt(thread->unreadReactions().minLoaded());
|
||||
}
|
||||
|
||||
void CornerButtons::clearReplyReturns() {
|
||||
|
@ -135,10 +134,10 @@ void CornerButtons::setReplyReturns(QVector<FullMsgId> replyReturns) {
|
|||
}
|
||||
|
||||
void CornerButtons::computeCurrentReplyReturn() {
|
||||
const auto entry = _delegate->cornerButtonsEntry();
|
||||
_replyReturn = (!entry || _replyReturns.empty())
|
||||
const auto thread = _delegate->cornerButtonsThread();
|
||||
_replyReturn = (!thread || _replyReturns.empty())
|
||||
? nullptr
|
||||
: entry->owner().message(_replyReturns.back());
|
||||
: thread->owner().message(_replyReturns.back());
|
||||
}
|
||||
|
||||
void CornerButtons::skipReplyReturn(FullMsgId id) {
|
||||
|
@ -188,15 +187,8 @@ CornerButton &CornerButtons::buttonByType(Type type) {
|
|||
}
|
||||
|
||||
History *CornerButtons::lookupHistory() const {
|
||||
const auto entry = _delegate->cornerButtonsEntry();
|
||||
if (!entry) {
|
||||
return nullptr;
|
||||
} else if (const auto history = entry->asHistory()) {
|
||||
return history;
|
||||
} else if (const auto topic = entry->asTopic()) {
|
||||
return topic->history();
|
||||
}
|
||||
return nullptr;
|
||||
const auto thread = _delegate->cornerButtonsThread();
|
||||
return thread ? thread->owningHistory().get() : nullptr;
|
||||
}
|
||||
|
||||
void CornerButtons::showAt(MsgId id) {
|
||||
|
@ -225,14 +217,14 @@ void CornerButtons::updateUnreadThingsVisibility() {
|
|||
if (_delegate->cornerButtonsIgnoreVisibility()) {
|
||||
return;
|
||||
}
|
||||
const auto entry = _delegate->cornerButtonsEntry();
|
||||
if (!entry) {
|
||||
const auto thread = _delegate->cornerButtonsThread();
|
||||
if (!thread) {
|
||||
updateVisibility(Type::Mentions, false);
|
||||
updateVisibility(Type::Reactions, false);
|
||||
return;
|
||||
}
|
||||
auto &unreadThings = entry->session().api().unreadThings();
|
||||
unreadThings.preloadEnough(entry);
|
||||
auto &unreadThings = thread->session().api().unreadThings();
|
||||
unreadThings.preloadEnough(thread);
|
||||
|
||||
const auto updateWithCount = [&](Type type, int count) {
|
||||
updateVisibility(
|
||||
|
@ -240,25 +232,25 @@ void CornerButtons::updateUnreadThingsVisibility() {
|
|||
(count > 0) && _delegate->cornerButtonsUnreadMayBeShown());
|
||||
};
|
||||
if (_delegate->cornerButtonsHas(Type::Mentions)
|
||||
&& unreadThings.trackMentions(entry)) {
|
||||
if (const auto count = entry->unreadMentions().count(0)) {
|
||||
&& unreadThings.trackMentions(thread)) {
|
||||
if (const auto count = thread->unreadMentions().count(0)) {
|
||||
_mentions.widget->setUnreadCount(count);
|
||||
}
|
||||
updateWithCount(
|
||||
Type::Mentions,
|
||||
entry->unreadMentions().loadedCount());
|
||||
thread->unreadMentions().loadedCount());
|
||||
} else {
|
||||
updateVisibility(Type::Mentions, false);
|
||||
}
|
||||
|
||||
if (_delegate->cornerButtonsHas(Type::Reactions)
|
||||
&& unreadThings.trackReactions(entry)) {
|
||||
if (const auto count = entry->unreadReactions().count(0)) {
|
||||
&& unreadThings.trackReactions(thread)) {
|
||||
if (const auto count = thread->unreadReactions().count(0)) {
|
||||
_reactions.widget->setUnreadCount(count);
|
||||
}
|
||||
updateWithCount(
|
||||
Type::Reactions,
|
||||
entry->unreadReactions().loadedCount());
|
||||
thread->unreadReactions().loadedCount());
|
||||
} else {
|
||||
updateVisibility(Type::Reactions, false);
|
||||
}
|
||||
|
@ -353,8 +345,8 @@ Fn<void(bool found)> CornerButtons::doneJumpFrom(
|
|||
return [=](bool found) {
|
||||
skipReplyReturn(targetId);
|
||||
if (originId) {
|
||||
if (const auto entry = _delegate->cornerButtonsEntry()) {
|
||||
if (const auto item = entry->owner().message(originId)) {
|
||||
if (const auto thread = _delegate->cornerButtonsThread()) {
|
||||
if (const auto item = thread->owner().message(originId)) {
|
||||
pushReplyReturn(item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,9 @@ class HistoryDownButton;
|
|||
|
||||
namespace Data {
|
||||
struct MessagePosition;
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs {
|
||||
class Entry;
|
||||
} // namespace Dialogs
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
struct CornerButton {
|
||||
|
@ -50,7 +47,7 @@ class CornerButtonsDelegate {
|
|||
public:
|
||||
virtual void cornerButtonsShowAtPosition(
|
||||
Data::MessagePosition position) = 0;
|
||||
[[nodiscard]] virtual Dialogs::Entry *cornerButtonsEntry() = 0;
|
||||
[[nodiscard]] virtual Data::Thread *cornerButtonsThread() = 0;
|
||||
[[nodiscard]] virtual FullMsgId cornerButtonsCurrentId() = 0;
|
||||
[[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0;
|
||||
[[nodiscard]] virtual std::optional<bool> cornerButtonsDownShown() = 0;
|
||||
|
|
|
@ -193,7 +193,7 @@ void PinnedWidget::cornerButtonsShowAtPosition(
|
|||
showAtPosition(position);
|
||||
}
|
||||
|
||||
Dialogs::Entry *PinnedWidget::cornerButtonsEntry() {
|
||||
Data::Thread *PinnedWidget::cornerButtonsThread() {
|
||||
return _history;
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ public:
|
|||
// CornerButtonsDelegate delegate.
|
||||
void cornerButtonsShowAtPosition(
|
||||
Data::MessagePosition position) override;
|
||||
Dialogs::Entry *cornerButtonsEntry() override;
|
||||
Data::Thread *cornerButtonsThread() override;
|
||||
FullMsgId cornerButtonsCurrentId() override;
|
||||
bool cornerButtonsIgnoreVisibility() override;
|
||||
std::optional<bool> cornerButtonsDownShown() override;
|
||||
|
|
|
@ -1387,8 +1387,8 @@ void RepliesWidget::cornerButtonsShowAtPosition(
|
|||
showAtPosition(position);
|
||||
}
|
||||
|
||||
Dialogs::Entry *RepliesWidget::cornerButtonsEntry() {
|
||||
return _topic ? static_cast<Dialogs::Entry*>(_topic) : _history;
|
||||
Data::Thread *RepliesWidget::cornerButtonsThread() {
|
||||
return _topic ? static_cast<Data::Thread*>(_topic) : _history;
|
||||
}
|
||||
|
||||
FullMsgId RepliesWidget::cornerButtonsCurrentId() {
|
||||
|
|
|
@ -161,7 +161,7 @@ public:
|
|||
// CornerButtonsDelegate delegate.
|
||||
void cornerButtonsShowAtPosition(
|
||||
Data::MessagePosition position) override;
|
||||
Dialogs::Entry *cornerButtonsEntry() override;
|
||||
Data::Thread *cornerButtonsThread() override;
|
||||
FullMsgId cornerButtonsCurrentId() override;
|
||||
bool cornerButtonsIgnoreVisibility() override;
|
||||
std::optional<bool> cornerButtonsDownShown() override;
|
||||
|
|
|
@ -822,7 +822,7 @@ void ScheduledWidget::cornerButtonsShowAtPosition(
|
|||
showAtPosition(position);
|
||||
}
|
||||
|
||||
Dialogs::Entry *ScheduledWidget::cornerButtonsEntry() {
|
||||
Data::Thread *ScheduledWidget::cornerButtonsThread() {
|
||||
return _history;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ public:
|
|||
// CornerButtonsDelegate delegate.
|
||||
void cornerButtonsShowAtPosition(
|
||||
Data::MessagePosition position) override;
|
||||
Dialogs::Entry *cornerButtonsEntry() override;
|
||||
Data::Thread *cornerButtonsThread() override;
|
||||
FullMsgId cornerButtonsCurrentId() override;
|
||||
bool cornerButtonsIgnoreVisibility() override;
|
||||
std::optional<bool> cornerButtonsDownShown() override;
|
||||
|
|
|
@ -152,29 +152,29 @@ void SetupMenuAndShortcuts(
|
|||
|
||||
void SetupReadAllMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<Dialogs::Entry*()> currentEntry,
|
||||
Fn<Data::Thread*()> currentThread,
|
||||
const QString &text,
|
||||
Fn<void(not_null<Dialogs::Entry*>, Fn<void()>)> sendReadRequest) {
|
||||
Fn<void(not_null<Data::Thread*>, Fn<void()>)> sendReadRequest) {
|
||||
struct State {
|
||||
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 showMenu = [=] {
|
||||
const auto entry = base::make_weak(currentEntry());
|
||||
if (!entry) {
|
||||
const auto thread = base::make_weak(currentThread());
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
||||
button,
|
||||
st::popupMenuWithIcons);
|
||||
state->menu->addAction(text, [=] {
|
||||
const auto strong = entry.get();
|
||||
if (!strong || !state->sentForEntries.emplace(entry).second) {
|
||||
const auto strong = thread.get();
|
||||
if (!strong || !state->sentForEntries.emplace(thread).second) {
|
||||
return;
|
||||
}
|
||||
sendReadRequest(strong, [=] {
|
||||
state->sentForEntries.remove(entry);
|
||||
state->sentForEntries.remove(thread);
|
||||
});
|
||||
}, &st::menuIconMarkRead);
|
||||
state->menu->popup(QCursor::pos());
|
||||
|
@ -191,21 +191,19 @@ void SetupReadAllMenu(
|
|||
|
||||
void SetupUnreadMentionsMenu(
|
||||
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 sendOne = [=](
|
||||
base::weak_ptr<Dialogs::Entry> weakEntry,
|
||||
base::weak_ptr<Data::Thread> weakThread,
|
||||
Fn<void()> done,
|
||||
auto resend) -> void {
|
||||
const auto entry = weakEntry.get();
|
||||
if (!entry) {
|
||||
const auto thread = weakThread.get();
|
||||
if (!thread) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
const auto history = entry->asHistory();
|
||||
const auto topic = entry->asTopic();
|
||||
Assert(history || topic);
|
||||
const auto peer = (history ? history : topic->history().get())->peer;
|
||||
const auto peer = thread->owningHistory()->peer;
|
||||
const auto topic = thread->asTopic();
|
||||
const auto rootId = topic ? topic->rootId() : 0;
|
||||
using Flag = MTPmessages_ReadMentions::Flag;
|
||||
peer->session().api().request(MTPmessages_ReadMentions(
|
||||
|
@ -217,7 +215,7 @@ void SetupUnreadMentionsMenu(
|
|||
peer,
|
||||
result);
|
||||
if (offset > 0) {
|
||||
resend(weakEntry, done, resend);
|
||||
resend(weakThread, done, resend);
|
||||
} else {
|
||||
done();
|
||||
peer->owner().history(peer)->clearUnreadMentionsFor(rootId);
|
||||
|
@ -225,30 +223,28 @@ void SetupUnreadMentionsMenu(
|
|||
}).fail(done).send();
|
||||
};
|
||||
const auto sendRequest = [=](
|
||||
not_null<Dialogs::Entry*> entry,
|
||||
not_null<Data::Thread*> thread,
|
||||
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(
|
||||
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 sendOne = [=](
|
||||
base::weak_ptr<Dialogs::Entry> weakEntry,
|
||||
base::weak_ptr<Data::Thread> weakThread,
|
||||
Fn<void()> done,
|
||||
auto resend) -> void {
|
||||
const auto entry = weakEntry.get();
|
||||
if (!entry) {
|
||||
const auto thread = weakThread.get();
|
||||
if (!thread) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
const auto history = entry->asHistory();
|
||||
const auto topic = entry->asTopic();
|
||||
Assert(history || topic);
|
||||
const auto peer = (history ? history : topic->history().get())->peer;
|
||||
const auto topic = thread->asTopic();
|
||||
const auto peer = thread->owningHistory()->peer;
|
||||
const auto rootId = topic ? topic->rootId() : 0;
|
||||
using Flag = MTPmessages_ReadReactions::Flag;
|
||||
peer->session().api().request(MTPmessages_ReadReactions(
|
||||
|
@ -260,7 +256,7 @@ void SetupUnreadReactionsMenu(
|
|||
peer,
|
||||
result);
|
||||
if (offset > 0) {
|
||||
resend(weakEntry, done, resend);
|
||||
resend(weakThread, done, resend);
|
||||
} else {
|
||||
done();
|
||||
peer->owner().history(peer)->clearUnreadReactionsFor(rootId);
|
||||
|
@ -268,11 +264,11 @@ void SetupUnreadReactionsMenu(
|
|||
}).fail(done).send();
|
||||
};
|
||||
const auto sendRequest = [=](
|
||||
not_null<Dialogs::Entry*> entry,
|
||||
not_null<Data::Thread*> thread,
|
||||
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
|
||||
|
|
|
@ -16,9 +16,9 @@ class PopupMenu;
|
|||
class RpWidget;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Dialogs {
|
||||
class Entry;
|
||||
} // namespace Dialogs
|
||||
namespace Data {
|
||||
class Thread;
|
||||
} // namespace Data
|
||||
|
||||
namespace SendMenu {
|
||||
|
||||
|
@ -55,10 +55,10 @@ void SetupMenuAndShortcuts(
|
|||
|
||||
void SetupUnreadMentionsMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<Dialogs::Entry*()> currentEntry);
|
||||
Fn<Data::Thread*()> currentThread);
|
||||
|
||||
void SetupUnreadReactionsMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<Dialogs::Entry*()> currentEntry);
|
||||
Fn<Data::Thread*()> currentThread);
|
||||
|
||||
} // namespace SendMenu
|
||||
|
|
|
@ -81,18 +81,18 @@ QString TextWithPermanentSpoiler(const TextWithEntities &textWithEntities) {
|
|||
struct System::Waiter {
|
||||
NotificationInHistoryKey key;
|
||||
UserData *reactionSender = nullptr;
|
||||
ItemNotificationType type = ItemNotificationType::Message;
|
||||
Data::ItemNotificationType type = Data::ItemNotificationType::Message;
|
||||
crl::time when = 0;
|
||||
};
|
||||
|
||||
System::NotificationInHistoryKey::NotificationInHistoryKey(
|
||||
ItemNotification notification)
|
||||
Data::ItemNotification notification)
|
||||
: NotificationInHistoryKey(notification.item->id, notification.type) {
|
||||
}
|
||||
|
||||
System::NotificationInHistoryKey::NotificationInHistoryKey(
|
||||
MsgId messageId,
|
||||
ItemNotificationType type)
|
||||
Data::ItemNotificationType type)
|
||||
: messageId(messageId)
|
||||
, type(type) {
|
||||
}
|
||||
|
@ -161,13 +161,13 @@ bool System::skipReactionNotification(not_null<HistoryItem*> item) const {
|
|||
}
|
||||
|
||||
System::SkipState System::skipNotification(
|
||||
ItemNotification notification) const {
|
||||
Data::ItemNotification notification) const {
|
||||
const auto item = notification.item;
|
||||
const auto type = notification.type;
|
||||
const auto messageNotification = (type == ItemNotificationType::Message);
|
||||
if (!item->history()->currentNotification()
|
||||
|| (messageNotification && item->skipNotification())
|
||||
|| (type == ItemNotificationType::Reaction
|
||||
const auto messageType = (type == Data::ItemNotificationType::Message);
|
||||
if (!item->thread()->currentNotification()
|
||||
|| (messageType && item->skipNotification())
|
||||
|| (type == Data::ItemNotificationType::Reaction
|
||||
&& skipReactionNotification(item))) {
|
||||
return { SkipState::Skip };
|
||||
}
|
||||
|
@ -175,27 +175,27 @@ System::SkipState System::skipNotification(
|
|||
}
|
||||
|
||||
System::SkipState System::computeSkipState(
|
||||
ItemNotification notification) const {
|
||||
Data::ItemNotification notification) const {
|
||||
const auto type = notification.type;
|
||||
const auto item = notification.item;
|
||||
const auto history = item->history();
|
||||
const auto messageNotification = (type == ItemNotificationType::Message);
|
||||
const auto messageType = (type == Data::ItemNotificationType::Message);
|
||||
const auto withSilent = [&](
|
||||
SkipState::Value value,
|
||||
bool forceSilent = false) {
|
||||
return SkipState{
|
||||
.value = value,
|
||||
.silent = (forceSilent
|
||||
|| !messageNotification
|
||||
|| !messageType
|
||||
|| item->isSilent()
|
||||
|| history->owner().notifySettings().sound(
|
||||
history->peer).none),
|
||||
};
|
||||
};
|
||||
const auto showForMuted = messageNotification
|
||||
const auto showForMuted = messageType
|
||||
&& item->out()
|
||||
&& item->isFromScheduled();
|
||||
const auto notifyBy = messageNotification
|
||||
const auto notifyBy = messageType
|
||||
? item->specialNotificationPeer()
|
||||
: notification.reactionSender;
|
||||
if (Core::Quitting()) {
|
||||
|
@ -205,7 +205,7 @@ System::SkipState System::computeSkipState(
|
|||
return { SkipState::Skip };
|
||||
}
|
||||
|
||||
if (messageNotification) {
|
||||
if (messageType) {
|
||||
history->owner().notifySettings().request(
|
||||
history->peer);
|
||||
} else if (notifyBy->blockStatus() == PeerData::BlockStatus::Unknown) {
|
||||
|
@ -215,10 +215,10 @@ System::SkipState System::computeSkipState(
|
|||
history->owner().notifySettings().request(notifyBy);
|
||||
}
|
||||
|
||||
if (messageNotification
|
||||
if (messageType
|
||||
&& history->owner().notifySettings().muteUnknown(history->peer)) {
|
||||
return { SkipState::Unknown };
|
||||
} else if (messageNotification
|
||||
} else if (messageType
|
||||
&& !history->owner().notifySettings().isMuted(history->peer)) {
|
||||
return withSilent(SkipState::DontSkip);
|
||||
} else if (!notifyBy) {
|
||||
|
@ -226,11 +226,11 @@ System::SkipState System::computeSkipState(
|
|||
showForMuted ? SkipState::DontSkip : SkipState::Skip,
|
||||
showForMuted);
|
||||
} else if (history->owner().notifySettings().muteUnknown(notifyBy)
|
||||
|| (!messageNotification
|
||||
|| (!messageType
|
||||
&& notifyBy->blockStatus() == PeerData::BlockStatus::Unknown)) {
|
||||
return withSilent(SkipState::Unknown);
|
||||
} else if (!history->owner().notifySettings().isMuted(notifyBy)
|
||||
&& (messageNotification || !notifyBy->isBlocked())) {
|
||||
&& (messageType || !notifyBy->isBlocked())) {
|
||||
return withSilent(SkipState::DontSkip);
|
||||
} else {
|
||||
return withSilent(
|
||||
|
@ -261,7 +261,7 @@ System::Timing System::countTiming(
|
|||
};
|
||||
}
|
||||
|
||||
void System::schedule(ItemNotification notification) {
|
||||
void System::schedule(Data::ItemNotification notification) {
|
||||
Expects(_manager != nullptr);
|
||||
|
||||
const auto item = notification.item;
|
||||
|
@ -275,13 +275,13 @@ void System::schedule(ItemNotification notification) {
|
|||
const auto ready = (skip.value != SkipState::Unknown)
|
||||
&& item->notificationReady();
|
||||
|
||||
const auto minimalDelay = (type == ItemNotificationType::Reaction)
|
||||
const auto minimalDelay = (type == Data::ItemNotificationType::Reaction)
|
||||
? kMinimalDelay
|
||||
: item->Has<HistoryMessageForwarded>()
|
||||
? kMinimalForwardDelay
|
||||
: kMinimalDelay;
|
||||
const auto timing = countTiming(history, minimalDelay);
|
||||
const auto notifyBy = (type == ItemNotificationType::Message)
|
||||
const auto notifyBy = (type == Data::ItemNotificationType::Message)
|
||||
? item->specialNotificationPeer()
|
||||
: notification.reactionSender;
|
||||
if (!skip.silent) {
|
||||
|
@ -320,8 +320,8 @@ void System::clearAll() {
|
|||
_manager->clearAll();
|
||||
}
|
||||
|
||||
for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) {
|
||||
i->first->clearNotifications();
|
||||
for (const auto &[thread, _] : _whenMaps) {
|
||||
thread->clearNotifications();
|
||||
}
|
||||
_whenMaps.clear();
|
||||
_whenAlerts.clear();
|
||||
|
@ -334,52 +334,33 @@ void System::clearFromTopic(not_null<Data::ForumTopic*> topic) {
|
|||
_manager->clearFromTopic(topic);
|
||||
}
|
||||
|
||||
// #TODO forum notifications
|
||||
//topic->clearNotifications();
|
||||
//_whenMaps.remove(topic);
|
||||
//_whenAlerts.remove(topic);
|
||||
//_waiters.remove(topic);
|
||||
//_settingWaiters.remove(topic);
|
||||
topic->clearNotifications();
|
||||
_whenMaps.remove(topic);
|
||||
_whenAlerts.remove(topic);
|
||||
_waiters.remove(topic);
|
||||
_settingWaiters.remove(topic);
|
||||
|
||||
_waitTimer.cancel();
|
||||
showNext();
|
||||
}
|
||||
|
||||
void System::clearFromHistory(not_null<History*> history) {
|
||||
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);
|
||||
}
|
||||
|
||||
void System::clearForThreadIf(Fn<bool(not_null<Data::Thread*>)> predicate) {
|
||||
for (auto i = _whenMaps.begin(); i != _whenMaps.end();) {
|
||||
const auto history = i->first;
|
||||
if (&history->session() != session) {
|
||||
const auto thread = i->first;
|
||||
if (!predicate(thread)) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
history->clearNotifications();
|
||||
i = _whenMaps.erase(i);
|
||||
_whenAlerts.remove(history);
|
||||
_waiters.remove(history);
|
||||
_settingWaiters.remove(history);
|
||||
|
||||
thread->clearNotifications();
|
||||
_whenAlerts.remove(thread);
|
||||
_waiters.remove(thread);
|
||||
_settingWaiters.remove(thread);
|
||||
}
|
||||
const auto clearFrom = [&](auto &map) {
|
||||
for (auto i = map.begin(); i != map.end();) {
|
||||
if (&i->first->session() == session) {
|
||||
if (predicate(i->first)) {
|
||||
i = map.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
|
@ -389,6 +370,27 @@ void System::clearFromSession(not_null<Main::Session*> session) {
|
|||
clearFrom(_whenAlerts);
|
||||
clearFrom(_waiters);
|
||||
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) {
|
||||
|
@ -403,9 +405,8 @@ void System::clearIncomingFromTopic(not_null<Data::ForumTopic*> topic) {
|
|||
if (_manager) {
|
||||
_manager->clearFromTopic(topic);
|
||||
}
|
||||
// #TODO forum notifications
|
||||
//topic->clearIncomingNotifications();
|
||||
//_whenAlerts.remove(topic);
|
||||
topic->clearIncomingNotifications();
|
||||
_whenAlerts.remove(topic);
|
||||
}
|
||||
|
||||
void System::clearFromItem(not_null<HistoryItem*> item) {
|
||||
|
@ -428,10 +429,10 @@ void System::clearAllFast() {
|
|||
void System::checkDelayed() {
|
||||
for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) {
|
||||
const auto remove = [&] {
|
||||
const auto history = i->first;
|
||||
const auto peer = history->peer;
|
||||
const auto thread = i->first;
|
||||
const auto peer = thread->owningHistory()->peer;
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
|
@ -503,7 +504,7 @@ void System::showNext() {
|
|||
auto alertPeer = (PeerData*)nullptr;
|
||||
for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) {
|
||||
while (!i->second.empty() && i->second.begin()->first <= ms) {
|
||||
const auto peer = i->first->peer;
|
||||
const auto peer = i->first->owningHistory()->peer;
|
||||
const auto ¬ifySettings = peer->owner().notifySettings();
|
||||
const auto peerUnknown = notifySettings.muteUnknown(peer);
|
||||
const auto peerAlert = !peerUnknown
|
||||
|
@ -559,15 +560,15 @@ void System::showNext() {
|
|||
|
||||
while (true) {
|
||||
auto next = 0LL;
|
||||
auto notify = std::optional<ItemNotification>();
|
||||
auto notifyHistory = (History*)nullptr;
|
||||
auto notify = std::optional<Data::ItemNotification>();
|
||||
auto notifyThread = (Data::Thread*)nullptr;
|
||||
for (auto i = _waiters.begin(); i != _waiters.end();) {
|
||||
const auto history = i->first;
|
||||
auto current = history->currentNotification();
|
||||
const auto thread = i->first;
|
||||
auto current = thread->currentNotification();
|
||||
if (current && current->item->id != i->second.key.messageId) {
|
||||
auto j = _whenMaps.find(history);
|
||||
auto j = _whenMaps.find(thread);
|
||||
if (j == _whenMaps.end()) {
|
||||
history->clearNotifications();
|
||||
thread->clearNotifications();
|
||||
i = _waiters.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
@ -578,12 +579,12 @@ void System::showNext() {
|
|||
i->second.when = k->second;
|
||||
break;
|
||||
}
|
||||
history->skipNotification();
|
||||
current = history->currentNotification();
|
||||
thread->skipNotification();
|
||||
current = thread->currentNotification();
|
||||
} while (current);
|
||||
}
|
||||
if (!current) {
|
||||
_whenMaps.remove(history);
|
||||
_whenMaps.remove(thread);
|
||||
i = _waiters.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
@ -591,7 +592,7 @@ void System::showNext() {
|
|||
if (!notify || next > when) {
|
||||
next = when;
|
||||
notify = current,
|
||||
notifyHistory = history;
|
||||
notifyThread = thread;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
@ -606,11 +607,11 @@ void System::showNext() {
|
|||
break;
|
||||
}
|
||||
const auto notifyItem = notify->item;
|
||||
const auto messageNotification = (notify->type
|
||||
== ItemNotificationType::Message);
|
||||
const auto isForwarded = messageNotification
|
||||
const auto messageType = (notify->type
|
||||
== Data::ItemNotificationType::Message);
|
||||
const auto isForwarded = messageType
|
||||
&& notifyItem->Has<HistoryMessageForwarded>();
|
||||
const auto isAlbum = messageNotification
|
||||
const auto isAlbum = messageType
|
||||
&& notifyItem->groupId();
|
||||
|
||||
// Forwarded and album notify grouping.
|
||||
|
@ -619,15 +620,15 @@ void System::showNext() {
|
|||
: nullptr;
|
||||
auto forwardedCount = isForwarded ? 1 : 0;
|
||||
|
||||
const auto history = notifyItem->history();
|
||||
const auto j = _whenMaps.find(history);
|
||||
const auto thread = notifyItem->thread();
|
||||
const auto j = _whenMaps.find(thread);
|
||||
if (j == _whenMaps.cend()) {
|
||||
history->clearNotifications();
|
||||
thread->clearNotifications();
|
||||
} else {
|
||||
while (true) {
|
||||
auto nextNotify = std::optional<ItemNotification>();
|
||||
history->skipNotification();
|
||||
if (!history->hasNotification()) {
|
||||
auto nextNotify = std::optional<Data::ItemNotification>();
|
||||
thread->skipNotification();
|
||||
if (!thread->hasNotification()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -637,25 +638,26 @@ void System::showNext() {
|
|||
});
|
||||
do {
|
||||
const auto k = j->second.find(
|
||||
history->currentNotification());
|
||||
thread->currentNotification());
|
||||
if (k != j->second.cend()) {
|
||||
nextNotify = history->currentNotification();
|
||||
_waiters.emplace(notifyHistory, Waiter{
|
||||
nextNotify = thread->currentNotification();
|
||||
_waiters.emplace(notifyThread, Waiter{
|
||||
.key = k->first,
|
||||
.when = k->second
|
||||
});
|
||||
break;
|
||||
}
|
||||
history->skipNotification();
|
||||
} while (history->hasNotification());
|
||||
thread->skipNotification();
|
||||
} while (thread->hasNotification());
|
||||
if (!nextNotify || !groupedItem) {
|
||||
break;
|
||||
}
|
||||
const auto nextMessageNotification
|
||||
= (nextNotify->type
|
||||
== ItemNotificationType::Message);
|
||||
== Data::ItemNotificationType::Message);
|
||||
const auto canNextBeGrouped = nextMessageNotification
|
||||
&& ((isForwarded && nextNotify->item->Has<HistoryMessageForwarded>())
|
||||
&& ((isForwarded
|
||||
&& nextNotify->item->Has<HistoryMessageForwarded>())
|
||||
|| (isAlbum && nextNotify->item->groupId()));
|
||||
const auto nextItem = canNextBeGrouped
|
||||
? nextNotify->item.get()
|
||||
|
@ -706,7 +708,7 @@ void System::showNext() {
|
|||
// to show the previous notification.
|
||||
showGrouped();
|
||||
const auto reactionNotification
|
||||
= (notify->type == ItemNotificationType::Reaction);
|
||||
= (notify->type == Data::ItemNotificationType::Reaction);
|
||||
const auto reaction = reactionNotification
|
||||
? notify->item->lookupUnreadReaction(notify->reactionSender)
|
||||
: Data::ReactionId();
|
||||
|
@ -720,9 +722,9 @@ void System::showNext() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!history->hasNotification()) {
|
||||
_waiters.remove(history);
|
||||
_whenMaps.remove(history);
|
||||
if (!thread->hasNotification()) {
|
||||
_waiters.remove(thread);
|
||||
_whenMaps.remove(thread);
|
||||
}
|
||||
}
|
||||
if (nextAlert) {
|
||||
|
@ -785,7 +787,7 @@ void System::playSound(not_null<Main::Session*> session, DocumentId id) {
|
|||
|
||||
Manager::DisplayOptions Manager::getNotificationOptions(
|
||||
HistoryItem *item,
|
||||
ItemNotificationType type) const {
|
||||
Data::ItemNotificationType type) const {
|
||||
const auto hideEverything = Core::App().passcodeLocked()
|
||||
|| forceHideDetails();
|
||||
const auto view = Core::App().settings().notifyView();
|
||||
|
@ -796,7 +798,7 @@ Manager::DisplayOptions Manager::getNotificationOptions(
|
|||
result.hideMessageText = hideEverything
|
||||
|| (view > Core::Settings::NotifyView::ShowPreview);
|
||||
result.hideMarkAsRead = result.hideMessageText
|
||||
|| (type != ItemNotificationType::Message)
|
||||
|| (type != Data::ItemNotificationType::Message)
|
||||
|| !item
|
||||
|| ((item->out() || item->history()->peer->isSelf())
|
||||
&& item->isFromScheduled());
|
||||
|
@ -1054,8 +1056,8 @@ void NativeManager::doShowNotification(NotificationFields &&fields) {
|
|||
const auto options = getNotificationOptions(
|
||||
fields.item,
|
||||
(fields.reactionFrom
|
||||
? ItemNotificationType::Reaction
|
||||
: ItemNotificationType::Message));
|
||||
? Data::ItemNotificationType::Reaction
|
||||
: Data::ItemNotificationType::Message));
|
||||
const auto item = fields.item;
|
||||
const auto peer = item->history()->peer;
|
||||
const auto reactionFrom = fields.reactionFrom;
|
||||
|
|
|
@ -12,13 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/timer.h"
|
||||
|
||||
class History;
|
||||
struct ItemNotification;
|
||||
enum class ItemNotificationType;
|
||||
|
||||
namespace Data {
|
||||
class Session;
|
||||
class CloudImageView;
|
||||
class ForumTopic;
|
||||
class Thread;
|
||||
struct ItemNotification;
|
||||
enum class ItemNotificationType;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
|
@ -31,17 +32,15 @@ class Manager;
|
|||
} // namespace Notifications
|
||||
} // namespace Platform
|
||||
|
||||
namespace Media {
|
||||
namespace Audio {
|
||||
namespace Media::Audio {
|
||||
class Track;
|
||||
} // namespace Audio
|
||||
} // namespace Media
|
||||
} // namespace Media::Audio
|
||||
|
||||
namespace Window {
|
||||
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Notifications {
|
||||
namespace Window::Notifications {
|
||||
|
||||
enum class ManagerType {
|
||||
Dummy,
|
||||
|
@ -62,19 +61,17 @@ enum class ChangeType {
|
|||
DemoIsHidden,
|
||||
};
|
||||
|
||||
} // namespace Notifications
|
||||
} // namespace Window
|
||||
} // namespace Window::Notifications
|
||||
|
||||
namespace base {
|
||||
|
||||
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 Window {
|
||||
namespace Notifications {
|
||||
namespace Window::Notifications {
|
||||
|
||||
class Manager;
|
||||
|
||||
|
@ -90,7 +87,7 @@ public:
|
|||
[[nodiscard]] ManagerType managerType() const;
|
||||
|
||||
void checkDelayed();
|
||||
void schedule(ItemNotification notification);
|
||||
void schedule(Data::ItemNotification notification);
|
||||
void clearFromTopic(not_null<Data::ForumTopic*> topic);
|
||||
void clearFromHistory(not_null<History*> history);
|
||||
void clearIncomingFromTopic(not_null<Data::ForumTopic*> topic);
|
||||
|
@ -123,18 +120,17 @@ private:
|
|||
bool silent = false;
|
||||
};
|
||||
struct NotificationInHistoryKey {
|
||||
NotificationInHistoryKey(ItemNotification notification);
|
||||
NotificationInHistoryKey(MsgId messageId, ItemNotificationType type);
|
||||
NotificationInHistoryKey(Data::ItemNotification notification);
|
||||
NotificationInHistoryKey(
|
||||
MsgId messageId,
|
||||
Data::ItemNotificationType type);
|
||||
|
||||
MsgId messageId = 0;
|
||||
ItemNotificationType type = ItemNotificationType();
|
||||
Data::ItemNotificationType type = Data::ItemNotificationType();
|
||||
|
||||
friend inline bool operator<(
|
||||
NotificationInHistoryKey a,
|
||||
NotificationInHistoryKey b) {
|
||||
return std::pair(a.messageId, a.type)
|
||||
< std::pair(b.messageId, b.type);
|
||||
}
|
||||
friend inline auto operator<=>(
|
||||
NotificationInHistoryKey a,
|
||||
NotificationInHistoryKey b) = default;
|
||||
};
|
||||
struct Timing {
|
||||
crl::time delay = 0;
|
||||
|
@ -152,10 +148,12 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
void clearForThreadIf(Fn<bool(not_null<Data::Thread*>)> predicate);
|
||||
|
||||
[[nodiscard]] SkipState skipNotification(
|
||||
ItemNotification notification) const;
|
||||
Data::ItemNotification notification) const;
|
||||
[[nodiscard]] SkipState computeSkipState(
|
||||
ItemNotification notification) const;
|
||||
Data::ItemNotification notification) const;
|
||||
[[nodiscard]] Timing countTiming(
|
||||
not_null<History*> history,
|
||||
crl::time minimalDelay) const;
|
||||
|
@ -170,16 +168,16 @@ private:
|
|||
DocumentId id);
|
||||
|
||||
base::flat_map<
|
||||
not_null<History*>,
|
||||
not_null<Data::Thread*>,
|
||||
base::flat_map<NotificationInHistoryKey, crl::time>> _whenMaps;
|
||||
|
||||
base::flat_map<not_null<History*>, Waiter> _waiters;
|
||||
base::flat_map<not_null<History*>, Waiter> _settingWaiters;
|
||||
base::flat_map<not_null<Data::Thread*>, Waiter> _waiters;
|
||||
base::flat_map<not_null<Data::Thread*>, Waiter> _settingWaiters;
|
||||
base::Timer _waitTimer;
|
||||
base::Timer _waitForAllGroupedTimer;
|
||||
|
||||
base::flat_map<
|
||||
not_null<History*>,
|
||||
not_null<Data::Thread*>,
|
||||
base::flat_map<crl::time, PeerData*>> _whenAlerts;
|
||||
|
||||
mutable base::flat_map<
|
||||
|
@ -270,7 +268,7 @@ public:
|
|||
};
|
||||
[[nodiscard]] DisplayOptions getNotificationOptions(
|
||||
HistoryItem *item,
|
||||
ItemNotificationType type) const;
|
||||
Data::ItemNotificationType type) const;
|
||||
[[nodiscard]] static TextWithEntities ComposeReactionEmoji(
|
||||
not_null<Main::Session*> session,
|
||||
const Data::ReactionId &reaction);
|
||||
|
@ -405,7 +403,6 @@ protected:
|
|||
|
||||
};
|
||||
|
||||
QString WrapFromScheduled(const QString &text);
|
||||
[[nodiscard]] QString WrapFromScheduled(const QString &text);
|
||||
|
||||
} // namespace Notifications
|
||||
} // namespace Window
|
||||
} // namespace Window::Notifications
|
||||
|
|
|
@ -823,8 +823,8 @@ void Notification::updateNotifyDisplay() {
|
|||
const auto options = manager()->getNotificationOptions(
|
||||
_item,
|
||||
(_reaction.empty()
|
||||
? ItemNotificationType::Message
|
||||
: ItemNotificationType::Reaction));
|
||||
? Data::ItemNotificationType::Message
|
||||
: Data::ItemNotificationType::Reaction));
|
||||
_hideReplyButton = options.hideReplyButton;
|
||||
|
||||
int32 w = width(), h = height();
|
||||
|
@ -1118,7 +1118,9 @@ bool Notification::unlinkSession(not_null<Main::Session*> session) {
|
|||
}
|
||||
|
||||
void Notification::enterEventHook(QEnterEvent *e) {
|
||||
if (!_history) return;
|
||||
if (!_history) {
|
||||
return;
|
||||
}
|
||||
manager()->stopAllHiding();
|
||||
if (!_replyArea && canReply()) {
|
||||
toggleActionButtons(true);
|
||||
|
@ -1126,7 +1128,9 @@ void Notification::enterEventHook(QEnterEvent *e) {
|
|||
}
|
||||
|
||||
void Notification::leaveEventHook(QEvent *e) {
|
||||
if (!_history) return;
|
||||
if (!_history) {
|
||||
return;
|
||||
}
|
||||
manager()->startAllHiding();
|
||||
toggleActionButtons(false);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue