mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-21 13:12:50 +02:00
Support unread state in sublists.
This commit is contained in:
parent
4bc5e81513
commit
b2c01991a6
16 changed files with 1287 additions and 339 deletions
|
@ -2442,6 +2442,32 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
session().data().updateRepliesReadTill({ id, readTillId, true });
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadMonoForumInbox: {
|
||||
const auto &d = update.c_updateReadMonoForumInbox();
|
||||
const auto parentChatId = ChannelId(d.vchannel_id());
|
||||
const auto sublistPeerId = peerFromMTP(d.vsaved_peer_id());
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
session().data().updateSublistReadTill({
|
||||
parentChatId,
|
||||
sublistPeerId,
|
||||
readTillId,
|
||||
false,
|
||||
});
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadMonoForumOutbox: {
|
||||
const auto &d = update.c_updateReadMonoForumOutbox();
|
||||
const auto parentChatId = ChannelId(d.vchannel_id());
|
||||
const auto sublistPeerId = peerFromMTP(d.vsaved_peer_id());
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
session().data().updateSublistReadTill({
|
||||
parentChatId,
|
||||
sublistPeerId,
|
||||
readTillId,
|
||||
true,
|
||||
});
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelAvailableMessages: {
|
||||
auto &d = update.c_updateChannelAvailableMessages();
|
||||
if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
|
||||
|
|
|
@ -3197,11 +3197,21 @@ void ApiWrap::sendAction(const SendAction &action) {
|
|||
&& !action.options.shortcutId
|
||||
&& !action.replaceMediaOf) {
|
||||
const auto topicRootId = action.replyTo.topicRootId;
|
||||
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
|
||||
const auto topic = topicRootId
|
||||
? action.history->peer->forumTopicFor(topicRootId)
|
||||
: nullptr;
|
||||
const auto monoforum = monoforumPeerId
|
||||
? action.history->peer->monoforum()
|
||||
: nullptr;
|
||||
const auto sublist = monoforum
|
||||
? monoforum->sublistLoaded(
|
||||
action.history->owner().peer(monoforumPeerId))
|
||||
: nullptr;
|
||||
if (topic) {
|
||||
topic->readTillEnd();
|
||||
} else if (sublist) {
|
||||
sublist->readTillEnd();
|
||||
} else {
|
||||
_session->data().histories().readInbox(action.history);
|
||||
}
|
||||
|
|
|
@ -362,8 +362,8 @@ void ForumTopic::subscribeToUnreadChanges() {
|
|||
) | rpl::filter([=] {
|
||||
return inChatList();
|
||||
}) | rpl::start_with_next([=](
|
||||
std::optional<int> previous,
|
||||
std::optional<int> now) {
|
||||
std::optional<int> previous,
|
||||
std::optional<int> now) {
|
||||
if (previous.value_or(0) != now.value_or(0)) {
|
||||
_forum->recentTopicsInvalidate(this);
|
||||
}
|
||||
|
|
|
@ -58,8 +58,6 @@ public:
|
|||
[[nodiscard]] bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
|
||||
[[nodiscard]] std::optional<int> computeUnreadCountLocally(
|
||||
MsgId afterId) const;
|
||||
void requestUnreadCount();
|
||||
|
||||
void readTill(not_null<HistoryItem*> item);
|
||||
|
@ -79,6 +77,8 @@ private:
|
|||
|
||||
void subscribeToUpdates();
|
||||
void appendClientSideMessages(MessagesSlice &slice);
|
||||
[[nodiscard]] std::optional<int> computeUnreadCountLocally(
|
||||
MsgId afterId) const;
|
||||
|
||||
[[nodiscard]] bool buildFromData(not_null<Viewer*> viewer);
|
||||
[[nodiscard]] bool applyItemDestroyed(
|
||||
|
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
|
@ -34,13 +34,15 @@ SavedMessages::SavedMessages(
|
|||
ChannelData *parentChat)
|
||||
: _owner(owner)
|
||||
, _parentChat(parentChat)
|
||||
, _parentHistory(parentChat ? owner->history(parentChat).get() : nullptr)
|
||||
, _owningHistory(parentChat ? owner->history(parentChat).get() : nullptr)
|
||||
, _chatsList(
|
||||
&_owner->session(),
|
||||
FilterId(),
|
||||
_owner->maxPinnedChatsLimitValue(this))
|
||||
, _loadMore([=] { sendLoadMoreRequests(); }) {
|
||||
if (_parentHistory && _parentHistory->inChatList()) {
|
||||
// We don't assign _owningHistory for my Saved Messages here,
|
||||
// because the data structures are not ready yet.
|
||||
if (_owningHistory && _owningHistory->inChatList()) {
|
||||
preloadSublists();
|
||||
}
|
||||
}
|
||||
|
@ -51,10 +53,22 @@ bool SavedMessages::supported() const {
|
|||
return !_unsupported;
|
||||
}
|
||||
|
||||
void SavedMessages::markUnsupported() {
|
||||
_unsupported = true;
|
||||
}
|
||||
|
||||
ChannelData *SavedMessages::parentChat() const {
|
||||
return _parentChat;
|
||||
}
|
||||
|
||||
not_null<History*> SavedMessages::owningHistory() const {
|
||||
if (!_owningHistory) {
|
||||
const_cast<SavedMessages*>(this)->_owningHistory
|
||||
= _owner->history(_owner->session().user());
|
||||
}
|
||||
return _owningHistory;
|
||||
}
|
||||
|
||||
Session &SavedMessages::owner() const {
|
||||
return *_owner;
|
||||
}
|
||||
|
@ -101,11 +115,6 @@ void SavedMessages::loadMore() {
|
|||
_loadMore.call();
|
||||
}
|
||||
|
||||
void SavedMessages::loadMore(not_null<SavedSublist*> sublist) {
|
||||
_loadMoreSublistsScheduled.emplace(sublist);
|
||||
_loadMore.call();
|
||||
}
|
||||
|
||||
void SavedMessages::sendLoadMore() {
|
||||
if (_loadMoreRequestId || _chatsList.loaded()) {
|
||||
return;
|
||||
|
@ -132,7 +141,7 @@ void SavedMessages::sendLoadMore() {
|
|||
reorderLastSublists();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
markUnsupported();
|
||||
}
|
||||
_chatsList.setLoaded();
|
||||
_loadMoreRequestId = 0;
|
||||
|
@ -150,7 +159,7 @@ void SavedMessages::loadPinned() {
|
|||
_chatsListChanges.fire({});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
markUnsupported();
|
||||
} else {
|
||||
_pinnedLoaded = true;
|
||||
}
|
||||
|
@ -158,82 +167,6 @@ void SavedMessages::loadPinned() {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void SavedMessages::sendLoadMore(not_null<SavedSublist*> sublist) {
|
||||
if (_loadMoreRequests.contains(sublist) || sublist->isFullLoaded()) {
|
||||
return;
|
||||
}
|
||||
const auto &list = sublist->messages();
|
||||
const auto offsetId = list.empty() ? MsgId(0) : list.back()->id;
|
||||
const auto offsetDate = list.empty() ? MsgId(0) : list.back()->date();
|
||||
const auto limit = offsetId ? kPerPage : kFirstPerPage;
|
||||
using Flag = MTPmessages_GetSavedHistory::Flag;
|
||||
const auto requestId = _owner->session().api().request(
|
||||
MTPmessages_GetSavedHistory(
|
||||
MTP_flags(_parentChat ? Flag::f_parent_peer : Flag(0)),
|
||||
_parentChat ? _parentChat->input : MTPInputPeer(),
|
||||
sublist->sublistPeer()->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(offsetDate),
|
||||
MTP_int(0), // add_offset
|
||||
MTP_int(limit),
|
||||
MTP_int(0), // max_id
|
||||
MTP_int(0), // min_id
|
||||
MTP_long(0)) // hash
|
||||
).done([=](const MTPmessages_Messages &result) {
|
||||
auto count = 0;
|
||||
auto list = (const QVector<MTPMessage>*)nullptr;
|
||||
result.match([&](const MTPDmessages_channelMessages &data) {
|
||||
if (const auto channel = _parentChat) {
|
||||
channel->ptsReceived(data.vpts().v);
|
||||
channel->processTopics(data.vtopics());
|
||||
list = &data.vmessages().v;
|
||||
count = data.vcount().v;
|
||||
} else {
|
||||
LOG(("API Error: messages.channelMessages in sublist."));
|
||||
}
|
||||
}, [](const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: messages.messagesNotModified in sublist."));
|
||||
}, [&](const auto &data) {
|
||||
owner().processUsers(data.vusers());
|
||||
owner().processChats(data.vchats());
|
||||
list = &data.vmessages().v;
|
||||
if constexpr (MTPDmessages_messages::Is<decltype(data)>()) {
|
||||
count = int(list->size());
|
||||
} else {
|
||||
count = data.vcount().v;
|
||||
}
|
||||
});
|
||||
|
||||
_loadMoreRequests.remove(sublist);
|
||||
if (!list) {
|
||||
sublist->setFullLoaded();
|
||||
return;
|
||||
}
|
||||
auto items = std::vector<not_null<HistoryItem*>>();
|
||||
items.reserve(list->size());
|
||||
for (const auto &message : *list) {
|
||||
const auto item = owner().addNewMessage(
|
||||
message,
|
||||
{},
|
||||
NewMessageType::Existing);
|
||||
if (item) {
|
||||
items.push_back(item);
|
||||
}
|
||||
}
|
||||
sublist->append(std::move(items), count);
|
||||
if (result.type() == mtpc_messages_messages) {
|
||||
sublist->setFullLoaded();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
}
|
||||
sublist->setFullLoaded();
|
||||
_loadMoreRequests.remove(sublist);
|
||||
}).send();
|
||||
_loadMoreRequests[sublist] = requestId;
|
||||
}
|
||||
|
||||
void SavedMessages::apply(
|
||||
const MTPmessages_SavedDialogs &result,
|
||||
bool pinned) {
|
||||
|
@ -291,8 +224,7 @@ void SavedMessages::apply(
|
|||
offsetDate = item->date();
|
||||
offsetId = topId;
|
||||
lastValid = true;
|
||||
const auto entry = sublist(peer);
|
||||
entry->applyMaybeLast(item);
|
||||
sublist(peer)->applyMonoforumDialog(data, item);
|
||||
} else {
|
||||
lastValid = false;
|
||||
}
|
||||
|
@ -321,9 +253,6 @@ void SavedMessages::sendLoadMoreRequests() {
|
|||
if (_loadMoreScheduled) {
|
||||
sendLoadMore();
|
||||
}
|
||||
for (const auto sublist : base::take(_loadMoreSublistsScheduled)) {
|
||||
sendLoadMore(sublist);
|
||||
}
|
||||
}
|
||||
|
||||
void SavedMessages::apply(const MTPDupdatePinnedSavedDialogs &update) {
|
||||
|
@ -371,7 +300,7 @@ void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) {
|
|||
}
|
||||
|
||||
void SavedMessages::reorderLastSublists() {
|
||||
if (!_parentHistory) {
|
||||
if (!_parentChat) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -411,7 +340,7 @@ void SavedMessages::reorderLastSublists() {
|
|||
}
|
||||
}
|
||||
++_lastSublistsVersion;
|
||||
_parentHistory->updateChatListEntry();
|
||||
owningHistory()->updateChatListEntry();
|
||||
}
|
||||
|
||||
void SavedMessages::listMessageChanged(HistoryItem *from, HistoryItem *to) {
|
||||
|
@ -426,11 +355,11 @@ int SavedMessages::recentSublistsListVersion() const {
|
|||
|
||||
void SavedMessages::recentSublistsInvalidate(
|
||||
not_null<SavedSublist*> sublist) {
|
||||
Expects(_parentHistory != nullptr);
|
||||
Expects(_parentChat != nullptr);
|
||||
|
||||
if (ranges::contains(_lastSublists, sublist)) {
|
||||
++_lastSublistsVersion;
|
||||
_parentHistory->updateChatListEntry();
|
||||
owningHistory()->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,10 @@ public:
|
|||
~SavedMessages();
|
||||
|
||||
[[nodiscard]] bool supported() const;
|
||||
void markUnsupported();
|
||||
|
||||
[[nodiscard]] ChannelData *parentChat() const;
|
||||
[[nodiscard]] not_null<History*> owningHistory() const;
|
||||
|
||||
[[nodiscard]] Session &owner() const;
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
|
@ -44,7 +47,6 @@ public:
|
|||
|
||||
void preloadSublists();
|
||||
void loadMore();
|
||||
void loadMore(not_null<SavedSublist*> sublist);
|
||||
|
||||
void apply(const MTPDupdatePinnedSavedDialogs &update);
|
||||
void apply(const MTPDupdateSavedDialogPinned &update);
|
||||
|
@ -64,12 +66,11 @@ private:
|
|||
void reorderLastSublists();
|
||||
|
||||
void sendLoadMore();
|
||||
void sendLoadMore(not_null<SavedSublist*> sublist);
|
||||
void sendLoadMoreRequests();
|
||||
|
||||
const not_null<Session*> _owner;
|
||||
ChannelData *_parentChat = nullptr;
|
||||
History *_parentHistory = nullptr;
|
||||
History *_owningHistory = nullptr;
|
||||
|
||||
rpl::event_stream<not_null<SavedSublist*>> _sublistDestroyed;
|
||||
|
||||
|
@ -78,7 +79,6 @@ private:
|
|||
not_null<PeerData*>,
|
||||
std::unique_ptr<SavedSublist>> _sublists;
|
||||
|
||||
base::flat_map<not_null<SavedSublist*>, mtpRequestId> _loadMoreRequests;
|
||||
mtpRequestId _loadMoreRequestId = 0;
|
||||
mtpRequestId _pinnedRequestId = 0;
|
||||
|
||||
|
@ -87,7 +87,6 @@ private:
|
|||
PeerData *_offsetPeer = nullptr;
|
||||
|
||||
SingleQueuedInvokation _loadMore;
|
||||
base::flat_set<not_null<SavedSublist*>> _loadMoreSublistsScheduled;
|
||||
bool _loadMoreScheduled = false;
|
||||
|
||||
std::vector<not_null<SavedSublist*>> _lastSublists;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
|
||||
|
@ -16,31 +17,60 @@ class History;
|
|||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class Histories;
|
||||
class SavedMessages;
|
||||
struct MessagePosition;
|
||||
struct MessageUpdate;
|
||||
struct SublistReadTillUpdate;
|
||||
struct MessagesSlice;
|
||||
|
||||
class SavedSublist final : public Data::Thread {
|
||||
public:
|
||||
SavedSublist(not_null<SavedMessages*> parent, not_null<PeerData*> peer);
|
||||
SavedSublist(
|
||||
not_null<SavedMessages*> parent,
|
||||
not_null<PeerData*> sublistPeer);
|
||||
~SavedSublist();
|
||||
|
||||
[[nodiscard]] bool inMonoforum() const;
|
||||
|
||||
void apply(const SublistReadTillUpdate &update);
|
||||
void apply(const MessageUpdate &update);
|
||||
void applyDifferenceTooLong();
|
||||
bool removeOne(not_null<HistoryItem*> item);
|
||||
|
||||
[[nodiscard]] rpl::producer<MessagesSlice> source(
|
||||
MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter);
|
||||
|
||||
[[nodiscard]] not_null<SavedMessages*> parent() const;
|
||||
[[nodiscard]] not_null<History*> owningHistory() override;
|
||||
[[nodiscard]] ChannelData *parentChat() const;
|
||||
[[nodiscard]] not_null<PeerData*> sublistPeer() const;
|
||||
[[nodiscard]] bool isHiddenAuthor() const;
|
||||
[[nodiscard]] bool isFullLoaded() const;
|
||||
[[nodiscard]] rpl::producer<> destroyed() const;
|
||||
|
||||
[[nodiscard]] auto messages() const
|
||||
-> const std::vector<not_null<HistoryItem*>> &;
|
||||
void growLastKnownServerMessageId(MsgId id);
|
||||
void applyMaybeLast(not_null<HistoryItem*> item, bool added = false);
|
||||
void removeOne(not_null<HistoryItem*> item);
|
||||
void append(std::vector<not_null<HistoryItem*>> &&items, int fullCount);
|
||||
void setFullLoaded(bool loaded = true);
|
||||
void applyItemAdded(not_null<HistoryItem*> item);
|
||||
void applyItemRemoved(MsgId id);
|
||||
|
||||
[[nodiscard]] rpl::producer<> changes() const;
|
||||
[[nodiscard]] std::optional<int> fullCount() const;
|
||||
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
||||
[[nodiscard]] rpl::producer<std::optional<int>> maybeFullCount() const;
|
||||
void loadFullCount();
|
||||
|
||||
[[nodiscard]] bool unreadCountKnown() const;
|
||||
[[nodiscard]] int unreadCountCurrent() const;
|
||||
[[nodiscard]] int displayedUnreadCount() const;
|
||||
[[nodiscard]] rpl::producer<std::optional<int>> unreadCountValue() const;
|
||||
|
||||
void applyMonoforumDialog(
|
||||
const MTPDmonoForumDialog &dialog,
|
||||
not_null<HistoryItem*> topItem);
|
||||
void readTillEnd();
|
||||
void requestChatListMessage();
|
||||
|
||||
int fixedOnTopIndex() const override;
|
||||
bool shouldBeInChatList() const override;
|
||||
|
@ -57,9 +87,27 @@ public:
|
|||
void hasUnreadMentionChanged(bool has) override;
|
||||
void hasUnreadReactionChanged(bool has) override;
|
||||
|
||||
[[nodiscard]] HistoryItem *lastMessage() const;
|
||||
[[nodiscard]] HistoryItem *lastServerMessage() const;
|
||||
[[nodiscard]] bool lastMessageKnown() const;
|
||||
[[nodiscard]] bool lastServerMessageKnown() const;
|
||||
[[nodiscard]] MsgId lastKnownServerMessageId() const;
|
||||
|
||||
void setInboxReadTill(MsgId readTillId, std::optional<int> unreadCount);
|
||||
[[nodiscard]] MsgId inboxReadTillId() const;
|
||||
[[nodiscard]] MsgId computeInboxReadTillFull() const;
|
||||
|
||||
void setOutboxReadTill(MsgId readTillId);
|
||||
[[nodiscard]] MsgId computeOutboxReadTillFull() const;
|
||||
|
||||
[[nodiscard]] bool isServerSideUnread(
|
||||
not_null<const HistoryItem*> item) const override;
|
||||
|
||||
void requestUnreadCount();
|
||||
|
||||
void readTill(not_null<HistoryItem*> item);
|
||||
void readTill(MsgId tillId);
|
||||
|
||||
void chatListPreloadData() override;
|
||||
void paintUserpic(
|
||||
Painter &p,
|
||||
|
@ -70,25 +118,75 @@ public:
|
|||
-> HistoryView::SendActionPainter* override;
|
||||
|
||||
private:
|
||||
struct Viewer;
|
||||
|
||||
enum class Flag : uchar {
|
||||
ResolveChatListMessage = (1 << 0),
|
||||
FullLoaded = (1 << 1),
|
||||
InMonoforum = (1 << 1),
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||
using Flags = base::flags<Flag>;
|
||||
|
||||
bool hasOrphanMediaGroupPart() const;
|
||||
[[nodiscard]] Histories &histories();
|
||||
|
||||
void subscribeToUnreadChanges();
|
||||
[[nodiscard]] Dialogs::UnreadState unreadStateFor(
|
||||
int count,
|
||||
bool known) const;
|
||||
void setLastMessage(HistoryItem *item);
|
||||
void setLastServerMessage(HistoryItem *item);
|
||||
void setChatListMessage(HistoryItem *item);
|
||||
void allowChatListMessageResolve();
|
||||
void resolveChatListMessageGroup();
|
||||
|
||||
const not_null<SavedMessages*> _parent;
|
||||
const not_null<History*> _history;
|
||||
void changeUnreadCountByMessage(MsgId id, int delta);
|
||||
void setUnreadCount(std::optional<int> count);
|
||||
void readTill(MsgId tillId, HistoryItem *tillIdItem);
|
||||
void checkReadTillEnd();
|
||||
void sendReadTillRequest();
|
||||
void reloadUnreadCountIfNeeded();
|
||||
|
||||
std::vector<not_null<HistoryItem*>> _items;
|
||||
std::optional<int> _fullCount;
|
||||
rpl::event_stream<> _changed;
|
||||
[[nodiscard]] bool buildFromData(not_null<Viewer*> viewer);
|
||||
[[nodiscard]] bool applyUpdate(const MessageUpdate &update);
|
||||
void appendClientSideMessages(MessagesSlice &slice);
|
||||
[[nodiscard]] std::optional<int> computeUnreadCountLocally(
|
||||
MsgId afterId) const;
|
||||
bool processMessagesIsEmpty(const MTPmessages_Messages &result);
|
||||
void loadAround(MsgId id);
|
||||
void loadBefore();
|
||||
void loadAfter();
|
||||
|
||||
const not_null<SavedMessages*> _parent;
|
||||
const not_null<History*> _sublistHistory;
|
||||
|
||||
MsgId _lastKnownServerMessageId = 0;
|
||||
|
||||
std::vector<MsgId> _list;
|
||||
std::optional<int> _skippedBefore;
|
||||
std::optional<int> _skippedAfter;
|
||||
rpl::variable<std::optional<int>> _fullCount;
|
||||
rpl::event_stream<> _listChanges;
|
||||
rpl::event_stream<> _instantChanges;
|
||||
std::optional<MsgId> _loadingAround;
|
||||
rpl::variable<std::optional<int>> _unreadCount;
|
||||
MsgId _inboxReadTillId = 0;
|
||||
MsgId _outboxReadTillId = 0;
|
||||
Flags _flags;
|
||||
|
||||
std::optional<HistoryItem*> _lastMessage;
|
||||
std::optional<HistoryItem*> _lastServerMessage;
|
||||
std::optional<HistoryItem*> _chatListMessage;
|
||||
base::flat_set<FullMsgId> _requestedGroups;
|
||||
int _beforeId = 0;
|
||||
int _afterId = 0;
|
||||
|
||||
base::Timer _readRequestTimer;
|
||||
mtpRequestId _readRequestId = 0;
|
||||
|
||||
mtpRequestId _reloadUnreadCountRequestId = 0;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -341,6 +341,19 @@ void Session::subscribeForTopicRepliesLists() {
|
|||
}
|
||||
}, _lifetime);
|
||||
|
||||
sublistReadTillUpdates(
|
||||
) | rpl::start_with_next([=](const SublistReadTillUpdate &update) {
|
||||
if (const auto parentChat = channelLoaded(update.parentChatId)) {
|
||||
if (const auto monoforum = parentChat->monoforum()) {
|
||||
const auto sublistPeerId = update.sublistPeerId;
|
||||
const auto peer = monoforum->owner().peer(sublistPeerId);
|
||||
if (const auto sublist = monoforum->sublistLoaded(peer)) {
|
||||
sublist->apply(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
session().changes().messageUpdates(
|
||||
MessageUpdate::Flag::NewAdded
|
||||
| MessageUpdate::Flag::NewMaybeAdded
|
||||
|
@ -349,6 +362,11 @@ void Session::subscribeForTopicRepliesLists() {
|
|||
) | rpl::start_with_next([=](const MessageUpdate &update) {
|
||||
if (const auto topic = update.item->topic()) {
|
||||
topic->replies()->apply(update);
|
||||
} else if (update.flags == MessageUpdate::Flag::ReplyToTopAdded) {
|
||||
// Not interested in this one for sublist.
|
||||
return;
|
||||
} else if (const auto sublist = update.item->savedSublist()) {
|
||||
sublist->apply(update);
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
|
@ -2914,6 +2932,15 @@ auto Session::repliesReadTillUpdates() const
|
|||
return _repliesReadTillUpdates.events();
|
||||
}
|
||||
|
||||
void Session::updateSublistReadTill(SublistReadTillUpdate update) {
|
||||
_sublistReadTillUpdates.fire(std::move(update));
|
||||
}
|
||||
|
||||
auto Session::sublistReadTillUpdates() const
|
||||
-> rpl::producer<SublistReadTillUpdate> {
|
||||
return _sublistReadTillUpdates.events();
|
||||
}
|
||||
|
||||
int Session::computeUnreadBadge(const Dialogs::UnreadState &state) const {
|
||||
const auto all = Core::App().settings().includeMutedCounter();
|
||||
return std::max(state.marks - (all ? 0 : state.marksMuted), 0)
|
||||
|
|
|
@ -80,6 +80,13 @@ struct RepliesReadTillUpdate {
|
|||
bool out = false;
|
||||
};
|
||||
|
||||
struct SublistReadTillUpdate {
|
||||
ChannelId parentChatId;
|
||||
PeerId sublistPeerId;
|
||||
MsgId readTillId;
|
||||
bool out = false;
|
||||
};
|
||||
|
||||
struct GiftUpdate {
|
||||
enum class Action : uchar {
|
||||
Save,
|
||||
|
@ -565,6 +572,10 @@ public:
|
|||
[[nodiscard]] auto repliesReadTillUpdates() const
|
||||
-> rpl::producer<RepliesReadTillUpdate>;
|
||||
|
||||
void updateSublistReadTill(SublistReadTillUpdate update);
|
||||
[[nodiscard]] auto sublistReadTillUpdates() const
|
||||
-> rpl::producer<SublistReadTillUpdate>;
|
||||
|
||||
void selfDestructIn(not_null<HistoryItem*> item, crl::time delay);
|
||||
|
||||
[[nodiscard]] not_null<PhotoData*> photo(PhotoId id);
|
||||
|
@ -1004,6 +1015,7 @@ private:
|
|||
rpl::event_stream<ChatListEntryRefresh> _chatListEntryRefreshes;
|
||||
rpl::event_stream<> _unreadBadgeChanges;
|
||||
rpl::event_stream<RepliesReadTillUpdate> _repliesReadTillUpdates;
|
||||
rpl::event_stream<SublistReadTillUpdate> _sublistReadTillUpdates;
|
||||
rpl::event_stream<SentToScheduled> _sentToScheduled;
|
||||
rpl::event_stream<SentFromScheduled> _sentFromScheduled;
|
||||
|
||||
|
|
|
@ -163,6 +163,9 @@ void History::itemRemoved(not_null<HistoryItem*> item) {
|
|||
if (const auto topic = item->topic()) {
|
||||
topic->applyItemRemoved(item->id);
|
||||
}
|
||||
if (const auto sublist = item->savedSublist()) {
|
||||
sublist->applyItemRemoved(item->id);
|
||||
}
|
||||
if (const auto chat = peer->asChat()) {
|
||||
if (const auto to = chat->getMigrateToChannel()) {
|
||||
if (const auto history = owner().historyLoaded(to)) {
|
||||
|
@ -1311,6 +1314,9 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
|||
if (const auto topic = item->topic()) {
|
||||
topic->applyItemAdded(item);
|
||||
}
|
||||
if (const auto sublist = item->savedSublist()) {
|
||||
sublist->applyItemAdded(item);
|
||||
}
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto gift = media->gift()) {
|
||||
if (const auto unique = gift->unique.get()) {
|
||||
|
|
|
@ -789,16 +789,6 @@ HistoryItem::~HistoryItem() {
|
|||
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||
reply->clearData(this);
|
||||
}
|
||||
if (const auto saved = Get<HistoryMessageSaved>()) {
|
||||
if (saved->savedMessagesSublist) {
|
||||
saved->savedMessagesSublist->removeOne(this);
|
||||
} else if (const auto monoforum = _history->peer->monoforum()) {
|
||||
const auto peer = _history->owner().peer(saved->sublistPeerId);
|
||||
if (const auto sublist = monoforum->sublistLoaded(peer)) {
|
||||
sublist->removeOne(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
clearDependencyMessage();
|
||||
applyTTL(0);
|
||||
}
|
||||
|
|
|
@ -191,6 +191,13 @@ object_ptr<Window::SectionWidget> ChatMemento::createWidget(
|
|||
_list.setScrollTopState(ListMemento::ScrollTopState{
|
||||
Data::MinMessagePosition
|
||||
});
|
||||
} else if (!_list.aroundPosition().fullId
|
||||
&& _id.sublist
|
||||
&& _id.sublist->computeInboxReadTillFull() == MsgId(1)) {
|
||||
_list.setAroundPosition(Data::MinMessagePosition);
|
||||
_list.setScrollTopState(ListMemento::ScrollTopState{
|
||||
Data::MinMessagePosition
|
||||
});
|
||||
}
|
||||
auto result = object_ptr<ChatWidget>(parent, controller, _id);
|
||||
result->setInternalState(geometry, this);
|
||||
|
@ -394,7 +401,9 @@ ChatWidget::ChatWidget(
|
|||
}
|
||||
}, lifetime());
|
||||
|
||||
if (!_topic) {
|
||||
if (_sublist) {
|
||||
subscribeToSublist();
|
||||
} else if (!_topic) {
|
||||
_history->session().changes().historyUpdates(
|
||||
_history,
|
||||
Data::HistoryUpdate::Flag::OutboxRead
|
||||
|
@ -2455,6 +2464,19 @@ void ChatWidget::setReplies(std::shared_ptr<Data::RepliesList> replies) {
|
|||
}, _repliesLifetime);
|
||||
}
|
||||
|
||||
void ChatWidget::subscribeToSublist() {
|
||||
Expects(_sublist != nullptr);
|
||||
|
||||
_sublist->unreadCountValue(
|
||||
) | rpl::start_with_next([=](std::optional<int> count) {
|
||||
refreshUnreadCountBadge(count);
|
||||
}, lifetime());
|
||||
|
||||
refreshUnreadCountBadge(_sublist->unreadCountKnown()
|
||||
? _sublist->unreadCountCurrent()
|
||||
: std::optional<int>());
|
||||
}
|
||||
|
||||
void ChatWidget::restoreState(not_null<ChatMemento*> memento) {
|
||||
if (auto replies = memento->getReplies()) {
|
||||
setReplies(std::move(replies));
|
||||
|
@ -2792,54 +2814,20 @@ rpl::producer<Data::MessagesSlice> ChatWidget::sublistSource(
|
|||
const auto messageId = aroundId.fullId.msg
|
||||
? aroundId.fullId.msg
|
||||
: (ServerMaxMsgId - 1);
|
||||
return [=](auto consumer) {
|
||||
const auto pushSlice = [=] {
|
||||
auto result = Data::MessagesSlice();
|
||||
result.fullCount = _sublist->fullCount();
|
||||
_topBar->setCustomTitle(result.fullCount
|
||||
? tr::lng_forum_messages(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
*result.fullCount)
|
||||
: tr::lng_contacts_loading(tr::now));
|
||||
const auto &messages = _sublist->messages();
|
||||
const auto i = ranges::lower_bound(
|
||||
messages,
|
||||
messageId,
|
||||
ranges::greater(),
|
||||
[](not_null<HistoryItem*> item) { return item->id; });
|
||||
const auto before = int(end(messages) - i);
|
||||
const auto useBefore = std::min(before, limitBefore);
|
||||
const auto after = int(i - begin(messages));
|
||||
const auto useAfter = std::min(after, limitAfter);
|
||||
const auto from = i - useAfter;
|
||||
const auto till = i + useBefore;
|
||||
auto nearestDistance = std::numeric_limits<int64>::max();
|
||||
result.ids.reserve(useAfter + useBefore);
|
||||
for (auto j = till; j != from;) {
|
||||
const auto item = *--j;
|
||||
result.ids.push_back(item->fullId());
|
||||
const auto distance = std::abs((messageId - item->id).bare);
|
||||
if (nearestDistance > distance) {
|
||||
nearestDistance = distance;
|
||||
result.nearestToAround = result.ids.back();
|
||||
}
|
||||
}
|
||||
result.skippedAfter = after - useAfter;
|
||||
result.skippedBefore = result.fullCount
|
||||
? (*result.fullCount - after - useBefore)
|
||||
: std::optional<int>();
|
||||
if (!result.fullCount || useBefore < limitBefore) {
|
||||
_sublist->parent()->loadMore(_sublist);
|
||||
}
|
||||
markLoaded();
|
||||
consumer.put_next(std::move(result));
|
||||
};
|
||||
auto lifetime = rpl::lifetime();
|
||||
_sublist->changes() | rpl::start_with_next(pushSlice, lifetime);
|
||||
pushSlice();
|
||||
return lifetime;
|
||||
};
|
||||
return _sublist->source(
|
||||
aroundId,
|
||||
limitBefore,
|
||||
limitAfter
|
||||
) | rpl::before_next([=](const Data::MessagesSlice &result) {
|
||||
// after_next makes a copy of value.
|
||||
_topBar->setCustomTitle(result.fullCount
|
||||
? tr::lng_forum_messages(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
*result.fullCount)
|
||||
: tr::lng_contacts_loading(tr::now));
|
||||
markLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
bool ChatWidget::listAllowsMultiSelect() {
|
||||
|
@ -2882,6 +2870,8 @@ void ChatWidget::listSelectionChanged(SelectedItems &&items) {
|
|||
void ChatWidget::listMarkReadTill(not_null<HistoryItem*> item) {
|
||||
if (_replies) {
|
||||
_replies->readTill(item);
|
||||
} else if (_sublist) {
|
||||
_sublist->readTill(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2892,16 +2882,22 @@ void ChatWidget::listMarkContentsRead(
|
|||
|
||||
MessagesBarData ChatWidget::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
if (_sublist || elements.empty()) {
|
||||
if ((!_sublist && !_replies) || elements.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto till = _replies->computeInboxReadTillFull();
|
||||
const auto till = _replies
|
||||
? _replies->computeInboxReadTillFull()
|
||||
: _sublist->computeInboxReadTillFull();
|
||||
const auto hidden = (till < 2);
|
||||
for (auto i = 0, count = int(elements.size()); i != count; ++i) {
|
||||
const auto item = elements[i]->data();
|
||||
if (item->isRegular() && item->id > till) {
|
||||
if (item->out() || !item->replyToId()) {
|
||||
_replies->readTill(item);
|
||||
if (item->out() || (_replies && !item->replyToId())) {
|
||||
if (_replies) {
|
||||
_replies->readTill(item);
|
||||
} else {
|
||||
_sublist->readTill(item);
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
.bar = {
|
||||
|
@ -2960,9 +2956,12 @@ bool ChatWidget::listElementHideReply(not_null<const Element*> view) {
|
|||
}
|
||||
|
||||
bool ChatWidget::listElementShownUnread(not_null<const Element*> view) {
|
||||
const auto item = view->data();
|
||||
return _replies
|
||||
? _replies->isServerSideUnread(view->data())
|
||||
: view->data()->unread(view->data()->history());
|
||||
? _replies->isServerSideUnread(item)
|
||||
: _sublist
|
||||
? _sublist->isServerSideUnread(item)
|
||||
: item->unread(item->history());
|
||||
}
|
||||
|
||||
bool ChatWidget::listIsGoodForAroundPosition(
|
||||
|
@ -2973,7 +2972,7 @@ bool ChatWidget::listIsGoodForAroundPosition(
|
|||
void ChatWidget::listSendBotCommand(
|
||||
const QString &command,
|
||||
const FullMsgId &context) {
|
||||
if (!_sublist) {
|
||||
if (!_sublist || _sublist->parentChat()) {
|
||||
sendBotCommandWithOptions(command, context, {});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,6 +265,7 @@ private:
|
|||
void setupRootView();
|
||||
void setupTopicViewer();
|
||||
void subscribeToTopic();
|
||||
void subscribeToSublist();
|
||||
void subscribeToPinnedMessages();
|
||||
void setTopic(Data::ForumTopic *topic);
|
||||
|
||||
|
|
|
@ -588,8 +588,8 @@ rpl::producer<int> SavedSublistCountValue(
|
|||
not_null<PeerData*> peer) {
|
||||
const auto saved = &peer->owner().savedMessages();
|
||||
const auto sublist = saved->sublist(peer);
|
||||
if (!sublist->fullCount()) {
|
||||
saved->loadMore(sublist);
|
||||
if (!sublist->fullCount().has_value()) {
|
||||
sublist->loadFullCount();
|
||||
return rpl::single(0) | rpl::then(sublist->fullCountValue());
|
||||
}
|
||||
return sublist->fullCountValue();
|
||||
|
|
|
@ -433,7 +433,7 @@ updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Updat
|
|||
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
|
||||
updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
|
||||
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
|
||||
updateReadMonoForumInbox#bcf34712 flags:# channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
@ -2395,6 +2395,7 @@ messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult
|
|||
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
|
||||
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
|
||||
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
||||
messages.getSavedDialogsByID#6f6f9c96 flags:# parent_peer:flags.1?InputPeer ids:Vector<InputPeer> = messages.SavedDialogs;
|
||||
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
|
|
Loading…
Add table
Reference in a new issue