mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Track forum unread state by topics inside.
This commit is contained in:
parent
a292f8a34e
commit
08ba277327
12 changed files with 116 additions and 30 deletions
|
@ -66,7 +66,9 @@ Data::ChatBotCommands::Changed MegagroupInfo::setBotCommands(
|
||||||
|
|
||||||
void MegagroupInfo::ensureForum(not_null<ChannelData*> that) {
|
void MegagroupInfo::ensureForum(not_null<ChannelData*> that) {
|
||||||
if (!_forum) {
|
if (!_forum) {
|
||||||
_forum = std::make_unique<Data::Forum>(that->owner().history(that));
|
const auto history = that->owner().history(that);
|
||||||
|
_forum = std::make_unique<Data::Forum>(history);
|
||||||
|
history->forumChanged(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +77,12 @@ Data::Forum *MegagroupInfo::forum() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Data::Forum> MegagroupInfo::takeForumData() {
|
std::unique_ptr<Data::Forum> MegagroupInfo::takeForumData() {
|
||||||
return std::move(_forum);
|
if (auto result = base::take(_forum)) {
|
||||||
|
result->history()->forumChanged(result.get());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
|
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
|
||||||
|
|
|
@ -212,7 +212,7 @@ bool ChatFilter::contains(not_null<History*> history) const {
|
||||||
&& history->folderKnown()
|
&& history->folderKnown()
|
||||||
&& !history->folder()))
|
&& !history->folder()))
|
||||||
&& (!(_flags & Flag::NoRead)
|
&& (!(_flags & Flag::NoRead)
|
||||||
|| history->unreadCount()
|
|| history->chatListUnreadCount()
|
||||||
|| history->unreadMark()
|
|| history->unreadMark()
|
||||||
|| history->unreadMentions().has()
|
|| history->unreadMentions().has()
|
||||||
|| history->fakeUnreadWhileOpened())
|
|| history->fakeUnreadWhileOpened())
|
||||||
|
|
|
@ -71,17 +71,6 @@ Folder::Folder(not_null<Data::Session*> owner, FolderId id)
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Folder::updateChatListEntryPostponed() {
|
|
||||||
if (_updateChatListEntryPostponed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_updateChatListEntryPostponed = true;
|
|
||||||
Ui::PostponeCall(this, [=] {
|
|
||||||
updateChatListEntry();
|
|
||||||
_updateChatListEntryPostponed = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderId Folder::id() const {
|
FolderId Folder::id() const {
|
||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,6 @@ private:
|
||||||
int chatListNameVersion() const override;
|
int chatListNameVersion() const override;
|
||||||
|
|
||||||
void reorderLastHistories();
|
void reorderLastHistories();
|
||||||
void updateChatListEntryPostponed();
|
|
||||||
|
|
||||||
void paintUserpic(
|
void paintUserpic(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
|
@ -101,7 +100,6 @@ private:
|
||||||
std::vector<not_null<History*>> _lastHistories;
|
std::vector<not_null<History*>> _lastHistories;
|
||||||
HistoryItem *_chatListMessage = nullptr;
|
HistoryItem *_chatListMessage = nullptr;
|
||||||
uint32 _chatListViewVersion = 0;
|
uint32 _chatListViewVersion = 0;
|
||||||
bool _updateChatListEntryPostponed = false;
|
|
||||||
//rpl::variable<MessagePosition> _unreadPosition;
|
//rpl::variable<MessagePosition> _unreadPosition;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace Data {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kTopicsFirstLoad = 20;
|
constexpr auto kTopicsFirstLoad = 20;
|
||||||
|
constexpr auto kLoadedTopicsMinCount = 20;
|
||||||
constexpr auto kTopicsPerPage = 500;
|
constexpr auto kTopicsPerPage = 500;
|
||||||
constexpr auto kGeneralColorId = 0xA9A9A9;
|
constexpr auto kGeneralColorId = 0xA9A9A9;
|
||||||
|
|
||||||
|
@ -39,6 +40,10 @@ Forum::Forum(not_null<History*> history)
|
||||||
: _history(history)
|
: _history(history)
|
||||||
, _topicsList(&session(), FilterId(0), rpl::single(1)) {
|
, _topicsList(&session(), FilterId(0), rpl::single(1)) {
|
||||||
Expects(_history->peer->isChannel());
|
Expects(_history->peer->isChannel());
|
||||||
|
|
||||||
|
if (_history->inChatList()) {
|
||||||
|
preloadTopics();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Forum::~Forum() {
|
Forum::~Forum() {
|
||||||
|
@ -89,6 +94,12 @@ rpl::producer<not_null<ForumTopic*>> Forum::topicDestroyed() const {
|
||||||
return _topicDestroyed.events();
|
return _topicDestroyed.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Forum::preloadTopics() {
|
||||||
|
if (topicsList()->indexed()->size() < kLoadedTopicsMinCount) {
|
||||||
|
requestTopics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Forum::requestTopics() {
|
void Forum::requestTopics() {
|
||||||
if (_allLoaded || _requestId) {
|
if (_allLoaded || _requestId) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -38,6 +38,7 @@ public:
|
||||||
[[nodiscard]] auto topicDestroyed() const
|
[[nodiscard]] auto topicDestroyed() const
|
||||||
-> rpl::producer<not_null<ForumTopic*>>;
|
-> rpl::producer<not_null<ForumTopic*>>;
|
||||||
|
|
||||||
|
void preloadTopics();
|
||||||
void requestTopics();
|
void requestTopics();
|
||||||
[[nodiscard]] rpl::producer<> chatsListChanges() const;
|
[[nodiscard]] rpl::producer<> chatsListChanges() const;
|
||||||
[[nodiscard]] rpl::producer<> chatsListLoadedEvents() const;
|
[[nodiscard]] rpl::producer<> chatsListLoadedEvents() const;
|
||||||
|
@ -68,6 +69,10 @@ public:
|
||||||
void clearAllUnreadReactions();
|
void clearAllUnreadReactions();
|
||||||
void enumerateTopics(Fn<void(not_null<ForumTopic*>)> action) const;
|
void enumerateTopics(Fn<void(not_null<ForumTopic*>)> action) const;
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
|
return _lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct TopicRequest {
|
struct TopicRequest {
|
||||||
mtpRequestId id = 0;
|
mtpRequestId id = 0;
|
||||||
|
@ -97,6 +102,8 @@ private:
|
||||||
rpl::event_stream<> _chatsListChanges;
|
rpl::event_stream<> _chatsListChanges;
|
||||||
rpl::event_stream<> _chatsListLoadedEvents;
|
rpl::event_stream<> _chatsListLoadedEvents;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -652,11 +652,7 @@ not_null<HistoryView::SendActionPainter*> ForumTopic::sendActionPainter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int ForumTopic::chatListUnreadCount() const {
|
int ForumTopic::chatListUnreadCount() const {
|
||||||
const auto state = chatListUnreadState();
|
return unreadCount();
|
||||||
return state.marks
|
|
||||||
+ (Core::App().settings().countUnreadMessages()
|
|
||||||
? state.messages
|
|
||||||
: state.chats);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialogs::UnreadState ForumTopic::chatListUnreadState() const {
|
Dialogs::UnreadState ForumTopic::chatListUnreadState() const {
|
||||||
|
|
|
@ -46,7 +46,11 @@ uint64 PinnedDialogPos(int pinnedIndex) {
|
||||||
|
|
||||||
Entry::Entry(not_null<Data::Session*> owner, Type type)
|
Entry::Entry(not_null<Data::Session*> owner, Type type)
|
||||||
: _owner(owner)
|
: _owner(owner)
|
||||||
, _type(type) {
|
, _flags((type == Type::History)
|
||||||
|
? (Flag::IsThread | Flag::IsHistory)
|
||||||
|
: (type == Type::ForumTopic)
|
||||||
|
? Flag::IsThread
|
||||||
|
: Flag(0)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry::~Entry() = default;
|
Entry::~Entry() = default;
|
||||||
|
@ -60,23 +64,23 @@ Main::Session &Entry::session() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
History *Entry::asHistory() {
|
History *Entry::asHistory() {
|
||||||
return (_type == Type::History) ? static_cast<History*>(this) : nullptr;
|
return (_flags & Flag::IsHistory) ? static_cast<History*>(this) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::Folder *Entry::asFolder() {
|
Data::Folder *Entry::asFolder() {
|
||||||
return (_type == Type::Folder)
|
return (_flags & Flag::IsThread)
|
||||||
? static_cast<Data::Folder*>(this)
|
? nullptr
|
||||||
: nullptr;
|
: static_cast<Data::Folder*>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::Thread *Entry::asThread() {
|
Data::Thread *Entry::asThread() {
|
||||||
return (_type == Type::History || _type == Type::ForumTopic)
|
return (_flags & Flag::IsThread)
|
||||||
? static_cast<Data::Thread*>(this)
|
? static_cast<Data::Thread*>(this)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::ForumTopic *Entry::asTopic() {
|
Data::ForumTopic *Entry::asTopic() {
|
||||||
return (_type == Type::ForumTopic)
|
return ((_flags & Flag::IsThread) && !(_flags & Flag::IsHistory))
|
||||||
? static_cast<Data::ForumTopic*>(this)
|
? static_cast<Data::ForumTopic*>(this)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +302,20 @@ void Entry::addChatListEntryByLetter(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Entry::updateChatListEntry() {
|
void Entry::updateChatListEntry() {
|
||||||
|
_flags &= ~Flag::UpdatePostponed;
|
||||||
session().changes().entryUpdated(this, Data::EntryUpdate::Flag::Repaint);
|
session().changes().entryUpdated(this, Data::EntryUpdate::Flag::Repaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entry::updateChatListEntryPostponed() {
|
||||||
|
if (_flags & Flag::UpdatePostponed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_flags |= Flag::UpdatePostponed;
|
||||||
|
Ui::PostponeCall(this, [=] {
|
||||||
|
if (_flags & Flag::UpdatePostponed) {
|
||||||
|
updateChatListEntry();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "base/flat_map.h"
|
#include "base/flat_map.h"
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
|
#include "base/flags.h"
|
||||||
#include "dialogs/dialogs_key.h"
|
#include "dialogs/dialogs_key.h"
|
||||||
#include "ui/unread_badge.h"
|
#include "ui/unread_badge.h"
|
||||||
|
|
||||||
|
@ -145,6 +146,7 @@ public:
|
||||||
QChar letter,
|
QChar letter,
|
||||||
not_null<Row*> row);
|
not_null<Row*> row);
|
||||||
void updateChatListEntry();
|
void updateChatListEntry();
|
||||||
|
void updateChatListEntryPostponed();
|
||||||
[[nodiscard]] bool isPinnedDialog(FilterId filterId) const {
|
[[nodiscard]] bool isPinnedDialog(FilterId filterId) const {
|
||||||
return lookupPinnedIndex(filterId) != 0;
|
return lookupPinnedIndex(filterId) != 0;
|
||||||
}
|
}
|
||||||
|
@ -214,6 +216,14 @@ protected:
|
||||||
[[nodiscard]] int lookupPinnedIndex(FilterId filterId) const;
|
[[nodiscard]] int lookupPinnedIndex(FilterId filterId) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class Flag : uchar {
|
||||||
|
IsThread = (1 << 0),
|
||||||
|
IsHistory = (1 << 1),
|
||||||
|
UpdatePostponed = (1 << 2),
|
||||||
|
};
|
||||||
|
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||||
|
using Flags = base::flags<Flag>;
|
||||||
|
|
||||||
virtual void changedChatListPinHook();
|
virtual void changedChatListPinHook();
|
||||||
void pinnedIndexChanged(FilterId filterId, int was, int now);
|
void pinnedIndexChanged(FilterId filterId, int was, int now);
|
||||||
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
||||||
|
@ -233,7 +243,7 @@ private:
|
||||||
mutable Ui::Text::String _chatListNameText;
|
mutable Ui::Text::String _chatListNameText;
|
||||||
mutable int _chatListNameVersion = 0;
|
mutable int _chatListNameVersion = 0;
|
||||||
TimeId _timeId = 0;
|
TimeId _timeId = 0;
|
||||||
const Type _type;
|
Flags _flags;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace {
|
||||||
const auto name = history->peer->name();
|
const auto name = history->peer->name();
|
||||||
return TextWithEntities{
|
return TextWithEntities{
|
||||||
.text = name,
|
.text = name,
|
||||||
.entities = (history->unreadCount() > 0)
|
.entities = (history->chatListUnreadCount() > 0)
|
||||||
? EntitiesInText{
|
? EntitiesInText{
|
||||||
{ EntityType::Semibold, 0, int(name.size()), QString() },
|
{ EntityType::Semibold, 0, int(name.size()), QString() },
|
||||||
{ EntityType::PlainLink, 0, int(name.size()), QString() },
|
{ EntityType::PlainLink, 0, int(name.size()), QString() },
|
||||||
|
|
|
@ -2067,6 +2067,13 @@ History *History::migrateSibling() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int History::chatListUnreadCount() const {
|
int History::chatListUnreadCount() const {
|
||||||
|
if (peer->isForum()) {
|
||||||
|
const auto state = chatListUnreadState();
|
||||||
|
return state.marks
|
||||||
|
+ (Core::App().settings().countUnreadMessages()
|
||||||
|
? state.messages
|
||||||
|
: state.chats);
|
||||||
|
}
|
||||||
const auto result = unreadCount();
|
const auto result = unreadCount();
|
||||||
if (const auto migrated = migrateSibling()) {
|
if (const auto migrated = migrateSibling()) {
|
||||||
return result + migrated->unreadCount();
|
return result + migrated->unreadCount();
|
||||||
|
@ -2084,10 +2091,24 @@ bool History::chatListUnreadMark() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool History::chatListMutedBadge() const {
|
bool History::chatListMutedBadge() const {
|
||||||
|
if (const auto forum = peer->forum()) {
|
||||||
|
const auto state = forum->topicsList()->unreadState();
|
||||||
|
return (state.marksMuted >= state.marks)
|
||||||
|
&& (Core::App().settings().countUnreadMessages()
|
||||||
|
? (state.messagesMuted >= state.messages)
|
||||||
|
: (state.chatsMuted >= state.chats));
|
||||||
|
}
|
||||||
return muted();
|
return muted();
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialogs::UnreadState History::chatListUnreadState() const {
|
Dialogs::UnreadState History::chatListUnreadState() const {
|
||||||
|
if (const auto forum = peer->forum()) {
|
||||||
|
return forum->topicsList()->unreadState();
|
||||||
|
}
|
||||||
|
return computeUnreadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialogs::UnreadState History::computeUnreadState() const {
|
||||||
auto result = Dialogs::UnreadState();
|
auto result = Dialogs::UnreadState();
|
||||||
const auto count = _unreadCount.value_or(0);
|
const auto count = _unreadCount.value_or(0);
|
||||||
const auto mark = !count && unreadMark();
|
const auto mark = !count && unreadMark();
|
||||||
|
@ -2639,6 +2660,10 @@ void History::applyDialog(
|
||||||
draft->c_draftMessage());
|
draft->c_draftMessage());
|
||||||
}
|
}
|
||||||
owner().histories().dialogEntryApplied(this);
|
owner().histories().dialogEntryApplied(this);
|
||||||
|
|
||||||
|
if (const auto forum = inChatList() ? peer->forum() : nullptr) {
|
||||||
|
forum->preloadTopics();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::dialogEntryApplied() {
|
void History::dialogEntryApplied() {
|
||||||
|
@ -2868,6 +2893,28 @@ const Data::Thread *History::threadFor(MsgId topicRootId) const {
|
||||||
return const_cast<History*>(this)->threadFor(topicRootId);
|
return const_cast<History*>(this)->threadFor(topicRootId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void History::forumChanged(Data::Forum *old) {
|
||||||
|
if (inChatList()) {
|
||||||
|
notifyUnreadStateChange(old
|
||||||
|
? old->topicsList()->unreadState()
|
||||||
|
: computeUnreadState());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto forum = peer->forum()) {
|
||||||
|
_flags |= Flag::IsForum;
|
||||||
|
|
||||||
|
forum->topicsList()->unreadStateChanges(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return (_flags & Flag::IsForum) && inChatList();
|
||||||
|
}) | rpl::start_with_next([=](const Dialogs::UnreadState &old) {
|
||||||
|
notifyUnreadStateChange(old);
|
||||||
|
updateChatListEntryPostponed();
|
||||||
|
}, forum->lifetime());
|
||||||
|
} else {
|
||||||
|
_flags &= ~Flag::IsForum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
not_null<History*> History::migrateToOrMe() const {
|
not_null<History*> History::migrateToOrMe() const {
|
||||||
if (const auto to = peer->migrateTo()) {
|
if (const auto to = peer->migrateTo()) {
|
||||||
return owner().history(to);
|
return owner().history(to);
|
||||||
|
|
|
@ -88,6 +88,8 @@ public:
|
||||||
return _delegateMixin.get();
|
return _delegateMixin.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void forumChanged(Data::Forum *old);
|
||||||
|
|
||||||
not_null<History*> migrateToOrMe() const;
|
not_null<History*> migrateToOrMe() const;
|
||||||
History *migrateFrom() const;
|
History *migrateFrom() const;
|
||||||
MsgRange rangeForDifferenceRequest() const;
|
MsgRange rangeForDifferenceRequest() const;
|
||||||
|
@ -461,6 +463,7 @@ private:
|
||||||
enum class Flag : uchar {
|
enum class Flag : uchar {
|
||||||
HasPendingResizedItems = (1 << 0),
|
HasPendingResizedItems = (1 << 0),
|
||||||
IsTopPromoted = (1 << 1),
|
IsTopPromoted = (1 << 1),
|
||||||
|
IsForum = (1 << 2),
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) {
|
friend inline constexpr auto is_flag_type(Flag) {
|
||||||
|
@ -571,6 +574,7 @@ private:
|
||||||
HistoryService *insertJoinedMessage();
|
HistoryService *insertJoinedMessage();
|
||||||
void insertMessageToBlocks(not_null<HistoryItem*> item);
|
void insertMessageToBlocks(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
[[nodiscard]] Dialogs::UnreadState computeUnreadState() const;
|
||||||
void setFolderPointer(Data::Folder *folder);
|
void setFolderPointer(Data::Folder *folder);
|
||||||
|
|
||||||
int chatListNameVersion() const override;
|
int chatListNameVersion() const override;
|
||||||
|
|
Loading…
Add table
Reference in a new issue