From 2a0b9a44dd2df2826966eb7558c0343fc4b113ba Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Jan 2019 15:57:45 +0400 Subject: [PATCH] Remove supergroup migrate messages. --- Telegram/Resources/langs/lang.strings | 1 - Telegram/SourceFiles/apiwrap.cpp | 146 ++++---- Telegram/SourceFiles/apiwrap.h | 3 +- Telegram/SourceFiles/app.cpp | 4 +- Telegram/SourceFiles/base/algorithm.h | 6 + Telegram/SourceFiles/boxes/confirm_box.cpp | 21 +- .../SourceFiles/boxes/create_poll_box.cpp | 2 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 2 +- Telegram/SourceFiles/boxes/share_box.cpp | 2 +- Telegram/SourceFiles/core/utils.h | 28 -- Telegram/SourceFiles/data/data_feed.cpp | 132 +++---- Telegram/SourceFiles/data/data_feed.h | 34 +- .../SourceFiles/data/data_media_types.cpp | 12 +- Telegram/SourceFiles/data/data_media_types.h | 10 +- Telegram/SourceFiles/data/data_session.cpp | 6 +- .../SourceFiles/dialogs/dialogs_entry.cpp | 8 +- Telegram/SourceFiles/dialogs/dialogs_entry.h | 18 +- .../dialogs/dialogs_indexed_list.cpp | 12 +- .../dialogs/dialogs_inner_widget.cpp | 12 +- .../SourceFiles/dialogs/dialogs_layout.cpp | 4 +- Telegram/SourceFiles/dialogs/dialogs_list.cpp | 12 +- .../admin_log/history_admin_log_inner.cpp | 2 +- Telegram/SourceFiles/history/history.cpp | 345 +++++++++++++++--- Telegram/SourceFiles/history/history.h | 30 +- .../history/history_inner_widget.cpp | 26 +- .../history/history_inner_widget.h | 5 +- Telegram/SourceFiles/history/history_item.cpp | 10 +- Telegram/SourceFiles/history/history_item.h | 6 +- .../SourceFiles/history/history_service.cpp | 147 +++++--- .../SourceFiles/history/history_service.h | 5 +- .../SourceFiles/history/history_widget.cpp | 44 +-- Telegram/SourceFiles/history/history_widget.h | 1 - .../view/history_view_top_bar_widget.cpp | 2 +- .../SourceFiles/info/feed/info_feed_cover.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 33 +- Telegram/SourceFiles/mainwidget.h | 2 +- .../mtproto/dedicated_file_loader.cpp | 19 +- Telegram/SourceFiles/mtproto/type_utils.h | 4 +- .../passport/passport_panel_edit_document.cpp | 2 +- .../SourceFiles/window/window_controller.cpp | 16 +- 40 files changed, 695 insertions(+), 481 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 892a64ede5..846f0f6abf 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -899,7 +899,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_changed_title_channel" = "Channel name was changed to «{title}»"; "lng_action_created_chat" = "{from} created group «{title}»"; "lng_action_created_channel" = "Channel created"; -"lng_action_group_migrate" = "The group was upgraded to a supergroup"; "lng_action_pinned_message" = "{from} pinned «{text}»"; "lng_action_pinned_media" = "{from} pinned {media}"; "lng_action_pinned_media_photo" = "a photo"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index c6e6c3bc3c..d5f264327c 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -416,7 +416,7 @@ void ApiWrap::savePinnedOrder() { const auto &order = _session->data().pinnedDialogsOrder(); auto peers = QVector(); peers.reserve(order.size()); - for (const auto &pinned : base::reversed(order)) { + for (const auto &pinned : ranges::view::reverse(order)) { if (const auto history = pinned.history()) { peers.push_back(MTP_inputDialogPeer(history->peer->input)); } else if (const auto feed = pinned.feed()) { @@ -708,7 +708,7 @@ void ApiWrap::requestDialogEntry( MTP_vector(std::move(peers)) )).done([=](const MTPmessages_PeerDialogs &result) { applyPeerDialogs(result); - historyDialogEntryApplied(history); + history->dialogEntryApplied(); finalize(); }).fail([=](const RPCError &error) { finalize(); @@ -744,7 +744,7 @@ void ApiWrap::requestDialogEntries( )).done([=](const MTPmessages_PeerDialogs &result) { applyPeerDialogs(result); for (const auto history : histories) { - historyDialogEntryApplied(history); + history->dialogEntryApplied(); } finalize(histories); }).fail([=](const RPCError &error) { @@ -778,89 +778,41 @@ void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) { _session->data().sendHistoryChangeNotifications(); } -void ApiWrap::historyDialogEntryApplied(not_null history) { - if (!history->lastMessage()) { - if (const auto chat = history->peer->asChat()) { - if (!chat->haveLeft()) { - Local::addSavedPeer( - history->peer, - ParseDateTime(history->chatsListTimeId())); - } - } else if (const auto channel = history->peer->asChannel()) { - const auto inviter = channel->inviter; - if (inviter != 0 && channel->amIn()) { - if (const auto from = App::userLoaded(inviter)) { - history->unloadBlocks(); - history->addNewerSlice(QVector()); - history->insertJoinedMessage(true); - } - } - } else { - App::main()->deleteConversation(history->peer, false); - } - return; - } - - if (history->chatsListTimeId() != 0 && history->loadedAtBottom()) { - if (const auto channel = history->peer->asChannel()) { - const auto inviter = channel->inviter; - if (inviter != 0 - && history->chatsListTimeId() <= channel->inviteDate - && channel->amIn()) { - if (const auto from = App::userLoaded(inviter)) { - history->insertJoinedMessage(true); - } - } - } - } - history->updateChatListExistence(); -} - void ApiWrap::applyFeedDialogs( not_null feed, const MTPmessages_Dialogs &dialogs) { - const auto [dialogsList, messagesList] = [&] { - const auto process = [&](const auto &data) { - App::feedUsers(data.vusers); - App::feedChats(data.vchats); - return std::make_tuple(&data.vdialogs.v, &data.vmessages.v); - }; - switch (dialogs.type()) { - case mtpc_messages_dialogs: - return process(dialogs.c_messages_dialogs()); - - case mtpc_messages_dialogsSlice: - LOG(("API Error: " - "Unexpected dialogsSlice in feed dialogs list.")); - return process(dialogs.c_messages_dialogsSlice()); - } - Unexpected("Type in DialogsWidget::dialogsReceived"); - }(); - - App::feedMsgs(*messagesList, NewMessageLast); + if (dialogs.type() == mtpc_messages_dialogsNotModified) { + LOG(("API Error: " + "messages.dialogsNotModified in ApiWrap::applyFeedDialogs.")); + return; + } auto channels = std::vector>(); - channels.reserve(dialogsList->size()); - for (const auto &dialog : *dialogsList) { - switch (dialog.type()) { - case mtpc_dialog: { - if (const auto peerId = peerFromMTP(dialog.c_dialog().vpeer)) { - if (peerIsChannel(peerId)) { - const auto history = App::history(peerId); - history->applyDialog(dialog.c_dialog()); - channels.emplace_back(history->peer->asChannel()); - } else { - LOG(("API Error: " - "Unexpected non-channel in feed dialogs list.")); + dialogs.match([&](const MTPDmessages_dialogsNotModified &) { + Unexpected("Type in ApiWrap::applyFeedDialogs."); + }, [&](const auto &data) { + App::feedUsers(data.vusers); + App::feedChats(data.vchats); + App::feedMsgs(data.vmessages.v, NewMessageLast); + channels.reserve(data.vdialogs.v.size()); + for (const auto &dialog : data.vdialogs.v) { + dialog.match([&](const MTPDdialog &data) { + if (const auto peerId = peerFromMTP(data.vpeer)) { + if (peerIsChannel(peerId)) { + const auto history = App::history(peerId); + history->applyDialog(dialog.c_dialog()); + channels.emplace_back(history->peer->asChannel()); + } else { + LOG(("API Error: " + "Unexpected peer in feed dialogs list.")); + } } - } - } break; - //case mtpc_dialogFeed: { // #feed - // LOG(("API Error: Unexpected dialogFeed in feed dialogs list.")); - //} break; - default: Unexpected("Type in DialogsInner::dialogsReceived"); + //}, [&](const MTPDdialogFeed &) { // #feed + // LOG(("API Error: " + // "Unexpected dialogFeed in feed dialogs list.")); + }); } - } + }); feed->setChannels(channels); _session->data().sendHistoryChangeNotifications(); @@ -878,6 +830,34 @@ void ApiWrap::changeDialogUnreadMark( )).send(); } +void ApiWrap::requestFakeChatListMessage( + not_null history) { + if (_fakeChatListRequests.contains(history)) { + return; + } + + _fakeChatListRequests.emplace(history); + request(MTPmessages_GetHistory( + history->peer->input, + MTP_int(0), // offset_id + MTP_int(0), // offset_date + MTP_int(0), // add_offset + MTP_int(2), // limit + MTP_int(0), // max_id + MTP_int(0), // min_id + MTP_int(0) + )).done([=](const MTPmessages_Messages &result) { + _fakeChatListRequests.erase(history); + history->setFakeChatListMessageFrom(result); + }).fail([=](const RPCError &error) { + _fakeChatListRequests.erase(history); + history->setFakeChatListMessageFrom(MTP_messages_messages( + MTP_vector(0), + MTP_vector(0), + MTP_vector(0))); + }).send(); +} + void ApiWrap::requestFullPeer(not_null peer) { if (_fullPeerRequests.contains(peer)) { return; @@ -1768,9 +1748,7 @@ void ApiWrap::deleteAllFromUserSend( if (offset > 0) { deleteAllFromUserSend(channel, from); } else if (const auto history = App::historyLoaded(channel)) { - if (!history->lastMessageKnown()) { - requestDialogEntry(history); - } + history->requestChatListMessage(); } }).send(); } @@ -2303,9 +2281,11 @@ int ApiWrap::OnlineTillFromStatus( void ApiWrap::clearHistory(not_null peer) { auto deleteTillId = MsgId(0); - if (auto history = App::historyLoaded(peer->id)) { + if (const auto history = App::historyLoaded(peer->id)) { if (const auto last = history->lastMessage()) { deleteTillId = last->id; + } + if (const auto last = history->chatListMessage()) { Local::addSavedPeer(history->peer, ItemDateTime(last)); } history->clear(); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 5117353f24..7342b8d97a 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -90,6 +90,7 @@ public: // const std::vector> &channels); void changeDialogUnreadMark(not_null history, bool unread); //void changeDialogUnreadMark(not_null feed, bool unread); // #feed + void requestFakeChatListMessage(not_null history); void requestFullPeer(not_null peer); void requestPeer(not_null peer); @@ -428,7 +429,6 @@ private: QVector collectMessageIds(const MessageDataRequests &requests); MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false); void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs); - void historyDialogEntryApplied(not_null history); void applyFeedDialogs( not_null feed, const MTPmessages_Dialogs &dialogs); @@ -666,6 +666,7 @@ private: base::flat_map< not_null, std::vector>> _dialogRequests; + base::flat_set> _fakeChatListRequests; base::flat_map, mtpRequestId> _unreadMentionsRequests; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 4e9f2d106a..97fd21d2f0 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -380,7 +380,7 @@ namespace App { if (j != data->cend()) { const auto history = (*j)->history(); (*j)->destroy(); - if (!history->lastMessageKnown()) { + if (!history->chatListMessageKnown()) { historiesToCheck.emplace(history); } } else if (affectedHistory) { @@ -388,7 +388,7 @@ namespace App { } } for (const auto history : historiesToCheck) { - Auth().api().requestDialogEntry(history); + history->requestChatListMessage(); } } diff --git a/Telegram/SourceFiles/base/algorithm.h b/Telegram/SourceFiles/base/algorithm.h index 0369859db8..62ca24e479 100644 --- a/Telegram/SourceFiles/base/algorithm.h +++ b/Telegram/SourceFiles/base/algorithm.h @@ -24,6 +24,12 @@ inline constexpr size_t array_size(const Type(&)[Size]) { return Size; } +template +inline bool contains(const Container &container, const T &value) { + const auto end = std::end(container); + return std::find(std::begin(container), end, value) != end; +} + } // namespace base template diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index e9dd8b2700..e32fd51f45 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -605,25 +605,26 @@ void DeleteMessagesBox::deleteAndClear() { _deleteConfirmedCallback(); } - QMap> idsByPeer; + base::flat_map, QVector> idsByPeer; for (const auto itemId : _ids) { - if (auto item = App::histItemById(itemId)) { - auto history = item->history(); - auto wasOnServer = (item->id > 0); - auto wasLast = (history->lastMessage() == item); + if (const auto item = App::histItemById(itemId)) { + const auto history = item->history(); + const auto wasOnServer = IsServerMsgId(item->id); + const auto wasLast = (history->lastMessage() == item); + const auto wasInChats = (history->chatListMessage() == item); item->destroy(); if (wasOnServer) { idsByPeer[history->peer].push_back(MTP_int(itemId.msg)); - } else if (wasLast && !history->lastMessageKnown()) { - history->session().api().requestDialogEntry(history); + } else if (wasLast || wasInChats) { + history->requestChatListMessage(); } } } - auto forEveryone = _forEveryone ? _forEveryone->checked() : false; - for (auto i = idsByPeer.cbegin(), e = idsByPeer.cend(); i != e; ++i) { - App::main()->deleteMessages(i.key(), i.value(), forEveryone); + const auto revoke = _forEveryone ? _forEveryone->checked() : false; + for (const auto &[peer, ids] : idsByPeer) { + App::main()->deleteMessages(peer, ids, revoke); } Ui::hideLayer(); Auth().data().sendHistoryChangeNotifications(); diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 9d4546e313..65c05109e0 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -435,7 +435,7 @@ void Options::removeEmptyTail() { _list, &Option::hasFocus); const auto end = _list.end(); - auto reversed = ranges::view::reverse(_list); + const auto reversed = ranges::view::reverse(_list); const auto emptyItem = ranges::find_if( reversed, ranges::not_fn(&Option::isEmpty)).base(); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 9dae7b5063..72712713d7 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -1830,7 +1830,7 @@ void SendFilesBox::updateControlsGeometry() { _sendPhotos.data(), _sendFiles.data() }; - for (auto pointer : base::reversed(pointers)) { + for (const auto pointer : ranges::view::reverse(pointers)) { if (pointer) { pointer->moveToLeft( st::boxPhotoPadding.left(), diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 3a81e70023..9bedd7e31f 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -966,7 +966,7 @@ void ShareBox::Inner::updateFilter(QString filter) { if (toFilter) { _filtered.reserve(toFilter->size()); for (const auto row : *toFilter) { - auto &nameWords = row->entry()->chatsListNameWords(); + const auto &nameWords = row->entry()->chatListNameWords(); auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb; for (fi = fb; fi != fe; ++fi) { auto filterName = *fi; diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 678707f699..e8b0d33b9b 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -36,12 +36,6 @@ inline constexpr D up_cast(T object) { } } -template -inline bool contains(const Container &container, const T &value) { - auto end = std::end(container); - return std::find(std::begin(container), end, value) != end; -} - // We need a custom comparator for std::set>::find to work with pointers. // thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs template @@ -86,28 +80,6 @@ using set_of_unique_ptr = std::set, base::pointer_comparator< template using set_of_shared_ptr = std::set, base::pointer_comparator>; -// Thanks https://stackoverflow.com/a/28139075 - -template -struct reversion_wrapper { - Container &container; -}; - -template -auto begin(reversion_wrapper wrapper) { - return std::rbegin(wrapper.container); -} - -template -auto end(reversion_wrapper wrapper) { - return std::rend(wrapper.container); -} - -template -reversion_wrapper reversed(Container &&container) { - return { container }; -} - template inline bool in_range(Value &&value, From &&from, Till &&till) { return (value >= from) && (value < till); diff --git a/Telegram/SourceFiles/data/data_feed.cpp b/Telegram/SourceFiles/data/data_feed.cpp index cff371824d..70336eeebd 100644 --- a/Telegram/SourceFiles/data/data_feed.cpp +++ b/Telegram/SourceFiles/data/data_feed.cpp @@ -31,14 +31,22 @@ namespace Data { // data.vid.v)); //} -Feed::Feed(FeedId id, not_null parent) +Feed::Feed(not_null owner, FeedId id) : Entry(this) , _id(id) -, _parent(parent) +, _owner(owner) , _name(lang(lng_feed_name)) { indexNameParts(); } +Data::Session &Feed::owner() const { + return *_owner; +} + +AuthSession &Feed::session() const { + return _owner->session(); +} + FeedId Feed::id() const { return _id; } @@ -74,17 +82,17 @@ void Feed::registerOne(not_null channel) { if (!base::contains(_channels, history)) { const auto invisible = (_channels.size() < 2); _channels.push_back(history); - _parent->session().storage().invalidate( + session().storage().invalidate( Storage::FeedMessagesInvalidate(_id)); - if (history->lastMessageKnown()) { - if (const auto last = history->lastMessage()) { - if (justUpdateLastMessage(last)) { + if (history->chatListMessageKnown()) { + if (const auto last = history->chatListMessage()) { + if (justUpdateChatListMessage(last)) { updateChatListEntry(); } } - } else if (lastMessageKnown()) { - _parent->session().api().requestDialogEntry(history); + } else if (chatListMessageKnown()) { + history->requestChatListMessage(); } if (unreadCountKnown()) { if (history->unreadCountKnown()) { @@ -96,7 +104,7 @@ void Feed::registerOne(not_null channel) { unreadCountChanged(count, history->mute() ? count : 0); } } else if (!_settingChannels) { - _parent->session().api().requestDialogEntry(this); + session().api().requestDialogEntry(this); } } if (invisible && _channels.size() > 1) { @@ -107,7 +115,7 @@ void Feed::registerOne(not_null channel) { } else { history->updateChatListExistence(); } - _parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels); + _owner->notifyFeedUpdated(this, FeedUpdateFlag::Channels); } } @@ -117,13 +125,13 @@ void Feed::unregisterOne(not_null channel) { if (i != end(_channels)) { const auto visible = (_channels.size() > 1); _channels.erase(i, end(_channels)); - _parent->session().storage().remove( + session().storage().remove( Storage::FeedMessagesRemoveAll(_id, channel->bareId())); - if (lastMessageKnown()) { - if (const auto last = lastMessage()) { + if (chatListMessageKnown()) { + if (const auto last = chatListMessage()) { if (last->history() == history) { - recountLastMessage(); + recountChatListMessage(); } } } @@ -133,7 +141,7 @@ void Feed::unregisterOne(not_null channel) { unreadCountChanged(delta, history->mute() ? delta : 0); } } else { - _parent->session().api().requestDialogEntry(this); + session().api().requestDialogEntry(this); } } if (visible && _channels.size() < 2) { @@ -144,14 +152,14 @@ void Feed::unregisterOne(not_null channel) { } else { history->updateChatListExistence(); } - _parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels); + _owner->notifyFeedUpdated(this, FeedUpdateFlag::Channels); } } -void Feed::updateLastMessage(not_null item) { - if (justUpdateLastMessage(item)) { - if (_lastMessage && *_lastMessage) { - setChatsListTimeId((*_lastMessage)->date()); +void Feed::updateChatListMessage(not_null item) { + if (justUpdateChatListMessage(item)) { + if (_chatListMessage && *_chatListMessage) { + setChatListTimeId((*_chatListMessage)->date()); } } } @@ -206,7 +214,7 @@ bool Feed::channelsLoaded() const { void Feed::setChannelsLoaded(bool loaded) { if (_channelsLoaded != loaded) { _channelsLoaded = loaded; - _parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels); + _owner->notifyFeedUpdated(this, FeedUpdateFlag::Channels); } } @@ -249,74 +257,72 @@ void Feed::changeChannelsList( // 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 oldLastMessage = base::take(_lastMessage); + const auto oldChatListMessage = base::take(_chatListMessage); for (const auto channel : add) { - _lastMessage = std::nullopt; + _chatListMessage = std::nullopt; channel->setFeed(this); } - _lastMessage = oldLastMessage; + _chatListMessage = oldChatListMessage; } -bool Feed::justUpdateLastMessage(not_null item) { - if (!_lastMessage) { +bool Feed::justUpdateChatListMessage(not_null item) { + if (!_chatListMessage) { return false; - } else if (*_lastMessage - && item->position() <= (*_lastMessage)->position()) { + } else if (*_chatListMessage + && item->position() <= (*_chatListMessage)->position()) { return false; } - _lastMessage = item; + _chatListMessage = item; return true; } void Feed::messageRemoved(not_null item) { - if (lastMessage() == item) { - recountLastMessage(); + if (chatListMessage() == item) { + recountChatListMessage(); } } void Feed::historyCleared(not_null history) { - if (const auto last = lastMessage()) { + if (const auto last = chatListMessage()) { if (last->history() == history) { messageRemoved(last); } } } -void Feed::recountLastMessage() { - _lastMessage = std::nullopt; +void Feed::requestChatListMessage() { + if (!chatListMessageKnown()) { + session().api().requestDialogEntry(this); + } +} + +void Feed::recountChatListMessage() { + _chatListMessage = std::nullopt; for (const auto history : _channels) { - if (!history->lastMessageKnown()) { - _parent->session().api().requestDialogEntry(this); + if (!history->chatListMessageKnown()) { + requestChatListMessage(); return; } } - setLastMessageFromChannels(); + setChatListMessageFromChannels(); } -void Feed::setLastMessageFromChannels() { - _lastMessage = nullptr; +void Feed::setChatListMessageFromChannels() { + _chatListMessage = nullptr; for (const auto history : _channels) { - if (const auto last = history->lastMessage()) { - justUpdateLastMessage(last); + if (const auto last = history->chatListMessage()) { + justUpdateChatListMessage(last); } } - updateChatsListDate(); + updateChatListDate(); } -void Feed::updateChatsListDate() { - if (_lastMessage && *_lastMessage) { - setChatsListTimeId((*_lastMessage)->date()); +void Feed::updateChatListDate() { + if (_chatListMessage && *_chatListMessage) { + setChatListTimeId((*_chatListMessage)->date()); } } -HistoryItem *Feed::lastMessage() const { - return _lastMessage ? *_lastMessage : nullptr; -} - -bool Feed::lastMessageKnown() const { - return !!_lastMessage; -} - int Feed::unreadCount() const { return _unreadCount ? *_unreadCount : 0; } @@ -341,17 +347,17 @@ bool Feed::unreadCountKnown() const { // addChannel(channelId.v); // } // -// _lastMessage = nullptr; +// _chatListMessage = nullptr; // if (const auto peerId = peerFromMTP(data.vpeer)) { // if (const auto channelId = peerToChannel(peerId)) { // addChannel(channelId); // const auto fullId = FullMsgId(channelId, data.vtop_message.v); // if (const auto item = App::histItemById(fullId)) { -// justUpdateLastMessage(item); +// justUpdateChatListMessage(item); // } // } // } -// updateChatsListDate(); +// updateChatListDate(); // // setUnreadCounts( // data.vunread_count.v, @@ -493,19 +499,23 @@ bool Feed::chatListMutedBadge() const { return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false; } -HistoryItem *Feed::chatsListItem() const { - return lastMessage(); +HistoryItem *Feed::chatListMessage() const { + return _chatListMessage ? *_chatListMessage : nullptr; } -const QString &Feed::chatsListName() const { +bool Feed::chatListMessageKnown() const { + return _chatListMessage.has_value(); +} + +const QString &Feed::chatListName() const { return _name; } -const base::flat_set &Feed::chatsListNameWords() const { +const base::flat_set &Feed::chatListNameWords() const { return _nameWords; } -const base::flat_set &Feed::chatsListFirstLetters() const { +const base::flat_set &Feed::chatListFirstLetters() const { return _nameFirstLetters; } diff --git a/Telegram/SourceFiles/data/data_feed.h b/Telegram/SourceFiles/data/data_feed.h index 8304dea629..5ead5e228e 100644 --- a/Telegram/SourceFiles/data/data_feed.h +++ b/Telegram/SourceFiles/data/data_feed.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_messages.h" class ChannelData; +class AuthSession; namespace Data { @@ -34,13 +35,18 @@ public: static constexpr auto kId = 1; static constexpr auto kChannelsLimit = 1000; - Feed(FeedId id, not_null parent); + Feed(not_null owner, FeedId id); + Feed(const Feed &) = delete; + Feed &operator=(const Feed &) = delete; + + Data::Session &owner() const; + AuthSession &session() const; FeedId id() const; void registerOne(not_null channel); void unregisterOne(not_null channel); - void updateLastMessage(not_null item); + void updateChatListMessage(not_null item); void messageRemoved(not_null item); void historyCleared(not_null history); @@ -54,8 +60,6 @@ public: MessagePosition unreadPosition() const; rpl::producer unreadPositionChanges() const; - HistoryItem *lastMessage() const; - bool lastMessageKnown() const; int unreadCount() const; bool unreadCountKnown() const; @@ -65,10 +69,12 @@ public: int chatListUnreadCount() const override; bool chatListUnreadMark() const override; bool chatListMutedBadge() const override; - HistoryItem *chatsListItem() const override; - const QString &chatsListName() const override; - const base::flat_set &chatsListNameWords() const override; - const base::flat_set &chatsListFirstLetters() const override; + HistoryItem *chatListMessage() const override; + bool chatListMessageKnown() const override; + void requestChatListMessage() override; + const QString &chatListName() const override; + const base::flat_set &chatListNameWords() const override; + const base::flat_set &chatListFirstLetters() const override; void changedInChatListHook(Dialogs::Mode list, bool added) override; void loadUserpic() override; @@ -86,10 +92,10 @@ public: private: void indexNameParts(); - void recountLastMessage(); - void setLastMessageFromChannels(); - bool justUpdateLastMessage(not_null item); - void updateChatsListDate(); + void recountChatListMessage(); + void setChatListMessageFromChannels(); + bool justUpdateChatListMessage(not_null item); + void updateChatListDate(); void changeChannelsList( const std::vector> &add, const std::vector> &remove); @@ -98,7 +104,7 @@ private: void updateUnreadCounts(PerformUpdate &&performUpdate); FeedId _id = 0; - not_null _parent; + not_null _owner; std::vector> _channels; bool _settingChannels = false; bool _channelsLoaded = false; @@ -106,7 +112,7 @@ private: QString _name; base::flat_set _nameWords; base::flat_set _nameFirstLetters; - std::optional _lastMessage; + std::optional _chatListMessage; rpl::variable _unreadPosition; std::optional _unreadCount; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index b9c04d8e76..ce165aa991 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -187,7 +187,7 @@ bool Media::canBeGrouped() const { return false; } -QString Media::chatsListText() const { +QString Media::chatListText() const { auto result = notificationText(); return result.isEmpty() ? QString() @@ -304,7 +304,7 @@ QString MediaPhoto::notificationText() const { //return WithCaptionNotificationText(lang(lng_in_dlg_album), _caption); } -QString MediaPhoto::chatsListText() const { +QString MediaPhoto::chatListText() const { return WithCaptionDialogsText( lang(lng_in_dlg_photo), parent()->originalText().text); @@ -539,9 +539,9 @@ Image *MediaFile::replyPreview() const { return _document->getReplyPreview(parent()->fullId()); } -QString MediaFile::chatsListText() const { +QString MediaFile::chatListText() const { if (const auto sticker = _document->sticker()) { - return Media::chatsListText(); + return Media::chatListText(); } const auto type = [&] { if (_document->isVideoMessage()) { @@ -861,7 +861,7 @@ LocationData *MediaLocation::location() const { return _location; } -QString MediaLocation::chatsListText() const { +QString MediaLocation::chatListText() const { return WithCaptionDialogsText(lang(lng_maps_point), _title); } @@ -1032,7 +1032,7 @@ Image *MediaWebPage::replyPreview() const { return nullptr; } -QString MediaWebPage::chatsListText() const { +QString MediaWebPage::chatListText() const { return notificationText(); } diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 806b8ddd1d..8d63dc60af 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -86,7 +86,7 @@ public: virtual Image *replyPreview() const; // Returns text with link-start and link-end commands for service-color highlighting. // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" - virtual QString chatsListText() const; + virtual QString chatListText() const; virtual QString notificationText() const = 0; virtual QString pinnedTextSubstring() const = 0; virtual TextWithEntities clipboardText() const = 0; @@ -136,7 +136,7 @@ public: bool canBeGrouped() const override; bool hasReplyPreview() const override; Image *replyPreview() const override; - QString chatsListText() const override; + QString chatListText() const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextWithEntities clipboardText() const override; @@ -171,7 +171,7 @@ public: bool canBeGrouped() const override; bool hasReplyPreview() const override; Image *replyPreview() const override; - QString chatsListText() const override; + QString chatListText() const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextWithEntities clipboardText() const override; @@ -233,7 +233,7 @@ public: std::unique_ptr clone(not_null parent) override; LocationData *location() const override; - QString chatsListText() const override; + QString chatListText() const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextWithEntities clipboardText() const override; @@ -296,7 +296,7 @@ public: bool hasReplyPreview() const override; Image *replyPreview() const override; - QString chatsListText() const override; + QString chatListText() const override; QString notificationText() const override; QString pinnedTextSubstring() const override; TextWithEntities clipboardText() const override; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index ef1938416f..e1919bc9f8 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2759,7 +2759,7 @@ not_null Session::feed(FeedId id) { } const auto [it, ok] = _feeds.emplace( id, - std::make_unique(id, this)); + std::make_unique(this, id)); return it->second.get(); } @@ -3038,9 +3038,7 @@ void Session::setProxyPromoted(PeerData *promoted) { if (_proxyPromoted) { const auto history = this->history(_proxyPromoted); history->cacheProxyPromoted(true); - if (!history->lastMessageKnown()) { - _session->api().requestDialogEntry(history); - } + history->requestChatListMessage(); Notify::peerUpdatedDelayed( _proxyPromoted, Notify::PeerUpdate::Flag::ChannelPromotedChanged); diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index ea486ee991..078cd637d6 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -102,7 +102,7 @@ void Entry::setChatListExistence(bool exists) { } TimeId Entry::adjustChatListTimeId() const { - return chatsListTimeId(); + return chatListTimeId(); } void Entry::changedInChatListHook(Dialogs::Mode list, bool added) { @@ -135,13 +135,13 @@ PositionChange Entry::adjustByPosInChatList( return { movedFrom, movedTo }; } -void Entry::setChatsListTimeId(TimeId date) { - if (_lastMessageTimeId && _lastMessageTimeId >= date) { +void Entry::setChatListTimeId(TimeId date) { + if (_timeId && _timeId >= date) { if (!inChatList(Dialogs::Mode::All)) { return; } } - _lastMessageTimeId = date; + _timeId = date; updateChatListSortPosition(); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index ac4c6714cb..c885ff33ce 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -65,7 +65,7 @@ public: return _sortKeyInChatList; } void updateChatListSortPosition(); - void setChatsListTimeId(TimeId date); + void setChatListTimeId(TimeId date); virtual void updateChatListExistence(); bool needUpdateInChatList() const; @@ -74,10 +74,12 @@ public: virtual int chatListUnreadCount() const = 0; virtual bool chatListUnreadMark() const = 0; virtual bool chatListMutedBadge() const = 0; - virtual HistoryItem *chatsListItem() const = 0; - virtual const QString &chatsListName() const = 0; - virtual const base::flat_set &chatsListNameWords() const = 0; - virtual const base::flat_set &chatsListFirstLetters() const = 0; + virtual HistoryItem *chatListMessage() const = 0; + virtual bool chatListMessageKnown() const = 0; + virtual void requestChatListMessage() = 0; + virtual const QString &chatListName() const = 0; + virtual const base::flat_set &chatListNameWords() const = 0; + virtual const base::flat_set &chatListFirstLetters() const = 0; virtual void loadUserpic() = 0; virtual void paintUserpic( @@ -94,8 +96,8 @@ public: paintUserpic(p, rtl() ? (w - x - size) : x, y, size); } - TimeId chatsListTimeId() const { - return _lastMessageTimeId; + TimeId chatListTimeId() const { + return _timeId; } virtual ~Entry() = default; @@ -118,7 +120,7 @@ private: uint64 _sortKeyInChatList = 0; int _pinnedIndex = 0; bool _isProxyPromoted = false; - TimeId _lastMessageTimeId = 0; + TimeId _timeId = 0; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp index b6a85cb8a9..616d9a5346 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_indexed_list.cpp @@ -23,7 +23,7 @@ RowsByLetter IndexedList::addToEnd(Key key) { RowsByLetter result; if (!_list.contains(key)) { result.emplace(0, _list.addToEnd(key)); - for (auto ch : key.entry()->chatsListFirstLetters()) { + for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = _index.find(ch); if (j == _index.cend()) { j = _index.emplace( @@ -42,7 +42,7 @@ Row *IndexedList::addByName(Key key) { } Row *result = _list.addByName(key); - for (auto ch : key.entry()->chatsListFirstLetters()) { + for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = _index.find(ch); if (j == _index.cend()) { j = _index.emplace( @@ -68,7 +68,7 @@ void IndexedList::adjustByPos(const RowsByLetter &links) { void IndexedList::moveToTop(Key key) { if (_list.moveToTop(key)) { - for (auto ch : key.entry()->chatsListFirstLetters()) { + for (const auto ch : key.entry()->chatListFirstLetters()) { if (auto it = _index.find(ch); it != _index.cend()) { it->second->moveToTop(key); } @@ -123,7 +123,7 @@ void IndexedList::adjustByName( auto toRemove = oldLetters; auto toAdd = base::flat_set(); - for (auto ch : key.entry()->chatsListFirstLetters()) { + for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = toRemove.find(ch); if (j == toRemove.cend()) { toAdd.insert(ch); @@ -162,7 +162,7 @@ void IndexedList::adjustNames( auto toRemove = oldLetters; auto toAdd = base::flat_set(); - for (auto ch : key.entry()->chatsListFirstLetters()) { + for (const auto ch : key.entry()->chatListFirstLetters()) { auto j = toRemove.find(ch); if (j == toRemove.cend()) { toAdd.insert(ch); @@ -194,7 +194,7 @@ void IndexedList::adjustNames( void IndexedList::del(Key key, Row *replacedBy) { if (_list.del(key, replacedBy)) { - for (auto ch : key.entry()->chatsListFirstLetters()) { + for (const auto ch : key.entry()->chatListFirstLetters()) { if (auto it = _index.find(ch); it != _index.cend()) { it->second->del(key, replacedBy); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 5dc2df70c0..76095b323b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1702,7 +1702,7 @@ void DialogsInner::applyFilterUpdate(QString newFilter, bool force) { + (toFilterContacts ? toFilterContacts->size() : 0)); if (toFilter) { for (const auto row : *toFilter) { - const auto &nameWords = row->entry()->chatsListNameWords(); + const auto &nameWords = row->entry()->chatListNameWords(); auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb; for (fi = fb; fi != fe; ++fi) { auto filterWord = *fi; @@ -1722,7 +1722,7 @@ void DialogsInner::applyFilterUpdate(QString newFilter, bool force) { } if (toFilterContacts) { for (const auto row : *toFilterContacts) { - const auto &nameWords = row->entry()->chatsListNameWords(); + const auto &nameWords = row->entry()->chatListNameWords(); auto nb = nameWords.cbegin(), ne = nameWords.cend(), ni = nb; for (fi = fb; fi != fe; ++fi) { auto filterWord = *fi; @@ -1875,7 +1875,7 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) { history->applyDialog(dialog); if (!history->useProxyPromotion() && !history->isPinnedDialog()) { - const auto date = history->chatsListTimeId(); + const auto date = history->chatListTimeId(); if (date != 0) { addSavedPeersAfter(ParseDateTime(date)); } @@ -1898,7 +1898,7 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) { // feed->applyDialog(dialog); // // if (!feed->useProxyPromotion() && !feed->isPinnedDialog()) { -// const auto date = feed->chatsListDate(); +// const auto date = feed->chatListDate(); // if (!date.isNull()) { // addSavedPeersAfter(date); // } @@ -1913,7 +1913,7 @@ void DialogsInner::addSavedPeersAfter(const QDateTime &date) { saved.remove(lastDate, lastPeer); const auto history = App::history(lastPeer); - history->setChatsListTimeId(ServerTimeFromParsed(lastDate)); + history->setChatListTimeId(ServerTimeFromParsed(lastDate)); _contactsNoDialogs->del(history); } } @@ -2233,7 +2233,7 @@ void DialogsInner::refreshSearchInChatLabel() { } return peer->name; } else if (const auto feed = _searchInChat.feed()) { - return feed->chatsListName(); + return feed->chatListName(); } return QString(); }(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 227248cca4..fffe33f0ed 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -377,7 +377,7 @@ void paintRow( from->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } else { p.setFont(st::msgNameFont); - auto text = entry->chatsListName(); // TODO feed name with emoji + auto text = entry->chatListName(); // TODO feed name with emoji auto textWidth = st::msgNameFont->width(text); if (textWidth > rectForName.width()) { text = st::msgNameFont->elided(text, rectForName.width()); @@ -528,7 +528,7 @@ void RowPainter::paint( const auto unreadCount = entry->chatListUnreadCount(); const auto unreadMark = entry->chatListUnreadMark(); const auto unreadMuted = entry->chatListMutedBadge(); - const auto item = entry->chatsListItem(); + const auto item = entry->chatListMessage(); const auto cloudDraft = [&]() -> const Data::Draft*{ if (history && (!item || (!unreadCount && !unreadMark))) { // Draw item, if there are unread messages. diff --git a/Telegram/SourceFiles/dialogs/dialogs_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_list.cpp index c95b6f8fad..73c214eb35 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_list.cpp @@ -114,15 +114,15 @@ Row *List::adjustByName(Key key) { if (i == _rowByKey.cend()) return nullptr; const auto row = i->second; - const auto name = key.entry()->chatsListName(); + const auto name = key.entry()->chatListName(); auto change = row; while (change->_prev - && change->_prev->entry()->chatsListName().compare(name, Qt::CaseInsensitive) < 0) { + && change->_prev->entry()->chatListName().compare(name, Qt::CaseInsensitive) < 0) { change = change->_prev; } if (!insertBefore(row, change)) { while (change->_next != _end - && change->_next->entry()->chatsListName().compare(name, Qt::CaseInsensitive) < 0) { + && change->_next->entry()->chatListName().compare(name, Qt::CaseInsensitive) < 0) { change = change->_next; } insertAfter(row, change); @@ -137,14 +137,14 @@ Row *List::addByName(Key key) { const auto row = addToEnd(key); auto change = row; - const auto name = key.entry()->chatsListName(); + const auto name = key.entry()->chatListName(); while (change->_prev - && change->_prev->entry()->chatsListName().compare(name, Qt::CaseInsensitive) > 0) { + && change->_prev->entry()->chatListName().compare(name, Qt::CaseInsensitive) > 0) { change = change->_prev; } if (!insertBefore(row, change)) { while (change->_next != _end - && change->_next->entry()->chatsListName().compare(name, Qt::CaseInsensitive) < 0) { + && change->_next->entry()->chatListName().compare(name, Qt::CaseInsensitive) < 0) { change = change->_next; } insertAfter(row, change); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 5026d9d993..d51e767396 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -716,7 +716,7 @@ int InnerWidget::resizeGetHeight(int newWidth) { const auto resizeAllItems = (_itemsWidth != newWidth); auto newHeight = 0; - for (auto &item : base::reversed(_items)) { + for (const auto &item : ranges::view::reverse(_items)) { item->setY(newHeight); if (item->pendingResize() || resizeAllItems) { newHeight += item->resizeGetHeight(newWidth); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index b539e8e498..75b0b37d45 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -60,9 +60,9 @@ constexpr auto kSkipCloudDraftsFor = TimeId(3); } // namespace -History::History(not_null owner, const PeerId &peerId) +History::History(not_null owner, PeerId peerId) : Entry(this) -, peer(App::peer(peerId)) +, peer(owner->peer(peerId)) , cloudDraftTextCache(st::dialogsTextWidthMin) , _owner(owner) , _mute(_owner->notifyIsMuted(peer)) @@ -138,15 +138,31 @@ void History::itemRemoved(not_null item) { setLastMessage(last); } } - if (const auto channel = peer->asChannel()) { - if (const auto feed = channel->feed()) { - // Must be after history->lastMessage() is updated. - // Otherwise feed last message will be this value again. - feed->messageRemoved(item); + } + checkChatListMessageRemoved(item); + itemVanished(item); + if (const auto chat = peer->asChat()) { + if (const auto to = chat->getMigrateToChannel()) { + if (const auto history = owner().historyLoaded(to)) { + history->checkChatListMessageRemoved(item); } } } - itemVanished(item); +} + +void History::checkChatListMessageRemoved(not_null item) { + if (chatListMessage() != item) { + return; + } + _chatListMessage = std::nullopt; + refreshChatListMessage(); + if (const auto channel = peer->asChannel()) { + if (const auto feed = channel->feed()) { + // Must be after history->chatListMessage() is updated. + // Otherwise feed last message will be this value again. + feed->messageRemoved(item); + } + } } void History::itemVanished(not_null item) { @@ -1130,7 +1146,7 @@ void History::newItemAdded(not_null item) { notifies.push_back(item); App::main()->newUnreadMsg(this, item); } - } else if (!item->isGroupMigrate() || !peer->isMegagroup()) { + } else { inboxRead(item); } } @@ -1285,7 +1301,7 @@ void History::addItemsToLists( // lastParticipants are displayed in Profile as members list. markupSenders = &peer->asChannel()->mgInfo->markupSenders; } - for (const auto item : base::reversed(items)) { + for (const auto item : ranges::view::reverse(items)) { item->addToUnreadMentions(UnreadMentionType::Existing); if (item->from()->id) { if (lastAuthors) { // chats @@ -1465,7 +1481,7 @@ void History::inboxRead(not_null wasRead) { void History::outboxRead(MsgId upTo) { setOutboxReadTill(upTo); - if (const auto last = lastMessage()) { + if (const auto last = chatListMessage()) { if (last->out() && IsServerMsgId(last->id) && last->id <= upTo) { if (const auto main = App::main()) { main->repaintDialogRow({ this, last->fullId() }); @@ -1701,7 +1717,7 @@ std::shared_ptr History::adminLogIdManager() { } TimeId History::adjustChatListTimeId() const { - const auto result = chatsListTimeId(); + const auto result = chatListTimeId(); if (const auto draft = cloudDraft()) { if (!Data::draftIsNull(draft) && !session().supportMode()) { return std::max(result, draft->date); @@ -1889,19 +1905,23 @@ bool History::chatListMutedBadge() const { return mute(); } -HistoryItem *History::chatsListItem() const { - return lastMessage(); +HistoryItem *History::chatListMessage() const { + return _chatListMessage.value_or(nullptr); } -const QString &History::chatsListName() const { +bool History::chatListMessageKnown() const { + return _chatListMessage.has_value(); +} + +const QString &History::chatListName() const { return peer->name; } -const base::flat_set &History::chatsListNameWords() const { +const base::flat_set &History::chatListNameWords() const { return peer->nameWords(); } -const base::flat_set &History::chatsListFirstLetters() const { +const base::flat_set &History::chatListFirstLetters() const { return peer->nameFirstLetters(); } @@ -2050,40 +2070,194 @@ void History::markFullyLoaded() { } void History::setLastMessage(HistoryItem *item) { - if (item) { - if (_lastMessage) { - if (!*_lastMessage) { - Local::removeSavedPeer(peer); - } else if (!IsServerMsgId((*_lastMessage)->id) - && (*_lastMessage)->date() > item->date()) { - return; - } - } - _lastMessage = item; - if (const auto feed = peer->feed()) { - feed->updateLastMessage(item); - } - setChatsListTimeId(item->date()); - } else if (!_lastMessage || *_lastMessage) { - _lastMessage = nullptr; - updateChatListEntry(); + if (_lastMessage && *_lastMessage == item) { + return; + } + _lastMessage = item; + _chatListMessage = std::nullopt; + if (!peer->migrateTo()) { + // We don't want to request last message for all deactivated chats. + // This is a heavy request for them, because we need to get last + // two items by messages.getHistory to skip the migration message. + requestChatListMessage(); } } +void History::refreshChatListMessage() { + const auto known = chatListMessageKnown(); + setChatListMessageFromLast(); + if (known && !_chatListMessage) { + requestChatListMessage(); + } +} + +void History::setChatListMessage(HistoryItem *item) { + if (_chatListMessage && *_chatListMessage == item) { + return; + } + if (item) { + if (_chatListMessage) { + if (!*_chatListMessage) { + Local::removeSavedPeer(peer); + } else if (!IsServerMsgId((*_chatListMessage)->id) + && (*_chatListMessage)->date() > item->date()) { + return; + } + } + _chatListMessage = item; + setChatListTimeId(item->date()); + } else if (!_chatListMessage || *_chatListMessage) { + _chatListMessage = nullptr; + updateChatListEntry(); + } + if (const auto to = peer->migrateTo()) { + if (const auto history = owner().historyLoaded(to)) { + if (!history->chatListMessageKnown()) { + history->requestChatListMessage(); + } + } + } +} + +auto History::computeChatListMessageFromLast() const +-> std::optional { + if (!_lastMessage) { + return _lastMessage; + } + + // In migrated groups we want to skip essential message + // about migration in the chats list and display the last + // non-migration message from the original legacy group. + const auto last = lastMessage(); + if (!last || !last->isGroupEssential() || !last->isEmpty()) { + return _lastMessage; + } + if (const auto chat = peer->asChat()) { + // In chats we try to take the item before the 'last', which + // is the empty-displayed migration message. + if (!loadedAtBottom()) { + // We don't know the tail of the history. + return std::nullopt; + } + const auto before = [&]() -> HistoryItem* { + for (const auto &block : ranges::view::reverse(blocks)) { + const auto &messages = block->messages; + for (const auto &item : ranges::view::reverse(messages)) { + if (item->data() != last) { + return item->data(); + } + } + } + return nullptr; + }(); + if (before) { + // We found a message that is not the migration one. + return before; + } else if (loadedAtTop()) { + // No other messages in this history. + return _lastMessage; + } + return std::nullopt; + } else if (const auto from = migrateFrom()) { + // In megagroups we just try to use + // the message from the original group. + return from->chatListMessageKnown() + ? std::make_optional(from->chatListMessage()) + : std::nullopt; + } + return _lastMessage; +} + +void History::setChatListMessageFromLast() { + if (const auto good = computeChatListMessageFromLast()) { + setChatListMessage(*good); + } else { + _chatListMessage = std::nullopt; + } +} + +void History::requestChatListMessage() { + if (!lastMessageKnown()) { + session().api().requestDialogEntry(this); + return; + } else if (chatListMessageKnown()) { + return; + } + setChatListMessageFromLast(); + if (!chatListMessageKnown()) { + setFakeChatListMessage(); + } +} + +void History::setFakeChatListMessage() { + if (const auto chat = peer->asChat()) { + // In chats we try to take the item before the 'last', which + // is the empty-displayed migration message. + session().api().requestFakeChatListMessage(this); + } else if (const auto from = migrateFrom()) { + // In megagroups we just try to use + // the message from the original group. + from->requestChatListMessage(); + } +} + +void History::setFakeChatListMessageFrom(const MTPmessages_Messages &data) { + if (!lastMessageKnown()) { + requestChatListMessage(); + return; + } + const auto finalize = gsl::finally([&] { + // Make sure that we have chatListMessage when we get out of here. + if (!chatListMessageKnown()) { + setChatListMessage(lastMessage()); + } + }); + const auto last = lastMessage(); + if (!last || !last->isGroupEssential() || !last->isEmpty()) { + // Last message is good enough. + return; + } + const auto other = data.match([&]( + const MTPDmessages_messagesNotModified &) { + return static_cast(nullptr); + }, [&](const auto &data) { + for (const auto &message : data.vmessages.v) { + const auto id = message.match([](const auto &data) { + return data.vid.v; + }); + if (id != last->id) { + return &message; + } + } + return static_cast(nullptr); + }); + if (!other) { + // Other (non equal to the last one) message not found. + return; + } + const auto item = owner().addNewMessage(*other, NewMessageExisting); + if (!item || (item->isGroupEssential() && item->isEmpty())) { + // Not better than the last one. + return; + } + setChatListMessage(item); +} + HistoryItem *History::lastMessage() const { - return _lastMessage ? (*_lastMessage) : nullptr; + return _lastMessage.value_or(nullptr); } bool History::lastMessageKnown() const { - return !!_lastMessage; + return _lastMessage.has_value(); } void History::updateChatListExistence() { Entry::updateChatListExistence(); - if (!lastMessageKnown() || !unreadCountKnown()) { - if (const auto channel = peer->asChannel()) { - if (!channel->feed()) { - // After ungrouping from a feed we need to load dialog. + if (const auto channel = peer->asChannel()) { + if (!channel->feed()) { + // After ungrouping from a feed we need to load dialog. + requestChatListMessage(); + if (!unreadCountKnown()) { session().api().requestDialogEntry(this); } } @@ -2164,6 +2338,51 @@ void History::applyDialog(const MTPDdialog &data) { } } +void History::dialogEntryApplied() { + if (!lastMessageKnown()) { + setLastMessage(nullptr); + } + if (!chatListMessageKnown()) { + requestChatListMessage(); + return; + } + if (!chatListMessage()) { + if (const auto chat = peer->asChat()) { + if (!chat->haveLeft()) { + Local::addSavedPeer( + peer, + ParseDateTime(chatListTimeId())); + } + } else if (const auto channel = peer->asChannel()) { + const auto inviter = channel->inviter; + if (inviter != 0 && channel->amIn()) { + if (const auto from = App::userLoaded(inviter)) { + unloadBlocks(); + addNewerSlice(QVector()); + insertJoinedMessage(true); + } + } + } else { + App::main()->deleteConversation(peer, false); + } + return; + } + + if (chatListTimeId() != 0 && loadedAtBottom()) { + if (const auto channel = peer->asChannel()) { + const auto inviter = channel->inviter; + if (inviter != 0 + && chatListTimeId() <= channel->inviteDate + && channel->amIn()) { + if (const auto from = App::userLoaded(inviter)) { + insertJoinedMessage(true); + } + } + } + } + updateChatListExistence(); +} + bool History::clearUnreadOnClientSide() const { if (!session().supportMode()) { return false; @@ -2253,8 +2472,8 @@ MsgId History::minMsgId() const { } MsgId History::maxMsgId() const { - for (const auto &block : base::reversed(blocks)) { - for (const auto &message : base::reversed(block->messages)) { + for (const auto &block : ranges::view::reverse(blocks)) { + for (const auto &message : ranges::view::reverse(block->messages)) { const auto item = message->data(); if (IsServerMsgId(item->id)) { return item->id; @@ -2278,8 +2497,8 @@ HistoryItem *History::lastSentMessage() const { if (!loadedAtBottom()) { return nullptr; } - for (const auto &block : base::reversed(blocks)) { - for (const auto &message : base::reversed(block->messages)) { + for (const auto &block : ranges::view::reverse(blocks)) { + for (const auto &message : ranges::view::reverse(block->messages)) { const auto item = message->data(); // Skip if message is video message or sticker. if (const auto media = item->media()) { @@ -2336,7 +2555,7 @@ bool History::isMegagroup() const { } not_null History::migrateToOrMe() const { - if (auto to = peer->migrateTo()) { + if (const auto to = peer->migrateTo()) { return App::history(to); } // We could get it by App::history(peer), but we optimize. @@ -2344,7 +2563,7 @@ not_null History::migrateToOrMe() const { } History *History::migrateFrom() const { - if (auto from = peer->migrateFrom()) { + if (const auto from = peer->migrateFrom()) { return App::history(from); } return nullptr; @@ -2423,7 +2642,7 @@ HistoryService *History::insertJoinedMessage(bool unread) { // Due to a server bug sometimes inviteDate is less (before) than the // first message in the megagroup (message about migration), let us // ignore that and think, that the inviteDate is always greater-or-equal. - if (item->isGroupMigrate() + if ((item->id == 1) && peer->isMegagroup() && peer->migrateFrom()) { peer->asChannel()->mgInfo->joinedMessageFound = true; @@ -2437,7 +2656,7 @@ HistoryService *History::insertJoinedMessage(bool unread) { inviter, flags); addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex); - const auto lastDate = chatsListTimeId(); + const auto lastDate = chatListTimeId(); if (!lastDate || inviteDate >= lastDate) { setLastMessage(_joinedMessage); if (unread) { @@ -2508,9 +2727,29 @@ bool History::isEmpty() const { } bool History::isDisplayedEmpty() const { - return isEmpty() || ((blocks.size() == 1) - && blocks.front()->messages.size() == 1 - && blocks.front()->messages.front()->data()->isEmpty()); + return findFirstNonEmpty() == nullptr; +} + +auto History::findFirstNonEmpty() const -> Element* { + for (const auto &block : blocks) { + for (const auto &element : block->messages) { + if (!element->data()->isEmpty()) { + return element.get(); + } + } + } + return nullptr; +} + +auto History::findLastNonEmpty() const -> Element* { + for (const auto &block : ranges::view::reverse(blocks)) { + for (const auto &element : ranges::view::reverse(block->messages)) { + if (!element->data()->isEmpty()) { + return element.get(); + } + } + } + return nullptr; } bool History::hasOrphanMediaGroupPart() const { @@ -2624,9 +2863,7 @@ void History::clearUpTill(MsgId availableMinId) { item->destroy(); } while (!isEmpty()); - if (!lastMessageKnown()) { - session().api().requestDialogEntry(this); - } + requestChatListMessage(); _owner->sendHistoryChangeNotifications(); } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index d748d79fe7..7eedaede2b 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -58,7 +58,7 @@ class History final : public Dialogs::Entry { public: using Element = HistoryView::Element; - History(not_null owner, const PeerId &peerId); + History(not_null owner, PeerId peerId); History(const History &) = delete; History &operator=(const History &) = delete; @@ -77,6 +77,8 @@ public: bool isEmpty() const; bool isDisplayedEmpty() const; + Element *findFirstNonEmpty() const; + Element *findLastNonEmpty() const; bool hasOrphanMediaGroupPart() const; bool removeOrphanMediaGroupPart(); QVector collectMessagesFromUserToDelete( @@ -192,6 +194,7 @@ public: int unreadCount, MsgId maxInboxRead, MsgId maxOutboxRead); + void dialogEntryApplied(); MsgId minMsgId() const; MsgId maxMsgId() const; @@ -283,10 +286,12 @@ public: int chatListUnreadCount() const override; bool chatListUnreadMark() const override; bool chatListMutedBadge() const override; - HistoryItem *chatsListItem() const override; - const QString &chatsListName() const override; - const base::flat_set &chatsListNameWords() const override; - const base::flat_set &chatsListFirstLetters() const override; + HistoryItem *chatListMessage() const override; + bool chatListMessageKnown() const override; + void requestChatListMessage() override; + const QString &chatListName() const override; + const base::flat_set &chatListNameWords() const override; + const base::flat_set &chatListFirstLetters() const override; void loadUserpic() override; void paintUserpic( Painter &p, @@ -294,6 +299,9 @@ public: int y, int size) const override; + void setFakeChatListMessageFrom(const MTPmessages_Messages &data); + void checkChatListMessageRemoved(not_null item); + void forgetScrollState() { scrollTopItem = nullptr; } @@ -411,6 +419,12 @@ private: void checkLastMessage(); void setLastMessage(HistoryItem *item); + void refreshChatListMessage(); + void setChatListMessage(HistoryItem *item); + std::optional computeChatListMessageFromLast() const; + void setChatListMessageFromLast(); + void setFakeChatListMessage(); + // Add all items to the unread mentions if we were not loaded at bottom and now are. void checkAddAllToUnreadMentions(); @@ -449,6 +463,12 @@ private: std::optional _unreadMentionsCount; base::flat_set _unreadMentions; std::optional _lastMessage; + + // This almost always is equal to _lastMessage. The only difference is + // for a group that migrated to a supergroup. Then _lastMessage can + // be a migrate message, but _chatListMessage should be the one before. + std::optional _chatListMessage; + bool _unreadMark = false; // A pointer to the block that is currently being built. diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index e1646100f3..c5e852cf52 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -436,10 +436,6 @@ void HistoryInner::enumerateDates(Method method) { if (itemtop > _visibleAreaTop) { // Previous item (from the _migrated history) is drawing date now. return false; - } else if (item == _history->blocks.front()->messages.front()->data() && item->isGroupMigrate() - && _migrated->blocks.back()->messages.back()->data()->isGroupMigrate()) { - // This item is completely invisible and should be completely ignored. - return false; } } @@ -1954,17 +1950,19 @@ void HistoryInner::recountHistoryGeometry() { _migrated->resizeToWidth(_contentWidth); } - // with migrated history we perhaps do not need to display first _history message - // (if last _migrated message and first _history message are both isGroupMigrate) - // or at least we don't need to display first _history date (just skip it by height) + // With migrated history we perhaps do not need to display + // the first _history message date (just skip it by height). _historySkipHeight = 0; - if (_migrated) { - if (!_migrated->isEmpty() && !_history->isEmpty() && _migrated->loadedAtBottom() && _history->loadedAtTop()) { - if (_migrated->blocks.back()->messages.back()->dateTime().date() == _history->blocks.front()->messages.front()->dateTime().date()) { - if (_migrated->blocks.back()->messages.back()->data()->isGroupMigrate() && _history->blocks.front()->messages.front()->data()->isGroupMigrate()) { - _historySkipHeight += _history->blocks.front()->messages.front()->height(); - } else if (_migrated->height() > _history->blocks.front()->messages.front()->displayedDateHeight()) { - _historySkipHeight += _history->blocks.front()->messages.front()->displayedDateHeight(); + if (_migrated + && _migrated->loadedAtBottom() + && _history->loadedAtTop()) { + if (const auto first = _history->findFirstNonEmpty()) { + if (const auto last = _migrated->findLastNonEmpty()) { + if (first->dateTime().date() == last->dateTime().date()) { + const auto dateHeight = first->displayedDateHeight(); + if (_migrated->height() > dateHeight) { + _historySkipHeight += dateHeight; + } } } } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 6f4b73a120..6bb22673b9 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -298,9 +298,8 @@ private: int _contentWidth = 0; int _historyPaddingTop = 0; - // with migrated history we perhaps do not need to display first _history message - // (if last _migrated message and first _history message are both isGroupMigrate) - // or at least we don't need to display first _history date (just skip it by height) + // With migrated history we perhaps do not need to display + // the first _history message date (just skip it by height). int _historySkipHeight = 0; std::unique_ptr _botAbout; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 6b17818a40..edfa2026e6 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -176,12 +176,12 @@ TimeId HistoryItem::date() const { void HistoryItem::finishEdition(int oldKeyboardTop) { _history->owner().requestItemViewRefresh(this); - invalidateChatsListEntry(); + invalidateChatListEntry(); if (const auto group = _history->owner().groups().find(this)) { const auto leader = group->items.back(); if (leader != this) { _history->owner().requestItemViewRefresh(leader); - leader->invalidateChatsListEntry(); + leader->invalidateChatListEntry(); } } @@ -217,7 +217,7 @@ ReplyKeyboard *HistoryItem::inlineReplyKeyboard() { return nullptr; } -void HistoryItem::invalidateChatsListEntry() { +void HistoryItem::invalidateChatListEntry() { if (const auto main = App::main()) { // #TODO feeds search results main->repaintDialogRow({ history(), fullId() }); @@ -462,7 +462,7 @@ bool HistoryItem::canDelete() const { } auto channel = _history->peer->asChannel(); if (!channel) { - return !(_flags & MTPDmessage_ClientFlag::f_is_group_migrate); + return !(_flags & MTPDmessage_ClientFlag::f_is_group_essential); } if (id == 1) { @@ -706,7 +706,7 @@ QString HistoryItem::notificationText() const { QString HistoryItem::inDialogsText(DrawInDialog way) const { auto getText = [this]() { if (_media) { - return _media->chatsListText(); + return _media->chatListText(); } else if (!emptyText()) { return TextUtilities::Clean(_text.originalText()); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 4f4b8e2f3b..1cf8482e2a 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -134,8 +134,8 @@ public: bool hasTextLinks() const { return _flags & MTPDmessage_ClientFlag::f_has_text_links; } - bool isGroupMigrate() const { - return _flags & MTPDmessage_ClientFlag::f_is_group_migrate; + bool isGroupEssential() const { + return _flags & MTPDmessage_ClientFlag::f_is_group_essential; } bool hasViews() const { return _flags & MTPDmessage::Flag::f_views; @@ -294,7 +294,7 @@ protected: } HistoryMessageReplyMarkup *inlineReplyMarkup(); ReplyKeyboard *inlineReplyKeyboard(); - void invalidateChatsListEntry(); + void invalidateChatListEntry(); void setGroupId(MessageGroupId groupId); diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 14f7137e01..f5372a2069 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -36,7 +36,7 @@ constexpr auto kPinnedMessageTextLimit = 16; void HistoryService::setMessageByAction(const MTPmessageAction &action) { auto prepareChatAddUserText = [this](const MTPDmessageActionChatAddUser &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; auto &users = action.vusers.v; if (users.size() == 1) { auto u = App::user(peerFromUser(users[0])); @@ -72,14 +72,14 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareChatJoinedByLink = [this](const MTPDmessageActionChatJoinedByLink &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; result.links.push_back(fromLink()); result.text = lng_action_user_joined_by_link(lt_from, fromLinkText()); return result; }; auto prepareChatCreate = [this](const MTPDmessageActionChatCreate &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; result.links.push_back(fromLink()); result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle))); return result; @@ -97,7 +97,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareChatDeletePhoto = [this] { - auto result = PreparedText {}; + auto result = PreparedText{}; if (isPost()) { result.text = lang(lng_action_removed_photo_channel); } else { @@ -108,7 +108,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareChatDeleteUser = [this](const MTPDmessageActionChatDeleteUser &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; if (peerFromUser(action.vuser_id) == _from->id) { result.links.push_back(fromLink()); result.text = lng_action_user_left(lt_from, fromLinkText()); @@ -122,7 +122,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareChatEditPhoto = [this](const MTPDmessageActionChatEditPhoto &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; if (isPost()) { result.text = lang(lng_action_changed_photo_channel); } else { @@ -133,7 +133,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareChatEditTitle = [this](const MTPDmessageActionChatEditTitle &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; if (isPost()) { result.text = lng_action_changed_title_channel(lt_title, TextUtilities::Clean(qs(action.vtitle))); } else { @@ -144,7 +144,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareScreenshotTaken = [this] { - auto result = PreparedText {}; + auto result = PreparedText{}; if (out()) { result.text = lang(lng_action_you_took_screenshot); } else { @@ -155,7 +155,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { }; auto prepareCustomAction = [&](const MTPDmessageActionCustomAction &action) { - auto result = PreparedText {}; + auto result = PreparedText{}; result.text = qs(action.vmessage); return result; }; @@ -214,69 +214,98 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return result; }; - auto messageText = PreparedText {}; - - switch (action.type()) { - case mtpc_messageActionChatAddUser: messageText = prepareChatAddUserText(action.c_messageActionChatAddUser()); break; - case mtpc_messageActionChatJoinedByLink: messageText = prepareChatJoinedByLink(action.c_messageActionChatJoinedByLink()); break; - case mtpc_messageActionChatCreate: messageText = prepareChatCreate(action.c_messageActionChatCreate()); break; - case mtpc_messageActionChannelCreate: messageText = prepareChannelCreate(action.c_messageActionChannelCreate()); break; - case mtpc_messageActionHistoryClear: break; // Leave empty text. - case mtpc_messageActionChatDeletePhoto: messageText = prepareChatDeletePhoto(); break; - case mtpc_messageActionChatDeleteUser: messageText = prepareChatDeleteUser(action.c_messageActionChatDeleteUser()); break; - case mtpc_messageActionChatEditPhoto: messageText = prepareChatEditPhoto(action.c_messageActionChatEditPhoto()); break; - case mtpc_messageActionChatEditTitle: messageText = prepareChatEditTitle(action.c_messageActionChatEditTitle()); break; - case mtpc_messageActionChatMigrateTo: messageText.text = lang(lng_action_group_migrate); break; - case mtpc_messageActionChannelMigrateFrom: messageText.text = lang(lng_action_group_migrate); break; - case mtpc_messageActionPinMessage: messageText = preparePinnedText(); break; - case mtpc_messageActionGameScore: messageText = prepareGameScoreText(); break; - case mtpc_messageActionPhoneCall: Unexpected("PhoneCall type in HistoryService."); - case mtpc_messageActionPaymentSent: messageText = preparePaymentSentText(); break; - case mtpc_messageActionScreenshotTaken: messageText = prepareScreenshotTaken(); break; - case mtpc_messageActionCustomAction: messageText = prepareCustomAction(action.c_messageActionCustomAction()); break; - case mtpc_messageActionBotAllowed: messageText = prepareBotAllowed(action.c_messageActionBotAllowed()); break; - case mtpc_messageActionSecureValuesSent: messageText = prepareSecureValuesSent(action.c_messageActionSecureValuesSent()); break; - case mtpc_messageActionContactSignUp: messageText = prepareContactSignUp(); break; - default: messageText.text = lang(lng_message_empty); break; - } + const auto messageText = action.match([&]( + const MTPDmessageActionChatAddUser &data) { + return prepareChatAddUserText(data); + }, [&](const MTPDmessageActionChatJoinedByLink &data) { + return prepareChatJoinedByLink(data); + }, [&](const MTPDmessageActionChatCreate &data) { + return prepareChatCreate(data); + }, [](const MTPDmessageActionChatMigrateTo &) { + return PreparedText(); + }, [](const MTPDmessageActionChannelMigrateFrom &) { + return PreparedText(); + }, [](const MTPDmessageActionHistoryClear &) { + return PreparedText(); + }, [&](const MTPDmessageActionChannelCreate &data) { + return prepareChannelCreate(data); + }, [&](const MTPDmessageActionChatDeletePhoto &) { + return prepareChatDeletePhoto(); + }, [&](const MTPDmessageActionChatDeleteUser &data) { + return prepareChatDeleteUser(data); + }, [&](const MTPDmessageActionChatEditPhoto &data) { + return prepareChatEditPhoto(data); + }, [&](const MTPDmessageActionChatEditTitle &data) { + return prepareChatEditTitle(data); + }, [&](const MTPDmessageActionPinMessage &) { + return preparePinnedText(); + }, [&](const MTPDmessageActionGameScore &) { + return prepareGameScoreText(); + }, [&](const MTPDmessageActionPhoneCall &) -> PreparedText { + Unexpected("PhoneCall type in HistoryService."); + }, [&](const MTPDmessageActionPaymentSent &) { + return preparePaymentSentText(); + }, [&](const MTPDmessageActionScreenshotTaken &) { + return prepareScreenshotTaken(); + }, [&](const MTPDmessageActionCustomAction &data) { + return prepareCustomAction(data); + }, [&](const MTPDmessageActionBotAllowed &data) { + return prepareBotAllowed(data); + }, [&](const MTPDmessageActionSecureValuesSent &data) { + return prepareSecureValuesSent(data); + }, [&](const MTPDmessageActionContactSignUp &data) { + return prepareContactSignUp(); + }, [](const MTPDmessageActionPaymentSentMe &) { + LOG(("API Error: messageActionPaymentSentMe received.")); + return PreparedText{ lang(lng_message_empty) }; + }, [](const MTPDmessageActionSecureValuesSentMe &) { + LOG(("API Error: messageActionSecureValuesSentMe received.")); + return PreparedText{ lang(lng_message_empty) }; + }, [](const MTPDmessageActionEmpty &) { + return PreparedText{ lang(lng_message_empty) }; + }); setServiceText(messageText); // Additional information. - switch (action.type()) { - case mtpc_messageActionChatAddUser: { - if (auto channel = history()->peer->asMegagroup()) { - auto &users = action.c_messageActionChatAddUser().vusers; - for_const (auto &item, users.v) { - if (item.v == history()->session().userId()) { + applyAction(action); +} + +void HistoryService::applyAction(const MTPMessageAction &action) { + action.match([&](const MTPDmessageActionChatAddUser &data) { + if (const auto channel = history()->peer->asMegagroup()) { + const auto selfUserId = history()->session().userId(); + for (const auto &item : data.vusers.v) { + if (item.v == selfUserId) { channel->mgInfo->joinedMessageFound = true; break; } } } - } break; - - case mtpc_messageActionChatJoinedByLink: { - if (_from->isSelf() && history()->peer->isMegagroup()) { - history()->peer->asChannel()->mgInfo->joinedMessageFound = true; + }, [&](const MTPDmessageActionChatJoinedByLink &data) { + if (_from->isSelf()) { + if (const auto channel = history()->peer->asMegagroup()) { + channel->mgInfo->joinedMessageFound = true; + } } - } break; - - case mtpc_messageActionChatEditPhoto: { - auto &photo = action.c_messageActionChatEditPhoto().vphoto; - if (photo.type() == mtpc_photo) { + }, [&](const MTPDmessageActionChatEditPhoto &data) { + data.vphoto.match([&](const MTPDphoto &photo) { _media = std::make_unique( this, history()->peer, - history()->owner().photo(photo.c_photo())); - } - } break; - - case mtpc_messageActionChatMigrateTo: - case mtpc_messageActionChannelMigrateFrom: { - _flags |= MTPDmessage_ClientFlag::f_is_group_migrate; - } break; - } + history()->owner().photo(photo)); + }, [](const MTPDphotoEmpty &) { + }); + }, [&](const MTPDmessageActionChatCreate &) { + _flags |= MTPDmessage_ClientFlag::f_is_group_essential; + }, [&](const MTPDmessageActionChannelCreate &) { + _flags |= MTPDmessage_ClientFlag::f_is_group_essential; + }, [&](const MTPDmessageActionChatMigrateTo &) { + _flags |= MTPDmessage_ClientFlag::f_is_group_essential; + }, [&](const MTPDmessageActionChannelMigrateFrom &) { + _flags |= MTPDmessage_ClientFlag::f_is_group_essential; + }, [](const auto &) { + }); } void HistoryService::setSelfDestruct(HistoryServiceSelfDestruct::Type type, int ttlSeconds) { diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index cfc7bd49be..57a81456ab 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -137,7 +137,10 @@ private: void createFromMtp(const MTPDmessage &message); void createFromMtp(const MTPDmessageService &message); void setMessageByAction(const MTPmessageAction &action); - void setSelfDestruct(HistoryServiceSelfDestruct::Type type, int ttlSeconds); + void setSelfDestruct( + HistoryServiceSelfDestruct::Type type, + int ttlSeconds); + void applyAction(const MTPMessageAction &action); PreparedText preparePinnedText(); PreparedText prepareGameScoreText(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7931cb5c5d..f79e4e2578 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -730,26 +730,6 @@ void HistoryWidget::highlightMessage(MsgId universalMessageId) { _highlightStart = getms(); _highlightedMessageId = universalMessageId; _highlightTimer.callEach(AnimationTimerDelta); - - adjustHighlightedMessageToMigrated(); -} - -void HistoryWidget::adjustHighlightedMessageToMigrated() { - if (_history - && _highlightTimer.isActive() - && _highlightedMessageId > 0 - && _migrated - && !_migrated->isEmpty() - && _migrated->loadedAtBottom() - && _migrated->blocks.back()->messages.back()->data()->isGroupMigrate() - && _list->historyTop() != _list->historyDrawTop()) { - auto highlighted = App::histItemById( - _history->channelId(), - _highlightedMessageId); - if (highlighted && highlighted->isGroupMigrate()) { - _highlightedMessageId = -_migrated->blocks.back()->messages.back()->data()->id; - } - } } void HistoryWidget::checkNextHighlight() { @@ -4902,7 +4882,6 @@ void HistoryWidget::addMessagesToFront(PeerData *peer, const QVector _list->messagesReceived(peer, messages); if (!_firstLoadRequest) { updateHistoryGeometry(); - adjustHighlightedMessageToMigrated(); updateBotKeyboard(); } } @@ -5553,22 +5532,15 @@ void HistoryWidget::replyToMessage(not_null item) { return; } if (item->history() == _migrated) { - if (item->isGroupMigrate() - && !_history->isEmpty() - && _history->blocks.front()->messages.front()->data()->isGroupMigrate() - && _history != _migrated) { - replyToMessage(_history->blocks.front()->messages.front()->data()); + if (item->serviceMsg()) { + Ui::show(Box(lang(lng_reply_cant))); } else { - if (item->serviceMsg()) { - Ui::show(Box(lang(lng_reply_cant))); - } else { - const auto itemId = item->fullId(); - Ui::show(Box(lang(lng_reply_cant_forward), lang(lng_selected_forward), crl::guard(this, [=] { - App::main()->setForwardDraft( - _peer->id, - { 1, itemId }); - }))); - } + const auto itemId = item->fullId(); + Ui::show(Box(lang(lng_reply_cant_forward), lang(lng_selected_forward), crl::guard(this, [=] { + App::main()->setForwardDraft( + _peer->id, + { 1, itemId }); + }))); } return; } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 7f890b755b..62bf01413e 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -387,7 +387,6 @@ private: void supportShareContact(Support::Contact contact); void highlightMessage(MsgId universalMessageId); - void adjustHighlightedMessageToMigrated(); void checkNextHighlight(); void updateHighlightedMessage(); void clearHighlightMessages(); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 0d63e9fc78..73f17957d4 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -310,7 +310,7 @@ void TopBarWidget::paintTopBar(Painter &p, TimeMs ms) { p.setPen(st::dialogsNameFg); if (const auto feed = _activeChat.feed()) { - auto text = feed->chatsListName(); // TODO feed name emoji + auto text = feed->chatListName(); // TODO feed name emoji auto textWidth = st::historySavedFont->width(text); if (namewidth < textWidth) { text = st::historySavedFont->elided(text, namewidth); diff --git a/Telegram/SourceFiles/info/feed/info_feed_cover.cpp b/Telegram/SourceFiles/info/feed/info_feed_cover.cpp index f61a05fea0..fea34f5705 100644 --- a/Telegram/SourceFiles/info/feed/info_feed_cover.cpp +++ b/Telegram/SourceFiles/info/feed/info_feed_cover.cpp @@ -71,7 +71,7 @@ void Cover::initViewers() { } void Cover::refreshNameText() { - _name->setText(_feed->chatsListName()); + _name->setText(_feed->chatListName()); refreshNameGeometry(width()); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 03689a6192..ea1c282637 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -731,9 +731,7 @@ void MainWidget::cancelUploadLayer(not_null item) { if (const auto item = App::histItemById(itemId)) { const auto history = item->history(); item->destroy(); - if (!history->lastMessageKnown()) { - Auth().api().requestDialogEntry(history); - } + history->requestChatListMessage(); Auth().data().sendHistoryChangeNotifications(); } Auth().uploader().unpause(); @@ -831,7 +829,7 @@ void MainWidget::deleteHistoryPart(DeleteHistoryRequest request, const MTPmessag void MainWidget::deleteMessages( not_null peer, const QVector &ids, - bool forEveryone) { + bool revoke) { if (const auto channel = peer->asChannel()) { MTP::send( MTPchannels_DeleteMessages( @@ -839,13 +837,10 @@ void MainWidget::deleteMessages( MTP_vector(ids)), rpcDone(&MainWidget::messagesAffected, peer)); } else { - auto flags = MTPmessages_DeleteMessages::Flags(0); - if (forEveryone) { - flags |= MTPmessages_DeleteMessages::Flag::f_revoke; - } + using Flag = MTPmessages_DeleteMessages::Flag; MTP::send( MTPmessages_DeleteMessages( - MTP_flags(flags), + MTP_flags(revoke ? Flag::f_revoke : Flag(0)), MTP_vector(ids)), rpcDone(&MainWidget::messagesAffected, peer)); } @@ -1039,9 +1034,7 @@ void MainWidget::messagesAffected( } if (const auto history = App::historyLoaded(peer)) { - if (!history->lastMessageKnown()) { - Auth().api().requestDialogEntry(history); - } + history->requestChatListMessage(); } } @@ -4003,9 +3996,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (existing && !local->mainView()) { const auto history = local->history(); local->destroy(); - if (!history->lastMessageKnown()) { - Auth().api().requestDialogEntry(history); - } + history->requestChatListMessage(); } else { if (existing) { existing->destroy(); @@ -4477,15 +4468,15 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { Auth().api().requestSelfParticipant(channel); } if (const auto feed = channel->feed()) { - if (!feed->lastMessageKnown() - || !feed->unreadCountKnown()) { - Auth().api().requestDialogEntry(feed); + feed->requestChatListMessage(); + if (!feed->unreadCountKnown()) { + feed->session().api().requestDialogEntry(feed); } } else if (channel->amIn()) { const auto history = App::history(channel->id); - if (!history->lastMessageKnown() - || !history->unreadCountKnown()) { - Auth().api().requestDialogEntry(history); + history->requestChatListMessage(); + if (!history->unreadCountKnown()) { + history->session().api().requestDialogEntry(history); } } } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index d2eab3b9c7..97bb26d044 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -196,7 +196,7 @@ public: void deleteMessages( not_null peer, const QVector &ids, - bool forEveryone); + bool revoke); void deletedContact(UserData *user, const MTPcontacts_Link &result); void deleteConversation( not_null peer, diff --git a/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp b/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp index e9746708d5..2da934259c 100644 --- a/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp +++ b/Telegram/SourceFiles/mtproto/dedicated_file_loader.cpp @@ -409,22 +409,13 @@ void ResolveChannel( std::optional GetMessagesElement( const MTPmessages_Messages &list) { - const auto get = [](auto &&data) -> std::optional { + return list.match([&](const MTPDmessages_messagesNotModified &) { + return std::optional(std::nullopt); + }, [&](const auto &data) { return data.vmessages.v.isEmpty() ? std::nullopt - : base::make_optional(data.vmessages.v[0]); - }; - switch (list.type()) { - case mtpc_messages_messages: - return get(list.c_messages_messages()); - case mtpc_messages_messagesSlice: - return get(list.c_messages_messagesSlice()); - case mtpc_messages_channelMessages: - return get(list.c_messages_channelMessages()); - case mtpc_messages_messagesNotModified: - return std::nullopt; - default: Unexpected("Type of messages.Messages (GetMessagesElement)"); - } + : std::make_optional(data.vmessages.v[0]); + }); } void StartDedicatedLoader( diff --git a/Telegram/SourceFiles/mtproto/type_utils.h b/Telegram/SourceFiles/mtproto/type_utils.h index e53d908c53..4cff19df1e 100644 --- a/Telegram/SourceFiles/mtproto/type_utils.h +++ b/Telegram/SourceFiles/mtproto/type_utils.h @@ -38,8 +38,8 @@ enum class MTPDmessage_ClientFlag : uint32 { // message has links for "shared links" indexing f_has_text_links = (1U << 30), - // message is a group migrate (group -> supergroup) service message - f_is_group_migrate = (1U << 29), + // message is a group / channel create or migrate service message + f_is_group_essential = (1U << 29), //// message needs initDimensions() + resize() + paint() //f_pending_init_dimensions = (1U << 28), diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp index bf8d29fa64..b032ffadc6 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp @@ -643,7 +643,7 @@ bool PanelEditDocument::validate() { error = firsttop.y(); } auto first = QPointer(); - for (const auto [i, field] : base::reversed(_details)) { + for (const auto [i, field] : ranges::view::reverse(_details)) { const auto &row = _scheme.rows[i]; if (row.valueClass == Scheme::ValueClass::Additional && !_additionalShown) { diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 576a0fdeae..34d56d00a1 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -407,14 +407,14 @@ void Controller::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { return history->blocks.front()->messages.front()->dateTime().date(); } } - } else if (history->chatsListTimeId() != 0) { - return ParseDateTime(history->chatsListTimeId()).date(); + } else if (history->chatListTimeId() != 0) { + return ParseDateTime(history->chatListTimeId()).date(); } } else if (const auto feed = chat.feed()) { /*if (chatScrollPosition(feed)) { // #TODO feeds save position - } else */if (feed->chatsListTimeId() != 0) { - return ParseDateTime(feed->chatsListTimeId()).date(); + } else */if (feed->chatListTimeId() != 0) { + return ParseDateTime(feed->chatListTimeId()).date(); } } return QDate::currentDate(); @@ -424,12 +424,12 @@ void Controller::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { if (const auto channel = history->peer->migrateTo()) { history = App::historyLoaded(channel); } - if (history && history->chatsListTimeId() != 0) { - return ParseDateTime(history->chatsListTimeId()).date(); + if (history && history->chatListTimeId() != 0) { + return ParseDateTime(history->chatListTimeId()).date(); } } else if (const auto feed = chat.feed()) { - if (feed->chatsListTimeId() != 0) { - return ParseDateTime(feed->chatsListTimeId()).date(); + if (feed->chatListTimeId() != 0) { + return ParseDateTime(feed->chatListTimeId()).date(); } } return QDate::currentDate();