Remove supergroup migrate messages.

This commit is contained in:
John Preston 2019-01-15 15:57:45 +04:00
parent c552db04d7
commit 2a0b9a44dd
40 changed files with 695 additions and 481 deletions

View File

@ -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";

View File

@ -416,7 +416,7 @@ void ApiWrap::savePinnedOrder() {
const auto &order = _session->data().pinnedDialogsOrder();
auto peers = QVector<MTPInputDialogPeer>();
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*> 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<MTPMessage>());
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<Data::Feed*> 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<not_null<ChannelData*>>();
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*> 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<MTPMessage>(0),
MTP_vector<MTPChat>(0),
MTP_vector<MTPUser>(0)));
}).send();
}
void ApiWrap::requestFullPeer(not_null<PeerData*> 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<PeerData*> 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();

View File

@ -90,6 +90,7 @@ public:
// const std::vector<not_null<ChannelData*>> &channels);
void changeDialogUnreadMark(not_null<History*> history, bool unread);
//void changeDialogUnreadMark(not_null<Data::Feed*> feed, bool unread); // #feed
void requestFakeChatListMessage(not_null<History*> history);
void requestFullPeer(not_null<PeerData*> peer);
void requestPeer(not_null<PeerData*> peer);
@ -428,7 +429,6 @@ private:
QVector<MTPInputMessage> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
void historyDialogEntryApplied(not_null<History*> history);
void applyFeedDialogs(
not_null<Data::Feed*> feed,
const MTPmessages_Dialogs &dialogs);
@ -666,6 +666,7 @@ private:
base::flat_map<
not_null<History*>,
std::vector<Fn<void()>>> _dialogRequests;
base::flat_set<not_null<History*>> _fakeChatListRequests;
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;

View File

@ -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();
}
}

View File

@ -24,6 +24,12 @@ inline constexpr size_t array_size(const Type(&)[Size]) {
return Size;
}
template <typename Container, typename T>
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 <typename T>

View File

@ -605,25 +605,26 @@ void DeleteMessagesBox::deleteAndClear() {
_deleteConfirmedCallback();
}
QMap<PeerData*, QVector<MTPint>> idsByPeer;
base::flat_map<not_null<PeerData*>, QVector<MTPint>> 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();

View File

@ -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();

View File

@ -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(),

View File

@ -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;

View File

@ -36,12 +36,6 @@ inline constexpr D up_cast(T object) {
}
}
template <typename Container, typename T>
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<std::unique_ptr<T>>::find to work with pointers.
// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs
template <typename T>
@ -86,28 +80,6 @@ using set_of_unique_ptr = std::set<std::unique_ptr<T>, base::pointer_comparator<
template <typename T>
using set_of_shared_ptr = std::set<std::shared_ptr<T>, base::pointer_comparator<T>>;
// Thanks https://stackoverflow.com/a/28139075
template <typename Container>
struct reversion_wrapper {
Container &container;
};
template <typename Container>
auto begin(reversion_wrapper<Container> wrapper) {
return std::rbegin(wrapper.container);
}
template <typename Container>
auto end(reversion_wrapper<Container> wrapper) {
return std::rend(wrapper.container);
}
template <typename Container>
reversion_wrapper<Container> reversed(Container &&container) {
return { container };
}
template <typename Value, typename From, typename Till>
inline bool in_range(Value &&value, From &&from, Till &&till) {
return (value >= from) && (value < till);

View File

@ -31,14 +31,22 @@ namespace Data {
// data.vid.v));
//}
Feed::Feed(FeedId id, not_null<Data::Session*> parent)
Feed::Feed(not_null<Data::Session*> 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<ChannelData*> 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<ChannelData*> 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<ChannelData*> channel) {
} else {
history->updateChatListExistence();
}
_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
_owner->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
}
}
@ -117,13 +125,13 @@ void Feed::unregisterOne(not_null<ChannelData*> 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<ChannelData*> 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<ChannelData*> channel) {
} else {
history->updateChatListExistence();
}
_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
_owner->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
}
}
void Feed::updateLastMessage(not_null<HistoryItem*> item) {
if (justUpdateLastMessage(item)) {
if (_lastMessage && *_lastMessage) {
setChatsListTimeId((*_lastMessage)->date());
void Feed::updateChatListMessage(not_null<HistoryItem*> 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<HistoryItem*> item) {
if (!_lastMessage) {
bool Feed::justUpdateChatListMessage(not_null<HistoryItem*> 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<HistoryItem*> item) {
if (lastMessage() == item) {
recountLastMessage();
if (chatListMessage() == item) {
recountChatListMessage();
}
}
void Feed::historyCleared(not_null<History*> 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<QString> &Feed::chatsListNameWords() const {
const base::flat_set<QString> &Feed::chatListNameWords() const {
return _nameWords;
}
const base::flat_set<QChar> &Feed::chatsListFirstLetters() const {
const base::flat_set<QChar> &Feed::chatListFirstLetters() const {
return _nameFirstLetters;
}

View File

@ -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<Data::Session*> parent);
Feed(not_null<Data::Session*> 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<ChannelData*> channel);
void unregisterOne(not_null<ChannelData*> channel);
void updateLastMessage(not_null<HistoryItem*> item);
void updateChatListMessage(not_null<HistoryItem*> item);
void messageRemoved(not_null<HistoryItem*> item);
void historyCleared(not_null<History*> history);
@ -54,8 +60,6 @@ public:
MessagePosition unreadPosition() const;
rpl::producer<MessagePosition> 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<QString> &chatsListNameWords() const override;
const base::flat_set<QChar> &chatsListFirstLetters() 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;
@ -86,10 +92,10 @@ public:
private:
void indexNameParts();
void recountLastMessage();
void setLastMessageFromChannels();
bool justUpdateLastMessage(not_null<HistoryItem*> item);
void updateChatsListDate();
void recountChatListMessage();
void setChatListMessageFromChannels();
bool justUpdateChatListMessage(not_null<HistoryItem*> item);
void updateChatListDate();
void changeChannelsList(
const std::vector<not_null<ChannelData*>> &add,
const std::vector<not_null<ChannelData*>> &remove);
@ -98,7 +104,7 @@ private:
void updateUnreadCounts(PerformUpdate &&performUpdate);
FeedId _id = 0;
not_null<Data::Session*> _parent;
not_null<Data::Session*> _owner;
std::vector<not_null<History*>> _channels;
bool _settingChannels = false;
bool _channelsLoaded = false;
@ -106,7 +112,7 @@ private:
QString _name;
base::flat_set<QString> _nameWords;
base::flat_set<QChar> _nameFirstLetters;
std::optional<HistoryItem*> _lastMessage;
std::optional<HistoryItem*> _chatListMessage;
rpl::variable<MessagePosition> _unreadPosition;
std::optional<int> _unreadCount;

View File

@ -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();
}

View File

@ -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<Media> clone(not_null<HistoryItem*> 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;

View File

@ -2759,7 +2759,7 @@ not_null<Feed*> Session::feed(FeedId id) {
}
const auto [it, ok] = _feeds.emplace(
id,
std::make_unique<Feed>(id, this));
std::make_unique<Feed>(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);

View File

@ -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();
}

View File

@ -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<QString> &chatsListNameWords() const = 0;
virtual const base::flat_set<QChar> &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<QString> &chatListNameWords() const = 0;
virtual const base::flat_set<QChar> &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;
};

View File

@ -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<QChar>();
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<QChar>();
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);
}

View File

@ -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();
}();

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -60,9 +60,9 @@ constexpr auto kSkipCloudDraftsFor = TimeId(3);
} // namespace
History::History(not_null<Data::Session*> owner, const PeerId &peerId)
History::History(not_null<Data::Session*> 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<HistoryItem*> 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<HistoryItem*> 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<HistoryItem*> item) {
@ -1130,7 +1146,7 @@ void History::newItemAdded(not_null<HistoryItem*> 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<const HistoryItem*> 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<AdminLog::LocalIdManager> 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<QString> &History::chatsListNameWords() const {
const base::flat_set<QString> &History::chatListNameWords() const {
return peer->nameWords();
}
const base::flat_set<QChar> &History::chatsListFirstLetters() const {
const base::flat_set<QChar> &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<HistoryItem*> {
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<const MTPMessage*>(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<const MTPMessage*>(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<MTPMessage>());
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*> 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*> 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();
}

View File

@ -58,7 +58,7 @@ class History final : public Dialogs::Entry {
public:
using Element = HistoryView::Element;
History(not_null<Data::Session*> owner, const PeerId &peerId);
History(not_null<Data::Session*> 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<MsgId> 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<QString> &chatsListNameWords() const override;
const base::flat_set<QChar> &chatsListFirstLetters() 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 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<HistoryItem*> item);
void forgetScrollState() {
scrollTopItem = nullptr;
}
@ -411,6 +419,12 @@ private:
void checkLastMessage();
void setLastMessage(HistoryItem *item);
void refreshChatListMessage();
void setChatListMessage(HistoryItem *item);
std::optional<HistoryItem*> 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<int> _unreadMentionsCount;
base::flat_set<MsgId> _unreadMentions;
std::optional<HistoryItem*> _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<HistoryItem*> _chatListMessage;
bool _unreadMark = false;
// A pointer to the block that is currently being built.

View File

@ -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;
}
}
}
}

View File

@ -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> _botAbout;

View File

@ -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());
}

View File

@ -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);

View File

@ -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<Data::MediaPhoto>(
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) {

View File

@ -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();

View File

@ -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<MTPMessage>
_list->messagesReceived(peer, messages);
if (!_firstLoadRequest) {
updateHistoryGeometry();
adjustHighlightedMessageToMigrated();
updateBotKeyboard();
}
}
@ -5553,22 +5532,15 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> 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<InformBox>(lang(lng_reply_cant)));
} else {
if (item->serviceMsg()) {
Ui::show(Box<InformBox>(lang(lng_reply_cant)));
} else {
const auto itemId = item->fullId();
Ui::show(Box<ConfirmBox>(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<ConfirmBox>(lang(lng_reply_cant_forward), lang(lng_selected_forward), crl::guard(this, [=] {
App::main()->setForwardDraft(
_peer->id,
{ 1, itemId });
})));
}
return;
}

View File

@ -387,7 +387,6 @@ private:
void supportShareContact(Support::Contact contact);
void highlightMessage(MsgId universalMessageId);
void adjustHighlightedMessageToMigrated();
void checkNextHighlight();
void updateHighlightedMessage();
void clearHighlightMessages();

View File

@ -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);

View File

@ -71,7 +71,7 @@ void Cover::initViewers() {
}
void Cover::refreshNameText() {
_name->setText(_feed->chatsListName());
_name->setText(_feed->chatListName());
refreshNameGeometry(width());
}

View File

@ -731,9 +731,7 @@ void MainWidget::cancelUploadLayer(not_null<HistoryItem*> 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<PeerData*> peer,
const QVector<MTPint> &ids,
bool forEveryone) {
bool revoke) {
if (const auto channel = peer->asChannel()) {
MTP::send(
MTPchannels_DeleteMessages(
@ -839,13 +837,10 @@ void MainWidget::deleteMessages(
MTP_vector<MTPint>(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<MTPint>(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);
}
}
}

View File

@ -196,7 +196,7 @@ public:
void deleteMessages(
not_null<PeerData*> peer,
const QVector<MTPint> &ids,
bool forEveryone);
bool revoke);
void deletedContact(UserData *user, const MTPcontacts_Link &result);
void deleteConversation(
not_null<PeerData*> peer,

View File

@ -409,22 +409,13 @@ void ResolveChannel(
std::optional<MTPMessage> GetMessagesElement(
const MTPmessages_Messages &list) {
const auto get = [](auto &&data) -> std::optional<MTPMessage> {
return list.match([&](const MTPDmessages_messagesNotModified &) {
return std::optional<MTPMessage>(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(

View File

@ -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),

View File

@ -643,7 +643,7 @@ bool PanelEditDocument::validate() {
error = firsttop.y();
}
auto first = QPointer<PanelDetailsRow>();
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) {

View File

@ -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();