Count unread correctly in folders.

This commit is contained in:
John Preston 2019-04-22 18:22:39 +04:00
parent 58519300ea
commit 8fd811517b
23 changed files with 673 additions and 798 deletions

View File

@ -433,8 +433,8 @@ void ApiWrap::applyUpdates(
App::main()->feedUpdates(updates, sentMessageRandomId);
}
void ApiWrap::savePinnedOrder(FolderId folderId) {
const auto &order = _session->data().pinnedChatsOrder(folderId);
void ApiWrap::savePinnedOrder(Data::Folder *folder) {
const auto &order = _session->data().pinnedChatsOrder(folder);
const auto input = [](const Dialogs::Key &key) {
if (const auto history = key.history()) {
return MTP_inputDialogPeer(history->peer->input);
@ -451,7 +451,7 @@ void ApiWrap::savePinnedOrder(FolderId folderId) {
input);
request(MTPmessages_ReorderPinnedDialogs(
MTP_flags(MTPmessages_ReorderPinnedDialogs::Flag::f_force),
MTP_int(folderId),
MTP_int(folder ? folder->id() : 0),
MTP_vector(peers)
)).send();
}
@ -707,17 +707,17 @@ void ApiWrap::requestContacts() {
}).send();
}
void ApiWrap::requestDialogs(FolderId folderId) {
if (folderId && !_foldersLoadState.contains(folderId)) {
_foldersLoadState.emplace(folderId, DialogsLoadState());
void ApiWrap::requestDialogs(Data::Folder *folder) {
if (folder && !_foldersLoadState.contains(folder)) {
_foldersLoadState.emplace(folder, DialogsLoadState());
}
requestMoreDialogs(folderId);
requestMoreDialogs(folder);
}
void ApiWrap::requestMoreDialogs(FolderId folderId) {
const auto state = dialogsLoadState(folderId);
void ApiWrap::requestMoreDialogs(Data::Folder *folder) {
const auto state = dialogsLoadState(folder);
if (!state) {
if (!folderId) {
if (!folder) {
_session->data().addAllSavedPeers();
}
return;
@ -734,7 +734,7 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
const auto hash = 0;
state->requestId = request(MTPmessages_GetDialogs(
MTP_flags(flags),
MTP_int(folderId),
MTP_int(folder ? folder->id() : 0),
MTP_int(state->offsetDate),
MTP_int(state->offsetId),
(state->offsetPeer
@ -743,31 +743,31 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
MTP_int(loadCount),
MTP_int(hash)
)).done([=](const MTPmessages_Dialogs &result) {
const auto state = dialogsLoadState(folderId);
const auto state = dialogsLoadState(folder);
result.match([](const MTPDmessages_dialogsNotModified & data) {
LOG(("API Error: not-modified received for requested dialogs."));
}, [&](const auto &data) {
if constexpr (data.Is<MTPDmessages_dialogs>()) {
if (state) {
state->listReceived = true;
dialogsLoadFinish(folderId); // may kill 'state'.
dialogsLoadFinish(folder); // may kill 'state'.
}
} else {
updateDialogsOffset(
folderId,
folder,
data.vdialogs.v,
data.vmessages.v);
}
_session->data().processUsers(data.vusers);
_session->data().processChats(data.vchats);
_session->data().applyDialogs(
folderId,
folder,
data.vmessages.v,
data.vdialogs.v);
});
if (!folderId) {
requestDialogs(folderId);
if (!folder) {
requestDialogs(folder);
requestContacts();
if (!_dialogsLoadState
|| (!_dialogsLoadState->listReceived
@ -775,15 +775,15 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
refreshDialogsLoadBlocked();
}
}
_session->data().chatsListChanged(folderId);
_session->data().chatsListChanged(folder);
}).fail([=](const RPCError &error) {
dialogsLoadState(folderId)->requestId = 0;
dialogsLoadState(folder)->requestId = 0;
}).send();
if (!state->pinnedReceived) {
requestPinnedDialogs(folderId);
requestPinnedDialogs(folder);
}
if (!folderId) {
if (!folder) {
refreshDialogsLoadBlocked();
}
}
@ -801,7 +801,7 @@ void ApiWrap::refreshDialogsLoadBlocked() {
}
void ApiWrap::updateDialogsOffset(
FolderId folderId,
Data::Folder *folder,
const QVector<MTPDialog> &dialogs,
const QVector<MTPMessage> &messages) {
auto lastDate = TimeId(0);
@ -834,7 +834,7 @@ void ApiWrap::updateDialogsOffset(
break;
}
}
if (const auto state = dialogsLoadState(folderId)) {
if (const auto state = dialogsLoadState(folder)) {
if (lastDate) {
state->offsetDate = lastDate;
state->offsetId = lastMsgId;
@ -842,31 +842,31 @@ void ApiWrap::updateDialogsOffset(
state->requestId = 0;
} else {
state->listReceived = true;
dialogsLoadFinish(folderId);
dialogsLoadFinish(folder);
}
}
}
auto ApiWrap::dialogsLoadState(FolderId folderId) -> DialogsLoadState* {
if (!folderId) {
auto ApiWrap::dialogsLoadState(Data::Folder *folder) -> DialogsLoadState* {
if (!folder) {
return _dialogsLoadState.get();
}
const auto i = _foldersLoadState.find(folderId);
const auto i = _foldersLoadState.find(folder);
return (i != end(_foldersLoadState)) ? &i->second : nullptr;
}
void ApiWrap::dialogsLoadFinish(FolderId folderId) {
void ApiWrap::dialogsLoadFinish(Data::Folder *folder) {
const auto notify = [&] {
Core::App().postponeCall(crl::guard(_session, [=] {
_session->data().chatsListDone(folderId);
_session->data().chatsListDone(folder);
}));
};
const auto state = dialogsLoadState(folderId);
const auto state = dialogsLoadState(folder);
if (!state || !state->listReceived || !state->pinnedReceived) {
return;
}
if (folderId) {
_foldersLoadState.remove(folderId);
if (folder) {
_foldersLoadState.remove(folder);
notify();
} else {
_dialogsLoadState = nullptr;
@ -874,32 +874,32 @@ void ApiWrap::dialogsLoadFinish(FolderId folderId) {
}
}
void ApiWrap::requestPinnedDialogs(FolderId folderId) {
const auto state = dialogsLoadState(folderId);
void ApiWrap::requestPinnedDialogs(Data::Folder *folder) {
const auto state = dialogsLoadState(folder);
if (!state || state->pinnedReceived || state->pinnedRequestId) {
return;
}
const auto finalize = [=] {
if (const auto state = dialogsLoadState(folderId)) {
if (const auto state = dialogsLoadState(folder)) {
state->pinnedRequestId = 0;
state->pinnedReceived = true;
dialogsLoadFinish(folderId);
dialogsLoadFinish(folder);
}
};
state->pinnedRequestId = request(MTPmessages_GetPinnedDialogs(
MTP_int(folderId)
MTP_int(folder ? folder->id() : 0)
)).done([=](const MTPmessages_PeerDialogs &result) {
finalize();
result.match([&](const MTPDmessages_peerDialogs &data) {
_session->data().processUsers(data.vusers);
_session->data().processChats(data.vchats);
_session->data().clearPinnedChats(folderId);
_session->data().clearPinnedChats(folder);
_session->data().applyDialogs(
folderId,
folder,
data.vmessages.v,
data.vdialogs.v);
_session->data().chatsListChanged(folderId);
_session->data().chatsListChanged(folder);
});
}).fail([=](const RPCError &error) {
finalize();
@ -1258,10 +1258,15 @@ void ApiWrap::gotChatFull(
channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
if (const auto history = _session->data().historyLoaded(channel)) {
history->clearUpTill(f.vavailable_min_id.v);
history->applyDialogFields(
f.vunread_count.v,
f.vread_inbox_max_id.v,
f.vread_outbox_max_id.v);
if (history->folderKnown()) {
history->applyDialogFields(
history->folder(),
f.vunread_count.v,
f.vread_inbox_max_id.v,
f.vread_outbox_max_id.v);
} else {
requestDialogEntry(history);
}
}
if (f.has_pinned_msg_id()) {
channel->setPinnedMessageId(f.vpinned_msg_id.v);

View File

@ -64,7 +64,7 @@ public:
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
void savePinnedOrder(FolderId folderId);
void savePinnedOrder(Data::Folder *folder);
void toggleHistoryArchived(
not_null<History*> history,
bool archived,
@ -79,8 +79,8 @@ public:
QString exportDirectMessageLink(not_null<HistoryItem*> item);
void requestContacts();
void requestDialogs(FolderId folderId);
void requestPinnedDialogs(FolderId folderId);
void requestDialogs(Data::Folder *folder = nullptr);
void requestPinnedDialogs(Data::Folder *folder = nullptr);
void requestMoreBlockedByDateDialogs();
rpl::producer<bool> dialogsLoadMayBlockByDate() const;
rpl::producer<bool> dialogsLoadBlockedByDate() const;
@ -467,12 +467,12 @@ private:
void setupSupportMode();
void refreshDialogsLoadBlocked();
void updateDialogsOffset(
FolderId folderId,
Data::Folder *folder,
const QVector<MTPDialog> &dialogs,
const QVector<MTPMessage> &messages);
void requestMoreDialogs(FolderId folderId);
DialogsLoadState *dialogsLoadState(FolderId folderId);
void dialogsLoadFinish(FolderId folderId);
void requestMoreDialogs(Data::Folder *folder);
DialogsLoadState *dialogsLoadState(Data::Folder *folder);
void dialogsLoadFinish(Data::Folder *folder);
void checkQuitPreventFinished();
@ -761,7 +761,9 @@ private:
rpl::variable<bool> _dialogsLoadMayBlockByDate = false;
rpl::variable<bool> _dialogsLoadBlockedByDate = false;
base::flat_map<FolderId, DialogsLoadState> _foldersLoadState;
base::flat_map<
not_null<Data::Folder*>,
DialogsLoadState> _foldersLoadState;
rpl::event_stream<SendOptions> _sendActions;

View File

@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "lang/lang_keys.h"
#include "history/history.h"
#include "dialogs/dialogs_indexed_list.h"
#include "dialogs/dialogs_main_list.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
@ -266,13 +266,13 @@ void ChatsListBoxController::rebuildRows() {
++added;
}
}
added += appendList(Auth().data().chatsList(Dialogs::Mode::All));
added += appendList(Auth().data().chatsList()->indexed());
added += appendList(Auth().data().contactsNoChatsList());
if (!wasEmpty && added > 0) {
// Place dialogs list before contactsNoDialogs list.
delegate()->peerListPartitionRows([](const PeerListRow &a) {
const auto history = static_cast<const Row&>(a).history();
return history->inChatList(Dialogs::Mode::All);
return history->inChatList();
});
if (respectSavedMessagesChat()) {
delegate()->peerListPartitionRows([](const PeerListRow &a) {

View File

@ -1080,7 +1080,7 @@ void AddSpecialBoxSearchController::addChatsContacts() {
return result;
};
const auto dialogsIndex = getSmallestIndex(
_peer->owner().chatsList(Dialogs::Mode::All));
_peer->owner().chatsList()->indexed());
const auto contactsIndex = getSmallestIndex(
_peer->owner().contactsNoChatsList());

View File

@ -489,7 +489,7 @@ ShareBox::Inner::Inner(
_rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent);
const auto dialogs = Auth().data().chatsList(Dialogs::Mode::All);
const auto dialogs = Auth().data().chatsList()->indexed();
const auto self = Auth().user();
if (_filterCallback(self)) {
_chatsIndexed->addToEnd(self->owner().history(self));

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "storage/storage_facade.h"
#include "core/application.h"
//#include "storage/storage_feed_messages.h" // #feed
#include "auth_session.h"
#include "apiwrap.h"
@ -25,6 +26,16 @@ namespace {
constexpr auto kLoadedChatsMinCount = 20;
rpl::producer<int> PinnedDialogsInFolderMaxValue() {
return rpl::single(
rpl::empty_value()
) | rpl::then(
Core::App().configUpdates()
) | rpl::map([=] {
return Global::PinnedDialogsInFolderMax();
});
}
} // namespace
// #feed
@ -40,9 +51,7 @@ constexpr auto kLoadedChatsMinCount = 20;
Folder::Folder(not_null<Data::Session*> owner, FolderId id)
: Entry(owner, this)
, _id(id)
, _chatsList(Dialogs::SortMode::Date)
, _importantChatsList(Dialogs::SortMode::Date)
, _pinnedChatsList(Global::PinnedDialogsInFolderMax())
, _chatsList(PinnedDialogsInFolderMaxValue())
, _name(lang(lng_archived_chats)) {
indexNameParts();
}
@ -78,51 +87,19 @@ void Folder::indexNameParts() {
}
void Folder::registerOne(not_null<History*> history) {
//session().storage().invalidate( // #feed
// Storage::FeedMessagesInvalidate(_id));
if (unreadCountKnown()) {
if (history->unreadCountKnown()) {
// If history unreadCount is known that means that we've
// already had the channel information and if it was in the
// feed already (not yet known) it wouldn't get here.
// That means here we get if we add a new channel to feed.
if (const auto count = history->unreadCount()) {
unreadCountChanged(count, history->mute() ? count : 0);
}
} else {
session().api().requestDialogEntry(this);
}
}
if (_chatsList.size() == 1) {
if (_chatsList.indexed()->size() == 1) {
updateChatListSortPosition();
}
owner().notifyFolderUpdated(this, FolderUpdateFlag::List);
}
void Folder::unregisterOne(not_null<History*> history) {
//session().storage().remove( // #feed
// Storage::FeedMessagesRemoveAll(_id, channel->bareId()));
if (unreadCountKnown()) {
if (history->unreadCountKnown()) {
if (const auto delta = -history->unreadCount()) {
unreadCountChanged(delta, history->mute() ? delta : 0);
}
} else {
session().api().requestDialogEntry(this);
}
}
if (_chatsList.empty()) {
updateChatListExistence();
}
owner().notifyFolderUpdated(this, FolderUpdateFlag::List);
}
not_null<Dialogs::IndexedList*> Folder::chatsList(Dialogs::Mode list) {
return (list == Dialogs::Mode::All)
? &_chatsList
: &_importantChatsList;
not_null<Dialogs::MainList*> Folder::chatsList() {
return &_chatsList;
}
void Folder::loadUserpic() {
@ -163,71 +140,16 @@ void Folder::paintUserpic(
}
bool Folder::chatsListLoaded() const {
return _chatsListLoaded;
return _chatsList.loaded();
}
void Folder::setChatsListLoaded(bool loaded) {
if (_chatsListLoaded != loaded) {
_chatsListLoaded = loaded;
owner().notifyFolderUpdated(this, FolderUpdateFlag::List);
if (_chatsList.loaded() == loaded) {
return;
}
const auto notifier = unreadStateChangeNotifier(true);
_chatsList.setLoaded(loaded);
}
// // #feed
//int32 Folder::chatsHash() const {
// const auto ordered = ranges::view::all(
// _histories
// ) | ranges::view::transform([](not_null<History*> history) {
// return history->peer->bareId();
// }) | ranges::to_vector | ranges::action::sort;
// return Api::CountHash(ordered);
//}
//
//void Folder::setChats(std::vector<not_null<PeerData*>> chats) {
// const auto remove = ranges::view::all(
// _histories
// ) | ranges::view::transform([](not_null<History*> history) {
// return history->peer;
// }) | ranges::view::filter([&](not_null<PeerData*> peer) {
// return !base::contains(chats, peer);
// }) | ranges::to_vector;
//
// const auto add = ranges::view::all(
// chats
// ) | ranges::view::filter([&](not_null<PeerData*> peer) {
// return ranges::find(
// _histories,
// peer,
// [](auto history) { return history->peer; }
// ) == end(_histories);
// }) | ranges::view::transform([](PeerData *peer) {
// return not_null<PeerData*>(peer);
// }) | ranges::to_vector;
//
// changeChatsList(add, remove);
//
// setChatsLoaded(true);
//}
//
//void Folder::changeChatsList(
// const std::vector<not_null<PeerData*>> &add,
// const std::vector<not_null<PeerData*>> &remove) {
// _settingChats = true;
// const auto restore = gsl::finally([&] { _settingChats = false; });
//
// for (const auto channel : remove) {
// channel->clearFeed();
// }
//
// //// We assume the last message was correct before requesting the list.
// //// So we save it and don't allow channels from the list to change it.
// //// After that we restore it.
// const auto oldChatListMessage = base::take(_chatListMessage);
// for (const auto channel : add) {
// _chatListMessage = std::nullopt;
// channel->setFeed(this);
// }
// _chatListMessage = oldChatListMessage;
//}
void Folder::requestChatListMessage() {
if (!chatListMessageKnown()) {
@ -235,66 +157,14 @@ void Folder::requestChatListMessage() {
}
}
void Folder::setPinnedChatsLimit(int limit) {
_pinnedChatsList.setLimit(limit);
}
void Folder::setChatPinned(const Dialogs::Key &key, bool pinned) {
_pinnedChatsList.setPinned(key, pinned);
}
void Folder::addPinnedChat(const Dialogs::Key &key) {
_pinnedChatsList.addPinned(key);
}
void Folder::applyPinnedChats(const QVector<MTPDialogPeer> &list) {
_pinnedChatsList.applyList(&owner(), list);
}
const std::vector<Dialogs::Key> &Folder::pinnedChatsOrder() const {
return _pinnedChatsList.order();
}
void Folder::clearPinnedChats() {
_pinnedChatsList.clear();
}
void Folder::reorderTwoPinnedChats(
const Dialogs::Key &key1,
const Dialogs::Key &key2) {
_pinnedChatsList.reorder(key1, key2);
}
TimeId Folder::adjustedChatListTimeId() const {
return _chatsList.empty()
const auto list = _chatsList.indexed();
return list->empty()
? TimeId(0)
: (*_chatsList.begin())->entry()->adjustedChatListTimeId();
}
int Folder::unreadCount() const {
return _unreadCount.value_or(0);
}
rpl::producer<int> Folder::unreadCountValue() const {
return rpl::single(
unreadCount()
) | rpl::then(_unreadCountChanges.events());
}
bool Folder::unreadCountKnown() const {
return !!_unreadCount;
: (*list->begin())->entry()->adjustedChatListTimeId();
}
void Folder::applyDialog(const MTPDdialogFolder &data) {
//const auto addChannel = [&](ChannelId channelId) { // #feed
// if (const auto channel = owner().channelLoaded(channelId)) {
// channel->setFeed(this);
// }
//};
//for (const auto &channelId : data.vfeed_other_channels.v) {
// addChannel(channelId.v);
//}
if (const auto peerId = peerFromMTP(data.vpeer)) {
const auto history = owner().history(peerId);
const auto fullId = FullMsgId(
@ -303,24 +173,36 @@ void Folder::applyDialog(const MTPDdialogFolder &data) {
history->setFolder(this, App::histItemById(fullId));
} else {
_chatsList.clear();
_importantChatsList.clear();
updateChatListExistence();
}
setUnreadCounts(
data.vunread_unmuted_messages_count.v,
data.vunread_muted_messages_count.v);
//setUnreadMark(data.is_unread_mark());
//setUnreadMentionsCount(data.vunread_mentions_count.v);
//if (data.has_read_max_position()) { // #feed
// setUnreadPosition(FeedPositionFromMTP(data.vread_max_position));
//}
if (_chatsList.size() < kLoadedChatsMinCount) {
session().api().requestDialogs(_id);
updateCloudUnread(data);
if (_chatsList.indexed()->size() < kLoadedChatsMinCount) {
session().api().requestDialogs(this);
}
}
void Folder::updateCloudUnread(const MTPDdialogFolder &data) {
const auto notifier = unreadStateChangeNotifier(!_chatsList.loaded());
_cloudUnread.messagesCountMuted = data.vunread_muted_messages_count.v;
_cloudUnread.messagesCount = _cloudUnread.messagesCountMuted
+ data.vunread_unmuted_messages_count.v;
_cloudUnread.chatsCountMuted = data.vunread_muted_peers_count.v;
_cloudUnread.chatsCount = _cloudUnread.chatsCountMuted
+ data.vunread_unmuted_peers_count.v;
}
Dialogs::UnreadState Folder::chatListUnreadState() const {
const auto state = _chatsList.loaded()
? _chatsList.unreadState()
: _cloudUnread;
auto result = Dialogs::UnreadState();
result.messagesCount = state.messagesCount;
result.messagesCountMuted = result.messagesCount.value_or(0);
result.chatsCount = result.chatsCountMuted = state.chatsCount;
return result;
}
void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
const auto folderId = data.has_folder_id() ? data.vfolder_id.v : 0;
if (folderId != 0) {
@ -329,129 +211,50 @@ void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
owner().setChatPinned(this, data.is_pinned());
}
void Folder::changedInChatListHook(Dialogs::Mode list, bool added) {
if (list != Dialogs::Mode::All) {
void Folder::unreadStateChanged(
const Dialogs::UnreadState &wasState,
const Dialogs::UnreadState &nowState) {
const auto updateCloudUnread = _cloudUnread.messagesCount.has_value()
&& wasState.messagesCount.has_value();
const auto notify = _chatsList.loaded() || updateCloudUnread;
const auto notifier = unreadStateChangeNotifier(notify);
_chatsList.unreadStateChanged(wasState, nowState);
if (!_cloudUnread.messagesCount.has_value()
|| !wasState.messagesCount.has_value()) {
return;
}
if (const auto count = unreadCount()) {
const auto mutedCount = _unreadMutedCount;
const auto nonMutedCount = count - mutedCount;
const auto mutedDelta = added ? mutedCount : -mutedCount;
const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount;
owner().unreadIncrement(nonMutedDelta, false);
owner().unreadIncrement(mutedDelta, true);
Assert(nowState.messagesCount.has_value());
const auto fullMuted = (nonMutedCount == 0);
const auto entriesWithUnreadDelta = added ? 1 : -1;
const auto mutedEntriesWithUnreadDelta = fullMuted
? entriesWithUnreadDelta
: 0;
owner().unreadEntriesChanged(
entriesWithUnreadDelta,
mutedEntriesWithUnreadDelta);
}
*_cloudUnread.messagesCount += *nowState.messagesCount
- *wasState.messagesCount;
_cloudUnread.messagesCountMuted += nowState.messagesCountMuted
- wasState.messagesCountMuted;
_cloudUnread.chatsCount += nowState.chatsCount - wasState.chatsCount;
_cloudUnread.chatsCountMuted += nowState.chatsCountMuted
- wasState.chatsCountMuted;
}
template <typename PerformUpdate>
void Folder::updateUnreadCounts(PerformUpdate &&performUpdate) {
const auto wasUnreadCount = _unreadCount ? *_unreadCount : 0;
const auto wasUnreadMutedCount = _unreadMutedCount;
const auto wasFullMuted = (wasUnreadMutedCount > 0)
&& (wasUnreadCount == wasUnreadMutedCount);
void Folder::unreadEntryChanged(
const Dialogs::UnreadState &state,
bool added) {
const auto updateCloudUnread = _cloudUnread.messagesCount.has_value()
&& state.messagesCount.has_value();
const auto notify = _chatsList.loaded() || updateCloudUnread;
const auto notifier = unreadStateChangeNotifier(notify);
performUpdate();
Assert(_unreadCount.has_value());
_unreadCountChanges.fire(unreadCount());
updateChatListEntry();
if (inChatList(Dialogs::Mode::All)) {
const auto nowUnreadCount = *_unreadCount;
const auto nowUnreadMutedCount = _unreadMutedCount;
const auto nowFullMuted = (nowUnreadMutedCount > 0)
&& (nowUnreadCount == nowUnreadMutedCount);
owner().unreadIncrement(
(nowUnreadCount - nowUnreadMutedCount)
- (wasUnreadCount - wasUnreadMutedCount),
false);
owner().unreadIncrement(
nowUnreadMutedCount - wasUnreadMutedCount,
true);
const auto entriesDelta = (nowUnreadCount && !wasUnreadCount)
? 1
: (wasUnreadCount && !nowUnreadCount)
? -1
: 0;
const auto mutedEntriesDelta = (!wasFullMuted && nowFullMuted)
? 1
: (wasFullMuted && !nowFullMuted)
? -1
: 0;
owner().unreadEntriesChanged(
entriesDelta,
mutedEntriesDelta);
}
}
void Folder::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
if (unreadCountKnown()
&& (*_unreadCount == unreadNonMutedCount + unreadMutedCount)
&& (_unreadMutedCount == unreadMutedCount)) {
_chatsList.unreadEntryChanged(state, added);
if (!_cloudUnread.messagesCount.has_value()
|| !state.messagesCount.has_value()) {
return;
}
updateUnreadCounts([&] {
_unreadCount = unreadNonMutedCount + unreadMutedCount;
_unreadMutedCount = unreadMutedCount;
});
const auto delta = (added ? 1 : -1);
*_cloudUnread.messagesCount += delta * *state.messagesCount;
_cloudUnread.messagesCountMuted += delta * state.messagesCountMuted;
_cloudUnread.chatsCount += delta * state.chatsCount;
_cloudUnread.chatsCountMuted += delta * state.chatsCountMuted;
}
// #feed
//void Folder::setUnreadPosition(const MessagePosition &position) {
// if (_unreadPosition.current() < position) {
// _unreadPosition = position;
// }
//}
void Folder::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) {
if (!unreadCountKnown()) {
return;
}
updateUnreadCounts([&] {
accumulate_max(unreadCountDelta, -*_unreadCount);
*_unreadCount += unreadCountDelta;
mutedCountDelta = snap(
mutedCountDelta,
-_unreadMutedCount,
*_unreadCount - _unreadMutedCount);
_unreadMutedCount += mutedCountDelta;
});
}
//
//void Folder::setUnreadMark(bool unread) {
// if (_unreadMark != unread) {
// _unreadMark = unread;
// if (!_unreadCount || !*_unreadCount) {
// if (inChatList(Dialogs::Mode::All)) {
// const auto delta = _unreadMark ? 1 : -1;
// owner().unreadIncrement(delta, mute());
// owner().unreadEntriesChanged(
// delta,
// mute() ? delta : 0);
//
// updateChatListEntry();
// }
// }
// Notify::peerUpdatedDelayed(
// peer,
// Notify::PeerUpdate::Flag::UnreadViewChanged);
// }
//}
//
//bool Folder::unreadMark() const {
// return _unreadMark;
//}
// #feed
//MessagePosition Folder::unreadPosition() const {
// return _unreadPosition.current();
@ -462,7 +265,7 @@ void Folder::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) {
//}
bool Folder::toImportant() const {
return !_importantChatsList.empty();
return false;
}
int Folder::fixedOnTopIndex() const {
@ -474,7 +277,9 @@ bool Folder::shouldBeInChatList() const {
}
int Folder::chatListUnreadCount() const {
return unreadCount();
return session().settings().countUnreadMessages()
? chatListUnreadState().messagesCount.value_or(0)
: chatListUnreadState().chatsCount;
}
bool Folder::chatListUnreadMark() const {
@ -482,18 +287,20 @@ bool Folder::chatListUnreadMark() const {
}
bool Folder::chatListMutedBadge() const {
return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false;
return true;
}
HistoryItem *Folder::chatListMessage() const {
return _chatsList.empty()
const auto list = _chatsList.indexed();
return list->empty()
? nullptr
: (*_chatsList.begin())->key().entry()->chatListMessage();
: (*list->begin())->key().entry()->chatListMessage();
}
bool Folder::chatListMessageKnown() const {
return _chatsList.empty()
|| (*_chatsList.begin())->key().entry()->chatListMessageKnown();
const auto list = _chatsList.indexed();
return list->empty()
|| (*list->begin())->key().entry()->chatListMessageKnown();
}
const QString &Folder::chatListName() const {

View File

@ -8,8 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "dialogs/dialogs_entry.h"
#include "dialogs/dialogs_indexed_list.h"
#include "dialogs/dialogs_pinned_list.h"
#include "dialogs/dialogs_main_list.h"
#include "data/data_messages.h"
class ChannelData;
@ -20,15 +19,6 @@ namespace Data {
class Session;
class Folder;
enum class FolderUpdateFlag {
List,
};
struct FolderUpdate {
not_null<Data::Folder*> folder;
FolderUpdateFlag flag;
};
//MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position); // #feed
class Folder final : public Dialogs::Entry {
@ -43,43 +33,21 @@ public:
void registerOne(not_null<History*> history);
void unregisterOne(not_null<History*> history);
not_null<Dialogs::IndexedList*> chatsList(Dialogs::Mode list);
not_null<Dialogs::MainList*> chatsList();
void applyDialog(const MTPDdialogFolder &data);
void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount);
//void setUnreadPosition(const MessagePosition &position); // #feed
void unreadCountChanged(
int unreadCountDelta,
int mutedCountDelta);
rpl::producer<int> unreadCountValue() const;
//MessagePosition unreadPosition() const; // #feed
//rpl::producer<MessagePosition> unreadPositionChanges() const; // #feed
//void setUnreadMark(bool unread);
//bool unreadMark() const;
//int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
void setPinnedChatsLimit(int limit);
// Places on the last place in the list otherwise.
// Does nothing if already pinned.
void addPinnedChat(const Dialogs::Key &key);
// if (pinned) places on the first place in the list.
void setChatPinned(const Dialogs::Key &key, bool pinned);
void applyPinnedChats(const QVector<MTPDialogPeer> &list);
const std::vector<Dialogs::Key> &pinnedChatsOrder() const;
void clearPinnedChats();
void reorderTwoPinnedChats(
const Dialogs::Key &key1,
const Dialogs::Key &key2);
void updateCloudUnread(const MTPDdialogFolder &data);
void unreadStateChanged(
const Dialogs::UnreadState &wasState,
const Dialogs::UnreadState &nowState);
void unreadEntryChanged(const Dialogs::UnreadState &state, bool added);
TimeId adjustedChatListTimeId() const override;
int unreadCount() const;
bool unreadCountKnown() const;
int fixedOnTopIndex() const override;
bool toImportant() const override;
@ -87,13 +55,13 @@ public:
int chatListUnreadCount() const override;
bool chatListUnreadMark() const override;
bool chatListMutedBadge() const override;
Dialogs::UnreadState chatListUnreadState() const override;
HistoryItem *chatListMessage() const override;
bool chatListMessageKnown() const override;
void requestChatListMessage() override;
const QString &chatListName() const override;
const base::flat_set<QString> &chatListNameWords() const override;
const base::flat_set<QChar> &chatListFirstLetters() const override;
void changedInChatListHook(Dialogs::Mode list, bool added) override;
void loadUserpic() override;
void paintUserpic(
@ -103,34 +71,20 @@ public:
int size) const override;
bool chatsListLoaded() const;
void setChatsListLoaded(bool loaded);
//int32 chatsHash() const;
//void setChats(std::vector<not_null<PeerData*>> chats); // #feed
void setChatsListLoaded(bool loaded = true);
private:
void indexNameParts();
//void changeChatsList(
// const std::vector<not_null<PeerData*>> &add,
// const std::vector<not_null<PeerData*>> &remove);
template <typename PerformUpdate>
void updateUnreadCounts(PerformUpdate &&performUpdate);
FolderId _id = 0;
Dialogs::IndexedList _chatsList;
Dialogs::IndexedList _importantChatsList;
Dialogs::PinnedList _pinnedChatsList;
bool _chatsListLoaded = false;
Dialogs::MainList _chatsList;
QString _name;
base::flat_set<QString> _nameWords;
base::flat_set<QChar> _nameFirstLetters;
Dialogs::UnreadState _cloudUnread;
//rpl::variable<MessagePosition> _unreadPosition;
std::optional<int> _unreadCount;
rpl::event_stream<int> _unreadCountChanges;
int _unreadMutedCount = 0;
//bool _unreadMark = false;
};

View File

@ -140,6 +140,16 @@ MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) {
: MTPPhotoSize(MTP_photoSizeEmpty(MTP_string("")));
}
rpl::producer<int> PinnedDialogsCountMaxValue() {
return rpl::single(
rpl::empty_value()
) | rpl::then(
Core::App().configUpdates()
) | rpl::map([=] {
return Global::PinnedDialogsCountMax();
});
}
} // namespace
Session::Session(not_null<AuthSession*> session)
@ -150,11 +160,9 @@ Session::Session(not_null<AuthSession*> session)
, _bigFileCache(Core::App().databases().get(
Local::cacheBigFilePath(),
Local::cacheBigFileSettings()))
, _chatsList(Dialogs::SortMode::Date)
, _importantChatsList(Dialogs::SortMode::Date)
, _chatsList(PinnedDialogsCountMaxValue())
, _contactsList(Dialogs::SortMode::Name)
, _contactsNoChatsList(Dialogs::SortMode::Name)
, _pinnedChatsList(Global::PinnedDialogsCountMax())
, _selfDestructTimer([=] { checkSelfDestructItems(); })
, _sendActionsAnimation([=](crl::time now) {
return sendActionsAnimationCallback(now);
@ -168,14 +176,6 @@ Session::Session(not_null<AuthSession*> session)
setupChannelLeavingViewer();
setupPeerNameViewer();
setupUserIsContactViewer();
Core::App().configUpdates(
) | rpl::start_with_next([=] {
_pinnedChatsList.setLimit(Global::PinnedDialogsCountMax());
for (const auto &[folderId, folder] : _folders) {
folder->setPinnedChatsLimit(Global::PinnedDialogsInFolderMax());
}
}, _lifetime);
}
void Session::clear() {
@ -786,7 +786,7 @@ bool Session::sendActionsAnimationCallback(crl::time now) {
}
bool Session::chatsListLoaded(Data::Folder *folder) {
return folder ? folder->chatsListLoaded() : _chatsListLoaded;
return chatsList(folder)->loaded();
}
void Session::chatsListChanged(FolderId folderId) {
@ -797,12 +797,11 @@ void Session::chatsListChanged(Data::Folder *folder) {
_chatsListChanged.fire_copy(folder);
}
void Session::chatsListDone(FolderId folderId) {
const auto folder = folderId ? this->folder(folderId).get() : nullptr;
void Session::chatsListDone(Data::Folder *folder) {
if (folder) {
folder->setChatsListLoaded(true);
folder->setChatsListLoaded();
} else {
_chatsListLoaded = true;
_chatsList.setLoaded();
}
_chatsListLoadedEvents.fire_copy(folder);
}
@ -964,13 +963,6 @@ void Session::setupPeerNameViewer() {
) | rpl::start_with_next([=](const Notify::PeerUpdate &update) {
const auto peer = update.peer;
const auto &oldLetters = update.oldNameFirstLetters;
_chatsList.peerNameChanged(Dialogs::Mode::All, peer, oldLetters);
if (Global::DialogsModeEnabled()) {
_importantChatsList.peerNameChanged(
Dialogs::Mode::Important,
peer,
oldLetters);
}
_contactsNoChatsList.peerNameChanged(peer, oldLetters);
_contactsList.peerNameChanged(peer, oldLetters);
}, _lifetime);
@ -991,7 +983,7 @@ void Session::setupUserIsContactViewer() {
if (user->contactStatus() == UserData::ContactStatus::Contact) {
const auto history = user->owner().history(user->id);
_contactsList.addByName(history);
if (!_chatsList.contains(history)) {
if (!_chatsList.indexed()->contains(history)) {
_contactsNoChatsList.addByName(history);
}
} else if (const auto history = user->owner().historyLoaded(user)) {
@ -1288,16 +1280,6 @@ rpl::producer<not_null<UserData*>> Session::megagroupParticipantAdded(
});
}
void Session::notifyFolderUpdated(
not_null<Folder*> folder,
FolderUpdateFlag update) {
_folderUpdates.fire({ folder, update });
}
rpl::producer<FolderUpdate> Session::folderUpdated() const {
return _folderUpdates.events();
}
void Session::notifyStickersUpdated() {
_stickersUpdated.fire({});
}
@ -1355,33 +1337,24 @@ MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
void Session::setChatPinned(const Dialogs::Key &key, bool pinned) {
Expects(key.entry()->folderKnown());
if (const auto folder = key.entry()->folder()) {
folder->setChatPinned(key, pinned);
} else {
_pinnedChatsList.setPinned(key, pinned);
}
const auto list = chatsList(key.entry()->folder())->pinned();
list->setPinned(key, pinned);
}
void Session::setPinnedFromDialog(const Dialogs::Key &key, bool pinned) {
Expects(key.entry()->folderKnown());
if (const auto folder = key.entry()->folder()) {
if (pinned) {
folder->addPinnedChat(key);
} else {
folder->setChatPinned(key, false);
}
} else if (pinned) {
_pinnedChatsList.addPinned(key);
const auto list = chatsList(key.entry()->folder())->pinned();
if (pinned) {
list->addPinned(key);
} else {
_pinnedChatsList.setPinned(key, false);
list->setPinned(key, false);
}
}
void Session::applyPinnedChats(
FolderId folderId,
Data::Folder *folder,
const QVector<MTPDialogPeer> &list) {
const auto folder = folderId ? this->folder(folderId).get() : nullptr;
for (const auto &peer : list) {
peer.match([&](const MTPDdialogPeer &data) {
const auto history = this->history(peerFromMTP(data.vpeer));
@ -1396,33 +1369,31 @@ void Session::applyPinnedChats(
}
});
}
if (folder) {
folder->applyPinnedChats(list);
} else {
_pinnedChatsList.applyList(this, list);
}
chatsList(folder)->pinned()->applyList(this, list);
}
void Session::applyDialogs(
FolderId requestFolderId,
Data::Folder *requestFolder,
const QVector<MTPMessage> &messages,
const QVector<MTPDialog> &dialogs) {
App::feedMsgs(messages, NewMessageLast);
for (const auto &dialog : dialogs) {
dialog.match([&](const auto &data) {
applyDialog(requestFolderId, data);
applyDialog(requestFolder, data);
});
}
}
void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) {
void Session::applyDialog(
Data::Folder *requestFolder,
const MTPDdialog &data) {
const auto peerId = peerFromMTP(data.vpeer);
if (!peerId) {
return;
}
const auto history = session().data().history(peerId);
history->applyDialog(requestFolderId, data);
history->applyDialog(requestFolder, data);
setPinnedFromDialog(history, data.is_pinned());
if (!history->fixedOnTopIndex() && !history->isPinnedDialog()) {
@ -1443,10 +1414,10 @@ void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) {
}
void Session::applyDialog(
FolderId requestFolderId,
Data::Folder *requestFolder,
const MTPDdialogFolder &data) {
if (requestFolderId != 0) {
LOG(("API Error: requestFolderId != 0 for dialogFolder."));
if (requestFolder) {
LOG(("API Error: requestFolder != nullptr for dialogFolder."));
}
const auto folder = processFolder(data.vfolder);
folder->applyDialog(data);
@ -1478,36 +1449,23 @@ void Session::addAllSavedPeers() {
addSavedPeersAfter(QDateTime());
}
int Session::pinnedChatsCount(FolderId folderId) const {
return pinnedChatsOrder(folderId).size();
int Session::pinnedChatsCount(Data::Folder *folder) const {
return pinnedChatsOrder(folder).size();
}
int Session::pinnedChatsLimit(FolderId folderId) const {
return folderId
int Session::pinnedChatsLimit(Data::Folder *folder) const {
return folder
? Global::PinnedDialogsInFolderMax()
: Global::PinnedDialogsCountMax();
}
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
FolderId folderId) const {
if (folderId) {
if (const auto folder = folderLoaded(folderId)) {
return folder->pinnedChatsOrder();
}
static const auto result = std::vector<Dialogs::Key>();
return result;
}
return _pinnedChatsList.order();
Data::Folder *folder) const {
return chatsList(folder)->pinned()->order();
}
void Session::clearPinnedChats(FolderId folderId) {
if (folderId) {
if (const auto folder = folderLoaded(folderId)) {
folder->clearPinnedChats();
}
} else {
_pinnedChatsList.clear();
}
void Session::clearPinnedChats(Data::Folder *folder) {
chatsList(folder)->pinned()->clear();
}
void Session::reorderTwoPinnedChats(
@ -1516,12 +1474,7 @@ void Session::reorderTwoPinnedChats(
Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown());
Expects(key1.entry()->folder() == key2.entry()->folder());
const auto folder = key1.entry()->folder();
if (folder) {
folder->reorderTwoPinnedChats(key1, key2);
} else {
_pinnedChatsList.reorder(key1, key2);
}
chatsList(key1.entry()->folder())->pinned()->reorder(key1, key2);
}
NotifySettings &Session::defaultNotifySettings(
@ -1621,57 +1574,61 @@ void Session::updateSendActionAnimation(
}
int Session::unreadBadge() const {
const auto state = _chatsList.unreadState();
return computeUnreadBadge(
_unreadFull,
_unreadMuted,
_unreadEntriesFull,
_unreadEntriesMuted);
state.messagesCount.value_or(0),
state.messagesCountMuted,
state.chatsCount,
state.chatsCountMuted);
}
bool Session::unreadBadgeMuted() const {
const auto state = _chatsList.unreadState();
return computeUnreadBadgeMuted(
_unreadFull,
_unreadMuted,
_unreadEntriesFull,
_unreadEntriesMuted);
state.messagesCount.value_or(0),
state.messagesCountMuted,
state.chatsCount,
state.chatsCountMuted);
}
int Session::unreadBadgeIgnoreOne(History *history) const {
const auto removeCount = (history
&& history->inChatList(Dialogs::Mode::All))
const auto removeCount = (history && history->inChatList())
? history->unreadCount()
: 0;
if (!removeCount) {
return unreadBadge();
}
const auto removeMuted = history->mute();
const auto state = _chatsList.unreadState();
const auto removeMuted = history->mute()
|| (history->folder() != nullptr);
return computeUnreadBadge(
_unreadFull - removeCount,
_unreadMuted - (removeMuted ? removeCount : 0),
_unreadEntriesFull - 1,
_unreadEntriesMuted - (removeMuted ? 1 : 0));
state.messagesCount.value_or(0) - removeCount,
state.messagesCountMuted - (removeMuted ? removeCount : 0),
state.chatsCount - 1,
state.chatsCountMuted - (removeMuted ? 1 : 0));
}
bool Session::unreadBadgeMutedIgnoreOne(History *history) const {
const auto removeCount = (history
&& history->inChatList(Dialogs::Mode::All))
const auto removeCount = (history && history->inChatList())
? history->unreadCount()
: 0;
if (!removeCount) {
return unreadBadgeMuted();
}
const auto state = _chatsList.unreadState();
const auto removeMuted = history->mute();
return computeUnreadBadgeMuted(
_unreadFull - removeCount,
_unreadMuted - (removeMuted ? removeCount : 0),
_unreadEntriesFull - 1,
_unreadEntriesMuted - (removeMuted ? 1 : 0));
state.messagesCount.value_or(0) - removeCount,
state.messagesCountMuted - (removeMuted ? removeCount : 0),
state.chatsCount - 1,
state.chatsCountMuted - (removeMuted ? 1 : 0));
}
int Session::unreadOnlyMutedBadge() const {
const auto state = _chatsList.unreadState();
return _session->settings().countUnreadMessages()
? _unreadMuted
: _unreadEntriesMuted;
? state.messagesCountMuted
: state.chatsCountMuted;
}
int Session::computeUnreadBadge(
@ -1698,57 +1655,29 @@ bool Session::computeUnreadBadgeMuted(
: (entriesMuted >= entriesFull);
}
void Session::unreadIncrement(int count, bool muted) {
if (!count) {
return;
}
_unreadFull += count;
if (muted) {
_unreadMuted += count;
}
if (_session->settings().countUnreadMessages()) {
if (!muted || _session->settings().includeMutedCounter()) {
Notify::unreadCounterUpdated();
}
}
}
void Session::unreadStateChanged(
const Dialogs::Key &key,
const Dialogs::UnreadState &wasState) {
Expects(key.entry()->folderKnown());
Expects(key.entry()->inChatList());
void Session::unreadMuteChanged(int count, bool muted) {
const auto wasAll = (_unreadMuted == _unreadFull);
if (muted) {
_unreadMuted += count;
const auto nowState = key.entry()->chatListUnreadState();
if (const auto folder = key.entry()->folder()) {
folder->unreadStateChanged(wasState, nowState);
} else {
_unreadMuted -= count;
}
if (_session->settings().countUnreadMessages()) {
const auto nowAll = (_unreadMuted == _unreadFull);
const auto changed = !_session->settings().includeMutedCounter()
|| (wasAll != nowAll);
if (changed) {
Notify::unreadCounterUpdated();
}
_chatsList.unreadStateChanged(wasState, nowState);
}
Notify::unreadCounterUpdated();
}
void Session::unreadEntriesChanged(
int withUnreadDelta,
int mutedWithUnreadDelta) {
if (!withUnreadDelta && !mutedWithUnreadDelta) {
return;
}
const auto wasAll = (_unreadEntriesMuted == _unreadEntriesFull);
_unreadEntriesFull += withUnreadDelta;
_unreadEntriesMuted += mutedWithUnreadDelta;
if (!_session->settings().countUnreadMessages()) {
const auto nowAll = (_unreadEntriesMuted == _unreadEntriesFull);
const auto withMuted = _session->settings().includeMutedCounter();
const auto withMutedChanged = withMuted
&& (withUnreadDelta != 0 || wasAll != nowAll);
const auto withoutMutedChanged = !withMuted
&& (withUnreadDelta != mutedWithUnreadDelta);
if (withMutedChanged || withoutMutedChanged) {
Notify::unreadCounterUpdated();
}
void Session::unreadEntryChanged(const Dialogs::Key &key, bool added) {
Expects(key.entry()->folderKnown());
const auto state = key.entry()->chatListUnreadState();
if (const auto folder = key.entry()->folder()) {
folder->unreadEntryChanged(state, added);
} else {
_chatsList.unreadEntryChanged(state, added);
}
}
@ -2986,10 +2915,13 @@ not_null<Folder*> Session::processFolder(const MTPDfolder &data) {
// return _defaultFeedId.value();
//}
not_null<Dialogs::IndexedList*> Session::chatsList(Dialogs::Mode list) {
return (list == Dialogs::Mode::All)
? &_chatsList
: &_importantChatsList;
not_null<Dialogs::MainList*> Session::chatsList(Data::Folder *folder) {
return folder ? folder->chatsList().get() : &_chatsList;
}
not_null<const Dialogs::MainList*> Session::chatsList(
Data::Folder *folder) const {
return folder ? folder->chatsList() : &_chatsList;
}
not_null<Dialogs::IndexedList*> Session::contactsList() {
@ -3006,7 +2938,7 @@ auto Session::refreshChatListEntry(Dialogs::Key key)
const auto entry = key.entry();
auto result = RefreshChatListEntryResult();
result.changed = !entry->inChatList(Mode::All);
result.changed = !entry->inChatList();
if (result.changed) {
const auto mainRow = entry->addToChatList(Mode::All);
_contactsNoChatsList.del(key, mainRow);
@ -3241,7 +3173,7 @@ void Session::serviceNotification(
MTPstring()));
}
const auto history = this->history(PeerData::kServiceNotificationsId);
if (!history->lastMessageKnown()) {
if (!history->folderKnown()) {
_session->api().requestDialogEntry(history, [=] {
insertCheckedServiceNotification(message, media, date);
});
@ -3263,10 +3195,6 @@ void Session::insertCheckedServiceNotification(
const MTPMessageMedia &media,
TimeId date) {
const auto history = this->history(PeerData::kServiceNotificationsId);
if (!history->isReadyFor(ShowAtUnreadMsgId)) {
history->setUnreadCount(0);
history->getReadyFor(ShowAtTheEndMsgId);
}
const auto flags = MTPDmessage::Flag::f_entities
| MTPDmessage::Flag::f_from_id
| MTPDmessage_ClientFlag::f_clientside_unread

View File

@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/stickers.h"
#include "dialogs/dialogs_key.h"
#include "dialogs/dialogs_indexed_list.h"
#include "dialogs/dialogs_pinned_list.h"
#include "dialogs/dialogs_main_list.h"
#include "data/data_groups.h"
#include "data/data_notify_settings.h"
#include "history/history_location_manager.h"
@ -51,8 +51,6 @@ struct SavedCredentials;
namespace Data {
class Folder;
enum class FolderUpdateFlag;
struct FolderUpdate;
class WallPaper;
@ -148,7 +146,7 @@ public:
}
void chatsListChanged(FolderId folderId);
void chatsListChanged(Data::Folder *folder);
void chatsListDone(FolderId folderId);
void chatsListDone(Data::Folder *folder);
struct ItemVisibilityQuery {
not_null<HistoryItem*> item;
@ -209,9 +207,6 @@ public:
[[nodiscard]] rpl::producer<not_null<UserData*>> megagroupParticipantAdded(
not_null<ChannelData*> channel) const;
void notifyFolderUpdated(not_null<Folder*> folder, FolderUpdateFlag update);
[[nodiscard]] rpl::producer<FolderUpdate> folderUpdated() const;
void notifyStickersUpdated();
[[nodiscard]] rpl::producer<> stickersUpdated() const;
void notifySavedGifsUpdated();
@ -299,20 +294,20 @@ public:
void applyUpdate(const MTPDupdateChatDefaultBannedRights &update);
void applyDialogs(
FolderId requestFolderId,
Data::Folder *requestFolder,
const QVector<MTPMessage> &messages,
const QVector<MTPDialog> &dialogs);
void addSavedPeersAfter(const QDateTime &date);
void addAllSavedPeers();
int pinnedChatsCount(FolderId folderId) const;
int pinnedChatsLimit(FolderId folderId) const;
int pinnedChatsCount(Data::Folder *folder) const;
int pinnedChatsLimit(Data::Folder *folder) const;
const std::vector<Dialogs::Key> &pinnedChatsOrder(
FolderId folderId) const;
Data::Folder *folder) const;
void setChatPinned(const Dialogs::Key &key, bool pinned);
void clearPinnedChats(FolderId folderId);
void clearPinnedChats(Data::Folder *folder);
void applyPinnedChats(
FolderId folderId,
Data::Folder *folder,
const QVector<MTPDialogPeer> &list);
void reorderTwoPinnedChats(
const Dialogs::Key &key1,
@ -346,11 +341,10 @@ public:
bool unreadBadgeMutedIgnoreOne(History *history) const;
int unreadOnlyMutedBadge() const;
void unreadIncrement(int count, bool muted);
void unreadMuteChanged(int count, bool muted);
void unreadEntriesChanged(
int withUnreadDelta,
int mutedWithUnreadDelta);
void unreadStateChanged(
const Dialogs::Key &key,
const Dialogs::UnreadState &wasState);
void unreadEntryChanged(const Dialogs::Key &key, bool added);
void selfDestructIn(not_null<HistoryItem*> item, crl::time delay);
@ -525,7 +519,9 @@ public:
//FeedId defaultFeedId() const;
//rpl::producer<FeedId> defaultFeedIdValue() const;
not_null<Dialogs::IndexedList*> chatsList(Dialogs::Mode list);
not_null<Dialogs::MainList*> chatsList(Data::Folder *folder = nullptr);
not_null<const Dialogs::MainList*> chatsList(
Data::Folder *folder = nullptr) const;
not_null<Dialogs::IndexedList*> contactsList();
not_null<Dialogs::IndexedList*> contactsNoChatsList();
@ -601,6 +597,7 @@ private:
void setupUserIsContactViewer();
void checkSelfDestructItems();
int computeUnreadBadge(
int full,
int muted,
@ -612,8 +609,10 @@ private:
int entriesFull,
int entriesMuted) const;
void applyDialog(FolderId requestFolderId, const MTPDdialog &data);
void applyDialog(FolderId requestFolderId, const MTPDdialogFolder &data);
void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data);
void applyDialog(
Data::Folder *requestFolder,
const MTPDdialogFolder &data);
void photoApplyFields(
not_null<PhotoData*> photo,
@ -734,7 +733,6 @@ private:
QPointer<BoxContent> _exportSuggestion;
rpl::variable<bool> _contactsLoaded = false;
bool _chatsListLoaded = false;
rpl::event_stream<Data::Folder*> _chatsListLoadedEvents;
rpl::event_stream<Data::Folder*> _chatsListChanged;
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
@ -756,7 +754,6 @@ private:
rpl::event_stream<not_null<History*>> _historyChanged;
rpl::event_stream<MegagroupParticipant> _megagroupParticipantRemoved;
rpl::event_stream<MegagroupParticipant> _megagroupParticipantAdded;
rpl::event_stream<FolderUpdate> _folderUpdates;
rpl::event_stream<DialogsRowReplacement> _dialogsRowReplacements;
rpl::event_stream<> _stickersUpdated;
@ -773,16 +770,9 @@ private:
Stickers::Order _archivedStickerSetsOrder;
Stickers::SavedGifs _savedGifs;
int _unreadFull = 0;
int _unreadMuted = 0;
int _unreadEntriesFull = 0;
int _unreadEntriesMuted = 0;
Dialogs::IndexedList _chatsList;
Dialogs::IndexedList _importantChatsList;
Dialogs::MainList _chatsList;
Dialogs::IndexedList _contactsList;
Dialogs::IndexedList _contactsNoChatsList;
Dialogs::PinnedList _pinnedChatsList;
base::Timer _selfDestructTimer;
std::vector<FullMsgId> _selfDestructItems;

View File

@ -77,7 +77,7 @@ void Entry::cacheProxyPromoted(bool promoted) {
}
bool Entry::needUpdateInChatList() const {
return inChatList(Dialogs::Mode::All) || shouldBeInChatList();
return inChatList() || shouldBeInChatList();
}
void Entry::updateChatListSortPosition() {
@ -102,6 +102,10 @@ void Entry::updateChatListExistence() {
setChatListExistence(shouldBeInChatList());
}
void Entry::notifyUnreadStateChange(const UnreadState &wasState) {
owner().unreadStateChanged(_key, wasState);
}
void Entry::setChatListExistence(bool exists) {
if (const auto main = App::main()) {
if (exists && _sortKeyInChatList) {
@ -117,9 +121,6 @@ TimeId Entry::adjustedChatListTimeId() const {
return chatListTimeId();
}
void Entry::changedInChatListHook(Dialogs::Mode list, bool added) {
}
void Entry::changedChatListPinHook() {
}
@ -160,7 +161,9 @@ int Entry::posInChatList(Dialogs::Mode list) const {
not_null<Row*> Entry::addToChatList(Mode list) {
if (!inChatList(list)) {
chatListLinks(list) = myChatsList(list)->addToEnd(_key);
changedInChatListHook(list, true);
if (list == Mode::All) {
owner().unreadEntryChanged(_key, true);
}
}
return mainChatListLink(list);
}
@ -169,7 +172,9 @@ void Entry::removeFromChatList(Dialogs::Mode list) {
if (inChatList(list)) {
myChatsList(list)->del(_key);
chatListLinks(list).clear();
changedInChatListHook(list, false);
if (list == Mode::All) {
owner().unreadEntryChanged(_key, false);
}
}
}
@ -194,7 +199,7 @@ void Entry::addChatListEntryByLetter(
void Entry::updateChatListEntry() const {
if (const auto main = App::main()) {
if (inChatList(Mode::All)) {
if (inChatList()) {
main->repaintDialogRow(
Mode::All,
mainChatListLink(Mode::All));
@ -212,10 +217,7 @@ void Entry::updateChatListEntry() const {
}
not_null<IndexedList*> Entry::myChatsList(Mode list) const {
if (const auto current = folder()) {
return current->chatsList(list);
}
return owner().chatsList(list);
return owner().chatsList(folder())->indexed(list);
}
} // namespace Dialogs

View File

@ -40,6 +40,15 @@ struct PositionChange {
int to = -1;
};
struct UnreadState {
std::optional<int> messagesCount;
int messagesCountMuted = 0;
int chatsCount = 0;
int chatsCountMuted = 0;
bool mark = false;
bool markMuted = false;
};
class Entry {
public:
Entry(not_null<Data::Session*> owner, const Key &key);
@ -51,7 +60,7 @@ public:
AuthSession &session() const;
PositionChange adjustByPosInChatList(Mode list);
bool inChatList(Mode list) const {
bool inChatList(Mode list = Mode::All) const {
return !chatListLinks(list).empty();
}
int posInChatList(Mode list) const;
@ -89,6 +98,7 @@ public:
virtual int chatListUnreadCount() const = 0;
virtual bool chatListUnreadMark() const = 0;
virtual bool chatListMutedBadge() const = 0;
virtual UnreadState chatListUnreadState() const = 0;
virtual HistoryItem *chatListMessage() const = 0;
virtual bool chatListMessageKnown() const = 0;
virtual void requestChatListMessage() = 0;
@ -125,10 +135,22 @@ public:
mutable const HistoryItem *textCachedFor = nullptr; // cache
mutable Text lastItemTextCache;
protected:
auto unreadStateChangeNotifier(bool required) {
const auto notify = required && inChatList();
const auto wasState = notify ? chatListUnreadState() : UnreadState();
return gsl::finally([=] {
if (notify) {
notifyUnreadStateChange(wasState);
}
});
}
private:
virtual void changedInChatListHook(Mode list, bool added);
virtual void changedChatListPinHook();
void notifyUnreadStateChange(const UnreadState &wasState);
void setChatListExistence(bool exists);
RowsByLetter &chatListLinks(Mode list);
const RowsByLetter &chatListLinks(Mode list) const;

View File

@ -96,6 +96,14 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
subscribe(session().downloaderTaskFinished(), [=] { update(); });
subscribe(session().notifications().settingsChanged(), [=](
Window::Notifications::ChangeType change) {
if (change == Window::Notifications::ChangeType::CountMessages) {
// Folder rows change their unread badge with this setting.
update();
}
});
session().data().contactsLoaded().changes(
) | rpl::start_with_next([=] {
refresh();
@ -143,8 +151,9 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
) | rpl::filter([=](Data::Folder *folder) {
return (folder == _openedFolder);
}) | rpl::start_with_next([=] {
const auto mode = Global::DialogsMode();
if (_openedFolder
&& _openedFolder->chatsList(Global::DialogsMode())->empty()) {
&& _openedFolder->chatsList()->indexed(mode)->empty()) {
_openedFolder->updateChatListSortPosition();
cancelFolder();
} else {
@ -188,10 +197,6 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
}
}
}));
session().data().folderUpdated(
) | rpl::start_with_next([=](const Data::FolderUpdate &update) {
updateDialogRow({ update.folder, FullMsgId() });
}, lifetime());
_controller->activeChatEntryValue(
) | rpl::combine_previous(
@ -218,8 +223,7 @@ void DialogsInner::handleChatMigration(not_null<ChatData*> chat) {
if (const auto from = chat->owner().historyLoaded(chat)) {
if (const auto to = chat->owner().historyLoaded(channel)) {
if (to->inChatList(Dialogs::Mode::All)
&& from->inChatList(Dialogs::Mode::All)) {
if (to->inChatList() && from->inChatList()) {
removeDialog(from);
}
}
@ -227,8 +231,11 @@ void DialogsInner::handleChatMigration(not_null<ChatData*> chat) {
}
int DialogsInner::dialogsOffset() const {
return (_openedFolder ? openedFolderSkip() : 0)
+ (_importantSwitch ? st::dialogsImportantBarHeight : 0);
return _openedFolder
? openedFolderSkip()
: _importantSwitch
? st::dialogsImportantBarHeight
: 0;
}
int DialogsInner::proxyPromotedCount() const {
@ -298,6 +305,9 @@ bool DialogsInner::changeOpenedFolder(Data::Folder *folder) {
Ui::DialogTextOptions());
}
refresh();
if (_loadMoreCallback) {
_loadMoreCallback();
}
return true;
}
@ -320,7 +330,7 @@ void DialogsInner::paintEvent(QPaintEvent *e) {
}
if (_state == State::Default) {
auto rows = shownDialogs();
if (_importantSwitch) {
if (!_openedFolder && _importantSwitch) {
auto selected = isPressed() ? _importantSwitchPressed : _importantSwitchSelected;
Dialogs::Layout::paintImportantSwitch(p, Global::DialogsMode(), fullWidth, selected);
dialogsClip.translate(0, -st::dialogsImportantBarHeight);
@ -807,12 +817,21 @@ void DialogsInner::selectByMouse(QPoint globalPosition) {
_mouseSelection = true;
_lastMousePosition = globalPosition;
int w = width(), mouseY = local.y();
const auto w = width();
const auto mouseY = local.y();
clearIrrelevantState();
if (_state == State::Default) {
auto importantSwitchSelected = (_importantSwitch && mouseY >= 0 && mouseY < dialogsOffset());
mouseY -= dialogsOffset();
auto selected = importantSwitchSelected ? nullptr : (mouseY >= 0 ? shownDialogs()->rowAtY(mouseY, st::dialogsRowHeight) : nullptr);
const auto switchTop = _openedFolder ? openedFolderSkip() : 0;
const auto switchBottom = dialogsOffset();
const auto importantSwitchSelected = _importantSwitch
&& !_openedFolder
&& (mouseY >= switchTop)
&& (mouseY < switchBottom);
const auto selected = importantSwitchSelected
? nullptr
: (mouseY >= switchBottom)
? shownDialogs()->rowAtY(mouseY - switchBottom, st::dialogsRowHeight)
: nullptr;
if (_selected != selected || _importantSwitchSelected != importantSwitchSelected) {
updateSelectedRow();
_selected = selected;
@ -945,8 +964,7 @@ void DialogsInner::checkReorderPinnedStart(QPoint localPosition) {
if (updateReorderIndexGetCount() < 2) {
_dragging = nullptr;
} else {
const auto folderId = _openedFolder ? _openedFolder->id() : 0;
const auto &order = session().data().pinnedChatsOrder(folderId);
const auto &order = session().data().pinnedChatsOrder(_openedFolder);
_pinnedOnDragStart = base::flat_set<Dialogs::Key>{
order.begin(),
order.end()
@ -989,8 +1007,7 @@ int DialogsInner::countPinnedIndex(Dialogs::Row *ofRow) {
}
void DialogsInner::savePinnedOrder() {
const auto folderId = _openedFolder ? _openedFolder->id() : 0;
const auto &newOrder = session().data().pinnedChatsOrder(folderId);
const auto &newOrder = session().data().pinnedChatsOrder(_openedFolder);
if (newOrder.size() != _pinnedOnDragStart.size()) {
return; // Something has changed in the set of pinned chats.
}
@ -999,7 +1016,7 @@ void DialogsInner::savePinnedOrder() {
return; // Something has changed in the set of pinned chats.
}
}
session().api().savePinnedOrder(folderId);
session().api().savePinnedOrder(_openedFolder);
}
void DialogsInner::finishReorderPinned() {
@ -1513,7 +1530,8 @@ void DialogsInner::updateSelectedRow(Dialogs::Key key) {
} else if (_selected) {
update(0, dialogsOffset() + _selected->pos() * st::dialogsRowHeight, width(), st::dialogsRowHeight);
} else if (_importantSwitchSelected) {
update(0, 0, width(), st::dialogsImportantBarHeight);
const auto switchTop = _openedFolder ? openedFolderSkip() : 0;
update(0, switchTop, width(), st::dialogsImportantBarHeight);
}
} else if (_state == State::Filtered) {
if (key) {
@ -1536,9 +1554,8 @@ void DialogsInner::updateSelectedRow(Dialogs::Key key) {
}
not_null<Dialogs::IndexedList*> DialogsInner::shownDialogs() const {
return _openedFolder
? _openedFolder->chatsList(Global::DialogsMode())
: session().data().chatsList(Global::DialogsMode());
const auto mode = Global::DialogsMode();
return session().data().chatsList(_openedFolder)->indexed(mode);
}
void DialogsInner::leaveEventHook(QEvent *e) {
@ -1676,7 +1693,7 @@ void DialogsInner::applyFilterUpdate(QString newFilter, bool force) {
_filterResultsGlobal.clear();
if (!_searchInChat && !words.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (const auto list = session().data().chatsList(Dialogs::Mode::All); !list->empty()) {
if (const auto list = session().data().chatsList()->indexed(); !list->empty()) {
for (fi = fb; fi != fe; ++fi) {
const auto found = list->filtered(fi->at(0));
if (!found || found->empty()) {
@ -2001,7 +2018,7 @@ void DialogsInner::peerSearchReceived(
for (const auto &mtpPeer : result) {
if (const auto peer = session().data().peerLoaded(peerFromMTP(mtpPeer))) {
if (const auto history = peer->owner().historyLoaded(peer)) {
if (history->inChatList(Dialogs::Mode::All)) {
if (history->inChatList()) {
continue; // skip existing chats
}
}
@ -2017,7 +2034,7 @@ void DialogsInner::peerSearchReceived(
}
void DialogsInner::notify_historyMuteUpdated(History *history) {
if (!_importantSwitch || !history->inChatList(Dialogs::Mode::All)) {
if (!_importantSwitch || !history->inChatList()) {
return;
}
refreshDialog(history);

View File

@ -14,7 +14,7 @@ namespace Dialogs {
enum class SortMode;
class List {
class List final {
public:
List(SortMode sortMode);
List(const List &other) = delete;

View File

@ -0,0 +1,130 @@
/*
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 "dialogs/dialogs_main_list.h"
#include "observer_peer.h"
#include "history/history.h"
namespace Dialogs {
namespace {
UnreadState ApplyMarkToCounters(const UnreadState &state) {
auto result = UnreadState();
const auto count = state.messagesCount.value_or(0);
result.messagesCount = (count > 0)
? count
: state.mark
? 1
: 0;
result.messagesCountMuted = (state.messagesCountMuted > 0)
? state.messagesCountMuted
: state.markMuted
? 1
: 0;
result.chatsCount = (state.chatsCount > 0)
? state.chatsCount
: state.mark
? 1
: 0;
result.chatsCountMuted = (state.chatsCountMuted > 0)
? state.chatsCountMuted
: state.markMuted
? 1
: 0;
return result;
}
} // namespace
MainList::MainList(rpl::producer<int> pinnedLimit)
: _all(SortMode::Date)
, _important(SortMode::Date)
, _pinned(1) {
_unreadState.messagesCount = 0;
std::move(
pinnedLimit
) | rpl::start_with_next([=](int limit) {
_pinned.setLimit(limit);
}, _lifetime);
Notify::PeerUpdateViewer(
Notify::PeerUpdate::Flag::NameChanged
) | rpl::start_with_next([=](const Notify::PeerUpdate &update) {
const auto peer = update.peer;
const auto &oldLetters = update.oldNameFirstLetters;
_all.peerNameChanged(Mode::All, peer, oldLetters);
_important.peerNameChanged(Mode::Important, peer, oldLetters);
}, _lifetime);
}
bool MainList::empty() const {
return _all.empty();
}
bool MainList::loaded() const {
return _loaded;
}
void MainList::setLoaded(bool loaded) {
_loaded = loaded;
}
void MainList::clear() {
_all.clear();
_important.clear();
_unreadState = UnreadState();
}
void MainList::unreadStateChanged(
const UnreadState &wasState,
const UnreadState &nowState) {
const auto wasWithMark = ApplyMarkToCounters(wasState);
const auto nowWithMark = ApplyMarkToCounters(nowState);
*_unreadState.messagesCount += *nowWithMark.messagesCount
- *wasWithMark.messagesCount;
_unreadState.messagesCountMuted += nowWithMark.messagesCountMuted
- wasWithMark.messagesCountMuted;
_unreadState.chatsCount += nowWithMark.chatsCount
- wasWithMark.chatsCount;
_unreadState.chatsCountMuted += nowWithMark.chatsCountMuted
- wasWithMark.chatsCountMuted;
}
void MainList::unreadEntryChanged(
const Dialogs::UnreadState &state,
bool added) {
const auto withMark = ApplyMarkToCounters(state);
const auto delta = (added ? 1 : -1);
*_unreadState.messagesCount += delta * *withMark.messagesCount;
_unreadState.messagesCountMuted += delta * withMark.messagesCountMuted;
_unreadState.chatsCount += delta * withMark.chatsCount;
_unreadState.chatsCountMuted += delta * withMark.chatsCountMuted;
}
UnreadState MainList::unreadState() const {
return _unreadState;
}
not_null<IndexedList*> MainList::indexed(Mode list) {
return (list == Mode::All) ? &_all : &_important;
}
not_null<const IndexedList*> MainList::indexed(Mode list) const {
return (list == Mode::All) ? &_all : &_important;
}
not_null<PinnedList*> MainList::pinned() {
return &_pinned;
}
not_null<const PinnedList*> MainList::pinned() const {
return &_pinned;
}
} // namespace Dialogs

View File

@ -0,0 +1,49 @@
/*
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_indexed_list.h"
#include "dialogs/dialogs_pinned_list.h"
namespace Dialogs {
class MainList final {
public:
explicit MainList(rpl::producer<int> pinnedLimit);
bool empty() const;
bool loaded() const;
void setLoaded(bool loaded = true);
void clear();
void unreadStateChanged(
const UnreadState &wasState,
const UnreadState &nowState);
void unreadEntryChanged(
const Dialogs::UnreadState &state,
bool added);
UnreadState unreadState() const;
not_null<IndexedList*> indexed(Mode list = Mode::All);
not_null<const IndexedList*> indexed(Mode list = Mode::All) const;
not_null<PinnedList*> pinned();
not_null<const PinnedList*> pinned() const;
private:
IndexedList _all;
IndexedList _important;
PinnedList _pinned;
UnreadState _unreadState;
bool _loaded = false;
rpl::lifetime _lifetime;
};
} // namespace Dialogs

View File

@ -41,7 +41,7 @@ int PinnedList::addPinnedGetPosition(const Key &key) {
applyLimit(_limit - 1);
const auto position = int(_data.size());
_data.push_back(key);
key.entry()->cachePinnedIndex(position);
key.entry()->cachePinnedIndex(position + 1);
return position;
}

View File

@ -15,7 +15,7 @@ namespace Dialogs {
class Key;
class PinnedList {
class PinnedList final {
public:
explicit PinnedList(int limit);

View File

@ -248,9 +248,8 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
onSearchMore();
} else {
const auto folder = _inner->shownFolder();
const auto folderId = folder ? folder->id() : FolderId(0);
if (!folderId || !folder->chatsListLoaded()) {
session().api().requestDialogs(folderId);
if (!folder || !folder->chatsListLoaded()) {
session().api().requestDialogs(folder);
}
}
});
@ -378,13 +377,13 @@ void DialogsWidget::activate() {
}
void DialogsWidget::refreshDialog(Dialogs::Key key) {
const auto creating = !key.entry()->inChatList(Dialogs::Mode::All);
const auto creating = !key.entry()->inChatList();
_inner->refreshDialog(key);
const auto history = key.history();
if (creating && history && history->peer->migrateFrom()) {
if (const auto migrated = history->owner().historyLoaded(
history->peer->migrateFrom())) {
if (migrated->inChatList(Dialogs::Mode::All)) {
if (migrated->inChatList()) {
_inner->removeDialog(migrated);
}
}

View File

@ -1610,98 +1610,69 @@ int History::unreadCountForBadge() const {
}
bool History::unreadCountKnown() const {
return !!_unreadCount;
return _unreadCount.has_value();
}
void History::setUnreadCount(int newUnreadCount) {
if (!_unreadCount || *_unreadCount != newUnreadCount) {
const auto wasUnread = _unreadMark || unreadCount();
const auto unreadCountDelta = _unreadCount | [&](int count) {
return newUnreadCount - count;
};
if (newUnreadCount == 1) {
if (loadedAtBottom()) {
_firstUnreadView = !isEmpty()
? blocks.back()->messages.back().get()
: nullptr;
}
if (const auto last = msgIdForRead()) {
setInboxReadTill(last - 1);
}
} else if (!newUnreadCount) {
_firstUnreadView = nullptr;
if (const auto last = msgIdForRead()) {
setInboxReadTill(last);
}
} else {
if (!_firstUnreadView && !_unreadBarView && loadedAtBottom()) {
calculateFirstUnreadMessage();
}
}
const auto unreadMarkDelta = [&] {
if (_unreadMark) {
const auto was = _unreadCount && (*_unreadCount > 0);
const auto now = (newUnreadCount > 0);
if (was != now) {
return was ? 1 : -1;
}
}
return 0;
}();
_unreadCount = newUnreadCount;
Expects(folderKnown());
if (_unreadBarView) {
const auto count = chatListUnreadCount();
if (count > 0) {
_unreadBarView->setUnreadBarCount(count);
} else {
_unreadBarView->setUnreadBarFreezed();
}
}
if (inChatList(Dialogs::Mode::All)) {
const auto delta = unreadMarkDelta + (unreadCountDelta
? *unreadCountDelta
: newUnreadCount);
owner().unreadIncrement(delta, mute());
const auto nowUnread = (*_unreadCount > 0) || _unreadMark;
const auto entriesDelta = (wasUnread && !nowUnread)
? -1
: (nowUnread && !wasUnread)
? 1
: 0;
owner().unreadEntriesChanged(
entriesDelta,
mute() ? entriesDelta : 0);
}
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::UnreadViewChanged);
if (_unreadCount == newUnreadCount) {
return;
}
const auto notifier = unreadStateChangeNotifier(true);
_unreadCount = newUnreadCount;
if (newUnreadCount == 1) {
if (loadedAtBottom()) {
_firstUnreadView = !isEmpty()
? blocks.back()->messages.back().get()
: nullptr;
}
if (const auto last = msgIdForRead()) {
setInboxReadTill(last - 1);
}
} else if (!newUnreadCount) {
_firstUnreadView = nullptr;
if (const auto last = msgIdForRead()) {
setInboxReadTill(last);
}
} else {
if (!_firstUnreadView && !_unreadBarView && loadedAtBottom()) {
calculateFirstUnreadMessage();
}
}
if (_unreadBarView) {
const auto count = chatListUnreadCount();
if (count > 0) {
_unreadBarView->setUnreadBarCount(count);
} else {
_unreadBarView->setUnreadBarFreezed();
}
}
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::UnreadViewChanged);
}
void History::setUnreadMark(bool unread) {
if (clearUnreadOnClientSide()) {
unread = false;
}
if (_unreadMark != unread) {
_unreadMark = unread;
if (!_unreadCount || !*_unreadCount) {
if (inChatList(Dialogs::Mode::All)) {
const auto delta = _unreadMark ? 1 : -1;
owner().unreadIncrement(delta, mute());
owner().unreadEntriesChanged(
delta,
mute() ? delta : 0);
updateChatListEntry();
}
}
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::UnreadViewChanged);
if (_unreadMark == unread) {
return;
}
const auto noUnreadMessages = !unreadCount();
const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
_unreadMark = unread;
if (inChatList() && noUnreadMessages) {
updateChatListEntry();
}
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::UnreadViewChanged);
}
bool History::unreadMark() const {
@ -1728,38 +1699,15 @@ bool History::changeMute(bool newMute) {
if (_mute == newMute) {
return false;
}
const auto notify = (unreadCountForBadge() > 0);
const auto notifier = unreadStateChangeNotifier(notify);
_mute = newMute;
//const auto feed = peer->isChannel() // #feed
// ? peer->asChannel()->feed()
// : nullptr;
//if (feed) {
// if (_unreadCount) {
// if (*_unreadCount) {
// const auto unreadCountDelta = 0;
// const auto mutedCountDelta = _mute ? *_unreadCount : -*_unreadCount;
// feed->unreadCountChanged(unreadCountDelta, mutedCountDelta);
// }
// } else {
// session().api().requestDialogEntry(this);
// session().api().requestDialogEntry(feed);
// }
//}
if (inChatList(Dialogs::Mode::All)) {
if (const auto count = unreadCountForBadge()) {
owner().unreadMuteChanged(count, _mute);
const auto entriesWithUnreadDelta = 0;
const auto mutedEntriesWithUnreadDelta = _mute ? 1 : -1;
owner().unreadEntriesChanged(
entriesWithUnreadDelta,
mutedEntriesWithUnreadDelta);
Notify::unreadCounterUpdated();
}
if (inChatList()) {
Notify::historyMuteUpdated(this);
updateChatListEntry();
}
updateChatListEntry();
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::NotificationsEnabled);
@ -1839,9 +1787,9 @@ void History::setFolderPointer(Data::Folder *folder) {
owner().setChatPinned(this, false);
}
const auto wasKnown = folderKnown();
const auto wasInAll = inChatList(Mode::All);
const auto wasInImportant = wasInAll && inChatList(Mode::Important);
if (wasInAll) {
const auto wasInList = inChatList();
const auto wasInImportant = wasInList && inChatList(Mode::Important);
if (wasInList) {
removeFromChatList(Mode::All);
if (wasInImportant) {
removeFromChatList(Mode::Important);
@ -1852,7 +1800,7 @@ void History::setFolderPointer(Data::Folder *folder) {
if (was) {
was->unregisterOne(this);
}
if (wasInAll) {
if (wasInList) {
addToChatList(Mode::All);
if (wasInImportant) {
addToChatList(Mode::Important);
@ -2068,6 +2016,18 @@ bool History::chatListMutedBadge() const {
return mute();
}
Dialogs::UnreadState History::chatListUnreadState() const {
auto result = Dialogs::UnreadState();
const auto count = _unreadCount.value_or(0);
result.messagesCount = _unreadCount;
result.messagesCountMuted = (_unreadCount && mute()) ? count : 0;
result.chatsCount = count ? 1 : 0;
result.chatsCountMuted = (count && mute()) ? 1 : 0;
result.mark = _unreadMark;
result.markMuted = mute() ? _unreadMark : false;
return result;
}
HistoryItem *History::chatListMessage() const {
return _chatListMessage.value_or(nullptr);
}
@ -2482,8 +2442,16 @@ bool History::isServerSideUnread(not_null<const HistoryItem*> item) const {
: (!_inboxReadBefore || (item->id >= *_inboxReadBefore));
}
void History::applyDialog(FolderId requestFolderId, const MTPDdialog &data) {
void History::applyDialog(
Data::Folder *requestFolder,
const MTPDdialog &data) {
const auto folder = data.has_folder_id()
? (data.vfolder_id.v
? owner().folder(data.vfolder_id.v).get()
: nullptr)
: requestFolder;
applyDialogFields(
folder,
data.vunread_count.v,
data.vread_inbox_max_id.v,
data.vread_outbox_max_id.v);
@ -2512,15 +2480,6 @@ void History::applyDialog(FolderId requestFolderId, const MTPDdialog &data) {
if (data.has_draft() && data.vdraft.type() == mtpc_draftMessage) {
Data::applyPeerCloudDraft(peer->id, data.vdraft.c_draftMessage());
}
const auto folderId = data.has_folder_id()
? data.vfolder_id.v
: requestFolderId;
if (folderId) {
setFolder(owner().folder(folderId));
} else {
clearFolder();
}
session().api().dialogEntryApplied(this);
}
@ -2593,9 +2552,15 @@ bool History::skipUnreadUpdate() const {
}
void History::applyDialogFields(
Data::Folder *folder,
int unreadCount,
MsgId maxInboxRead,
MsgId maxOutboxRead) {
if (folder) {
setFolder(folder);
} else {
clearFolder();
}
if (!skipUnreadUpdate()) {
setUnreadCount(unreadCount);
setInboxReadTill(maxInboxRead);
@ -3089,19 +3054,6 @@ void History::applyGroupAdminChanges(
}
}
void History::changedInChatListHook(Dialogs::Mode list, bool added) {
if (list == Dialogs::Mode::All) {
if (const auto delta = unreadCountForBadge() * (added ? 1 : -1)) {
owner().unreadIncrement(delta, mute());
const auto entriesDelta = added ? 1 : -1;
owner().unreadEntriesChanged(
entriesDelta,
mute() ? entriesDelta : 0);
}
}
}
void History::changedChatListPinHook() {
Notify::peerUpdatedDelayed(
peer,

View File

@ -191,9 +191,10 @@ public:
bool lastMessageKnown() const;
void unknownMessageDeleted(MsgId messageId);
void applyDialogTopMessage(MsgId topMessageId);
void applyDialog(FolderId requestFolderId, const MTPDdialog &data);
void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data);
void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
void applyDialogFields(
Data::Folder *folder,
int unreadCount,
MsgId maxInboxRead,
MsgId maxOutboxRead);
@ -299,6 +300,7 @@ public:
int chatListUnreadCount() const override;
bool chatListUnreadMark() const override;
bool chatListMutedBadge() const override;
Dialogs::UnreadState chatListUnreadState() const override;
HistoryItem *chatListMessage() const override;
bool chatListMessageKnown() const override;
void requestChatListMessage() override;
@ -417,7 +419,6 @@ private:
void removeNotification(not_null<HistoryItem*> item);
TimeId adjustedChatListTimeId() const override;
void changedInChatListHook(Dialogs::Mode list, bool added) override;
void changedChatListPinHook() override;
void setInboxReadTill(MsgId upTo);

View File

@ -2872,16 +2872,16 @@ void MainWidget::gotChannelDifference(
history->setNotLoadedAtBottom();
session().api().requestChannelRangeDifference(history);
}
App::feedMsgs(data.vmessages, NewMessageLast);
data.vdialog.match([&](const MTPDdialog &data) {
if (data.has_pts()) {
channel->ptsInit(data.vpts.v);
}
if (history) {
history->applyDialog(data);
}
}, [&](const MTPDdialogFolder &) {
});
session().data().applyDialogs(
nullptr,
data.vmessages.v,
QVector<MTPDialog>(1, data.vdialog));
if (_history->peer() == channel) {
_history->updateHistoryDownVisibility();
_history->preloadHistoryIfNeeded();
@ -4270,6 +4270,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updatePinnedDialogs: {
const auto &d = update.c_updatePinnedDialogs();
const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0;
const auto loaded = !folderId
|| (session().data().folderLoaded(folderId) != nullptr);
const auto folder = folderId
? session().data().folder(folderId).get()
: nullptr;
const auto done = [&] {
if (!d.has_order()) {
return false;
@ -4280,6 +4285,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
return !session().data().historyLoaded(
peerFromMTP(data.vpeer));
}, [&](const MTPDdialogPeerFolder &data) {
if (folderId) {
LOG(("API Error: "
"updatePinnedDialogs has nested folders."));
return true;
}
return !session().data().folderLoaded(data.vfolder_id.v);
});
};
@ -4288,17 +4298,23 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
if (!allLoaded) {
return false;
}
session().data().applyPinnedChats(folderId, order);
session().data().applyPinnedChats(folder, order);
return true;
}();
if (!done) {
session().api().requestPinnedDialogs(folderId);
session().api().requestPinnedDialogs(folder);
}
if (!loaded) {
session().api().requestDialogEntry(folder);
}
} break;
case mtpc_updateDialogPinned: {
const auto &d = update.c_updateDialogPinned();
const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0;
const auto folder = folderId
? session().data().folder(folderId).get()
: nullptr;
const auto done = d.vpeer.match([&](const MTPDdialogPeer &data) {
const auto id = peerFromMTP(data.vpeer);
if (const auto history = session().data().historyLoaded(id)) {
@ -4329,7 +4345,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
return false;
});
if (!done) {
session().api().requestPinnedDialogs(folderId);
session().api().requestPinnedDialogs(folder);
}
} break;

View File

@ -94,13 +94,13 @@ private:
};
History *FindWastedPin(FolderId folderId) {
const auto &order = Auth().data().pinnedChatsOrder(folderId);
History *FindWastedPin(Data::Folder *folder) {
const auto &order = Auth().data().pinnedChatsOrder(folder);
for (const auto &pinned : order) {
if (const auto history = pinned.history()) {
if (history->peer->isChat()
&& history->peer->asChat()->isDeactivated()
&& !history->inChatList(Dialogs::Mode::All)) {
&& !history->inChatList()) {
return history;
}
}
@ -116,17 +116,16 @@ bool PinnedLimitReached(Dialogs::Key key) {
Expects(key.entry()->folderKnown());
const auto folder = key.entry()->folder();
const auto folderId = folder ? folder->id() : 0;
const auto pinnedCount = Auth().data().pinnedChatsCount(folderId);
const auto pinnedMax = Auth().data().pinnedChatsLimit(folderId);
const auto pinnedCount = Auth().data().pinnedChatsCount(folder);
const auto pinnedMax = Auth().data().pinnedChatsLimit(folder);
if (pinnedCount < pinnedMax) {
return false;
}
// Some old chat, that was converted, maybe is still pinned.
if (const auto wasted = FindWastedPin(folderId)) {
if (const auto wasted = FindWastedPin(folder)) {
Auth().data().setChatPinned(wasted, false);
Auth().data().setChatPinned(key, true);
Auth().api().savePinnedOrder(folderId);
Auth().api().savePinnedOrder(folder);
} else {
auto errorText = lng_error_pinned_max(
lt_count,

View File

@ -228,6 +228,8 @@
<(src_loc)/dialogs/dialogs_layout.h
<(src_loc)/dialogs/dialogs_list.cpp
<(src_loc)/dialogs/dialogs_list.h
<(src_loc)/dialogs/dialogs_main_list.cpp
<(src_loc)/dialogs/dialogs_main_list.h
<(src_loc)/dialogs/dialogs_pinned_list.cpp
<(src_loc)/dialogs/dialogs_pinned_list.h
<(src_loc)/dialogs/dialogs_row.cpp