Better chats list entries management.

Make unread counts and last message base::optional<>.
Remove ChannelHistory.
This commit is contained in:
John Preston 2018-01-31 20:10:29 +03:00
parent edcaccba1f
commit a7f67c4bc9
22 changed files with 1329 additions and 1146 deletions

View File

@ -1420,6 +1420,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_feed_group" = "Group in feed";
"lng_feed_ungroup" = "Ungroup from feed";
"lng_feed_channel_added" = "Channel added to your feed.";
"lng_feed_channel_removed" = "Channel removed from your feed.";
"lng_info_feed_title" = "Feed Info";

View File

@ -172,9 +172,10 @@ void ApiWrap::savePinnedOrder() {
void ApiWrap::toggleChannelGrouping(
not_null<ChannelData*> channel,
bool group) {
bool group,
base::lambda<void()> callback) {
if (const auto already = _channelGroupingRequests.take(channel)) {
request(*already).cancel();
request(already->first).cancel();
}
const auto feedId = Data::Feed::kId;
const auto flags = group
@ -185,15 +186,18 @@ void ApiWrap::toggleChannelGrouping(
channel->inputChannel,
MTP_int(feedId)
)).done([=](const MTPBool &result) {
_channelGroupingRequests.remove(channel);
if (group) {
channel->setFeed(Auth().data().feed(feedId));
} else {
channel->clearFeed();
}
if (const auto data = _channelGroupingRequests.take(channel)) {
data->second();
}
}).fail([=](const RPCError &error) {
_channelGroupingRequests.remove(channel);
}).send();
_channelGroupingRequests.emplace(channel, requestId, callback);
}
void ApiWrap::sendMessageFail(const RPCError &error) {
@ -362,6 +366,124 @@ void ApiWrap::requestContacts() {
}).send();
}
void ApiWrap::requestDialogEntry(not_null<Data::Feed*> feed) {
if (_dialogFeedRequests.contains(feed)) {
return;
}
_dialogFeedRequests.emplace(feed);
auto peers = QVector<MTPInputDialogPeer>(
1,
MTP_inputDialogPeerFeed(MTP_int(feed->id())));
if (feed->channelsLoaded()) {
const auto &channels = feed->channels();
peers.reserve(channels.size() + 1);
for (const auto history : feed->channels()) {
peers.push_back(MTP_inputDialogPeer(history->peer->input));
}
} else {
request(MTPmessages_GetDialogs(
MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id),
MTP_int(feed->id()),
MTP_int(0), // offset_date
MTP_int(0), // offset_id
MTP_inputPeerEmpty(), // offset_peer
MTP_int(Data::Feed::kChannelsLimit)
)).done([=](const MTPmessages_Dialogs &result) {
// applyFeedChannels(result);
}).send();
}
request(MTPmessages_GetPeerDialogs(
MTP_vector(std::move(peers))
)).done([=](const MTPmessages_PeerDialogs &result) {
applyPeerDialogs(result);
_dialogFeedRequests.remove(feed);
}).fail([=](const RPCError &error) {
_dialogFeedRequests.remove(feed);
}).send();
}
void ApiWrap::requestDialogEntry(not_null<History*> history) {
if (_dialogRequests.contains(history)) {
return;
}
_dialogRequests.emplace(history);
auto peers = QVector<MTPInputDialogPeer>(
1,
MTP_inputDialogPeer(history->peer->input));
request(MTPmessages_GetPeerDialogs(
MTP_vector(std::move(peers))
)).done([=](const MTPmessages_PeerDialogs &result) {
applyPeerDialogs(result);
if (history->lastMessage()) {
if (!history->chatsListDate().isNull()
&& history->loadedAtBottom()) {
if (const auto channel = history->peer->asChannel()) {
const auto inviter = channel->inviter;
if (inviter != 0
&& history->chatsListDate() <= channel->inviteDate
&& channel->amIn()) {
if (const auto from = App::userLoaded(inviter)) {
history->insertJoinedMessage(true);
}
}
}
}
history->updateChatListExistence();
} else {
if (const auto chat = history->peer->asChat()) {
if (!chat->haveLeft()) {
Local::addSavedPeer(
history->peer,
history->chatsListDate());
}
} 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);
}
}
_dialogRequests.remove(history);
}).fail([=](const RPCError &error) {
_dialogRequests.remove(history);
}).send();
}
void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) {
Expects(dialogs.type() == mtpc_messages_peerDialogs);
const auto &data = dialogs.c_messages_peerDialogs();
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
App::feedMsgs(data.vmessages, NewMessageLast);
for (const auto &dialog : data.vdialogs.v) {
switch (dialog.type()) {
case mtpc_dialog: {
const auto &fields = dialog.c_dialog();
if (const auto peerId = peerFromMTP(fields.vpeer)) {
App::history(peerId)->applyDialog(fields);
}
} break;
case mtpc_dialogFeed: {
const auto &fields = dialog.c_dialogFeed();
const auto feed = Auth().data().feed(fields.vfeed_id.v);
feed->applyDialog(fields);
} break;
}
}
_session->data().sendHistoryChangeNotifications();
}
void ApiWrap::requestFullPeer(PeerData *peer) {
if (!peer || _fullPeerRequests.contains(peer)) return;
@ -464,7 +586,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
if (auto h = App::historyLoaded(cfrom->id)) {
if (auto hto = App::historyLoaded(channel->id)) {
if (!h->isEmpty()) {
h->clear(true);
h->unloadBlocks();
}
if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) {
App::main()->removeDialog(h);
@ -495,14 +617,12 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
channel->setRestrictedCount(f.has_banned_count() ? f.vbanned_count.v : 0);
channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0);
channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
if (auto h = App::historyLoaded(channel->id)) {
if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) {
h->setUnreadCount(f.vunread_count.v);
h->inboxReadBefore = f.vread_inbox_max_id.v + 1;
}
accumulate_max(h->outboxReadBefore, f.vread_outbox_max_id.v + 1);
if (const auto history = App::historyLoaded(channel->id)) {
history->applyDialogFields(
f.vunread_count.v,
f.vread_inbox_max_id.v,
f.vread_outbox_max_id.v);
}
ranges::overload([] {}, [](int a) {});
if (f.has_pinned_msg_id()) {
channel->setPinnedMessageId(f.vpinned_msg_id.v);
} else {
@ -1063,7 +1183,9 @@ void ApiWrap::unblockParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
const auto kick = KickRequest(channel, user);
if (_kickRequests.contains(kick)) return;
if (_kickRequests.contains(kick)) {
return;
}
const auto requestId = request(MTPchannels_EditBanned(
channel->inputChannel,
@ -1085,6 +1207,43 @@ void ApiWrap::unblockParticipant(
_kickRequests.emplace(kick, requestId);
}
void ApiWrap::deleteAllFromUser(
not_null<ChannelData*> channel,
not_null<UserData*> from) {
const auto history = App::historyLoaded(channel->id);
const auto ids = history
? history->collectMessagesFromUserToDelete(from)
: QVector<MsgId>();
const auto channelId = peerToChannel(channel->id);
for (const auto msgId : ids) {
if (const auto item = App::histItemById(channelId, msgId)) {
item->destroy();
}
}
Auth().data().sendHistoryChangeNotifications();
deleteAllFromUserSend(channel, from);
}
void ApiWrap::deleteAllFromUserSend(
not_null<ChannelData*> channel,
not_null<UserData*> from) {
request(MTPchannels_DeleteUserHistory(
channel->inputChannel,
from->inputUser
)).done([=](const MTPmessages_AffectedHistory &result) {
const auto offset = applyAffectedHistory(channel, result);
if (offset > 0) {
deleteAllFromUserSend(channel, from);
} else if (const auto history = App::historyLoaded(channel)) {
if (!history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history);
}
}
}).send();
}
void ApiWrap::requestChannelMembersForAdd(
not_null<ChannelData*> channel,
base::lambda<void(const MTPchannels_ChannelParticipants&)> callback) {
@ -1505,23 +1664,23 @@ int ApiWrap::onlineTillFromStatus(const MTPUserStatus &status, int currentOnline
}
void ApiWrap::clearHistory(not_null<PeerData*> peer) {
auto lastMsgId = MsgId(0);
auto deleteTillId = MsgId(0);
if (auto history = App::historyLoaded(peer->id)) {
if (history->lastMsg) {
lastMsgId = history->lastMsg->id;
Local::addSavedPeer(history->peer, history->lastMsg->date);
if (const auto last = history->lastMessage()) {
deleteTillId = last->id;
Local::addSavedPeer(history->peer, last->date);
}
history->clear();
history->newLoaded = history->oldLoaded = true;
}
if (auto channel = peer->asChannel()) {
if (auto migrated = peer->migrateFrom()) {
if (const auto channel = peer->asChannel()) {
if (const auto migrated = peer->migrateFrom()) {
clearHistory(migrated);
}
if (IsServerMsgId(lastMsgId)) {
if (IsServerMsgId(deleteTillId)) {
request(MTPchannels_DeleteHistory(
channel->inputChannel,
MTP_int(lastMsgId)
MTP_int(deleteTillId)
)).send();
}
} else {
@ -1530,7 +1689,7 @@ void ApiWrap::clearHistory(not_null<PeerData*> peer) {
peer->input,
MTP_int(0)
)).done([=](const MTPmessages_AffectedHistory &result) {
auto offset = applyAffectedHistory(peer, result);
const auto offset = applyAffectedHistory(peer, result);
if (offset > 0) {
clearHistory(peer);
}
@ -1775,6 +1934,94 @@ void ApiWrap::requestParticipantsCountDelayed(
[this, channel] { channel->updateFullForced(); });
}
void ApiWrap::requestChannelRangeDifference(not_null<History*> history) {
Expects(history->isChannel());
const auto channel = history->peer->asChannel();
if (const auto requestId = _rangeDifferenceRequests.take(channel)) {
request(*requestId).cancel();
}
const auto range = history->rangeForDifferenceRequest();
if (!(range.from < range.till) || !channel->pts()) {
return;
}
MTP_LOG(0, ("getChannelDifference { good - "
"after channelDifferenceTooLong was received, "
"validating history part }%1").arg(cTestMode() ? " TESTMODE" : ""));
channelRangeDifferenceSend(channel, range, channel->pts());
}
void ApiWrap::channelRangeDifferenceSend(
not_null<ChannelData*> channel,
MsgRange range,
int32 pts) {
Expects(range.from < range.till);
const auto limit = range.till - range.from;
const auto filter = MTP_channelMessagesFilter(
MTP_flags(0),
MTP_vector<MTPMessageRange>(1, MTP_messageRange(
MTP_int(range.from),
MTP_int(range.till - 1))));
const auto requestId = request(MTPupdates_GetChannelDifference(
MTP_flags(MTPupdates_GetChannelDifference::Flag::f_force),
channel->inputChannel,
filter,
MTP_int(pts),
MTP_int(limit)
)).done([=](const MTPupdates_ChannelDifference &result) {
_rangeDifferenceRequests.remove(channel);
channelRangeDifferenceDone(channel, range, result);
}).fail([=](const RPCError &error) {
_rangeDifferenceRequests.remove(channel);
}).send();
_rangeDifferenceRequests.emplace(channel, requestId);
}
void ApiWrap::channelRangeDifferenceDone(
not_null<ChannelData*> channel,
MsgRange range,
const MTPupdates_ChannelDifference &result) {
auto nextRequestPts = int32(0);
auto isFinal = true;
switch (result.type()) {
case mtpc_updates_channelDifferenceEmpty: {
const auto &d = result.c_updates_channelDifferenceEmpty();
nextRequestPts = d.vpts.v;
isFinal = d.is_final();
} break;
case mtpc_updates_channelDifferenceTooLong: {
const auto &d = result.c_updates_channelDifferenceTooLong();
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
nextRequestPts = d.vpts.v;
isFinal = d.is_final();
} break;
case mtpc_updates_channelDifference: {
const auto &d = result.c_updates_channelDifference();
App::main()->feedChannelDifference(d);
nextRequestPts = d.vpts.v;
isFinal = d.is_final();
} break;
}
if (!isFinal) {
MTP_LOG(0, ("getChannelDifference { "
"good - after not final channelDifference was received, "
"validating history part }%1"
).arg(cTestMode() ? " TESTMODE" : ""));
channelRangeDifferenceSend(channel, range, nextRequestPts);
}
}
void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
const QVector<MTPMessage> *v = 0;
switch (msgs.type()) {
@ -3438,7 +3685,11 @@ void ApiWrap::readServerHistory(not_null<History*> history) {
void ApiWrap::readServerHistoryForce(not_null<History*> history) {
const auto peer = history->peer;
const auto upTo = history->inboxRead(0);
const auto upTo = history->readInbox();
if (!upTo) {
return;
}
if (const auto channel = peer->asChannel()) {
if (!channel->amIn()) {
return; // no read request for channels that I didn't join

View File

@ -56,7 +56,10 @@ public:
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
void savePinnedOrder();
void toggleChannelGrouping(not_null<ChannelData*> channel, bool group);
void toggleChannelGrouping(
not_null<ChannelData*> channel,
bool group,
base::lambda<void()> callback);
using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>;
void requestMessageData(
@ -65,6 +68,8 @@ public:
RequestMessageDataCallback callback);
void requestContacts();
void requestDialogEntry(not_null<Data::Feed*> feed);
void requestDialogEntry(not_null<History*> history);
void requestFullPeer(PeerData *peer);
void requestPeer(PeerData *peer);
@ -73,6 +78,7 @@ public:
void requestBots(not_null<ChannelData*> channel);
void requestAdmins(not_null<ChannelData*> channel);
void requestParticipantsCountDelayed(not_null<ChannelData*> channel);
void requestChannelRangeDifference(not_null<History*> history);
void requestChangelog(
const QString &sinceVersion,
@ -96,6 +102,9 @@ public:
void unblockParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user);
void deleteAllFromUser(
not_null<ChannelData*> channel,
not_null<UserData*> from);
void requestWebPageDelayed(WebPageData *page);
void clearWebPageRequest(WebPageData *page);
@ -269,6 +278,7 @@ private:
QVector<MTPint> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req);
@ -285,10 +295,24 @@ private:
int availableCount,
const QVector<MTPChannelParticipant> &list);
void resolveWebPages();
void gotWebPages(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
void gotWebPages(
ChannelData *channel,
const MTPmessages_Messages &result,
mtpRequestId req);
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
PeerData *notifySettingReceived(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings);
void channelRangeDifferenceSend(
not_null<ChannelData*> channel,
MsgRange range,
int32 pts);
void channelRangeDifferenceDone(
not_null<ChannelData*> channel,
MsgRange range,
const MTPupdates_ChannelDifference &result);
PeerData *notifySettingReceived(
MTPInputNotifyPeer peer,
const MTPPeerNotifySettings &settings);
void stickerSetDisenabled(mtpRequestId requestId);
void stickersSaveOrder();
@ -347,6 +371,11 @@ private:
void applyAffectedMessages(
not_null<PeerData*> peer,
const MTPmessages_AffectedMessages &result);
void deleteAllFromUserSend(
not_null<ChannelData*> channel,
not_null<UserData*> from);
void sendMessageFail(const RPCError &error);
void uploadAlbumMedia(
not_null<HistoryItem*> item,
@ -391,7 +420,7 @@ private:
const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback;
base::flat_map<
not_null<ChannelData*>,
mtpRequestId> _channelGroupingRequests;
std::pair<mtpRequestId,base::lambda<void()>>> _channelGroupingRequests;
using KickRequest = std::pair<
not_null<ChannelData*>,
@ -400,6 +429,10 @@ private:
QMap<ChannelData*, mtpRequestId> _selfParticipantRequests;
base::flat_map<
not_null<ChannelData*>,
mtpRequestId> _rangeDifferenceRequests;
QMap<WebPageData*, mtpRequestId> _webPagesPending;
base::Timer _webPagesTimer;
@ -432,6 +465,8 @@ private:
mtpRequestId _contactsRequestId = 0;
mtpRequestId _contactsStatusesRequestId = 0;
base::flat_set<not_null<Data::Feed*>> _dialogFeedRequests;
base::flat_set<not_null<History*>> _dialogRequests;
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;

View File

@ -459,7 +459,7 @@ namespace {
if (auto h = App::historyLoaded(cdata->id)) {
if (auto hto = App::historyLoaded(channel->id)) {
if (!h->isEmpty()) {
h->clear(true);
h->unloadBlocks();
}
if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) {
App::main()->removeDialog(h);
@ -1019,7 +1019,7 @@ namespace {
}
void feedInboxRead(const PeerId &peer, MsgId upTo) {
if (auto history = App::historyLoaded(peer)) {
if (const auto history = App::historyLoaded(peer)) {
history->inboxRead(upTo);
}
}
@ -1027,17 +1027,8 @@ namespace {
void feedOutboxRead(const PeerId &peer, MsgId upTo, TimeId when) {
if (auto history = App::historyLoaded(peer)) {
history->outboxRead(upTo);
if (history->lastMsg
&& history->lastMsg->out()
&& history->lastMsg->id <= upTo) {
if (const auto main = App::main()) {
main->repaintDialogRow(history, history->lastMsg->id);
}
}
history->updateChatListEntry();
if (history->peer->isUser()) {
history->peer->asUser()->madeAction(when);
if (const auto user = history->peer->asUser()) {
user->madeAction(when);
}
}
}
@ -1055,37 +1046,31 @@ namespace {
return &(*i);
}
void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds) {
void feedWereDeleted(
ChannelId channelId,
const QVector<MTPint> &msgsIds) {
const auto data = fetchMsgsData(channelId, false);
if (!data) return;
const auto channelHistory = (channelId != NoChannel)
? App::history(peerFromChannel(channelId))->asChannelHistory()
const auto affectedHistory = (channelId != NoChannel)
? App::history(peerFromChannel(channelId)).get()
: nullptr;
base::flat_set<not_null<History*>> historiesToCheck;
auto historiesToCheck = base::flat_set<not_null<History*>>();
for (const auto msgId : msgsIds) {
auto j = data->constFind(msgId.v);
if (j != data->cend()) {
const auto h = (*j)->history();
const auto history = (*j)->history();
(*j)->destroy();
if (!h->lastMsg) {
historiesToCheck.emplace(h);
}
} else {
if (channelHistory) {
if (channelHistory->unreadCount() > 0
&& msgId.v >= channelHistory->inboxReadBefore) {
channelHistory->setUnreadCount(
channelHistory->unreadCount() - 1);
}
if (!history->lastMessageKnown()) {
historiesToCheck.emplace(history);
}
} else if (affectedHistory) {
affectedHistory->unknownMessageDeleted(msgId.v);
}
}
if (main()) {
for (const auto history : historiesToCheck) {
main()->checkPeerHistory(history->peer);
}
for (const auto history : historiesToCheck) {
Auth().api().requestDialogEntry(history);
}
}
@ -1222,10 +1207,6 @@ namespace {
return ::histories.findOrInsert(peer);
}
not_null<History*> historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead, int32 maxOutboxRead) {
return ::histories.findOrInsert(peer, unreadCnt, maxInboxRead, maxOutboxRead);
}
History *historyLoaded(const PeerId &peer) {
if (!peer) {
return nullptr;

View File

@ -148,7 +148,6 @@ namespace App {
Histories &histories();
not_null<History*> history(const PeerId &peer);
not_null<History*> historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead, int32 maxOutboxRead);
History *historyLoaded(const PeerId &peer);
HistoryItem *histItemById(ChannelId channelId, MsgId itemId);
inline not_null<History*> history(const PeerData *peer) {

View File

@ -568,7 +568,7 @@ void DeleteMessagesBox::deleteAndClear() {
MTP_vector<MTPint>(1, MTP_int(_ids[0].msg))));
}
if (_deleteAll && _deleteAll->checked()) {
App::main()->deleteAllFromUser(
Auth().api().deleteAllFromUser(
_moderateInChannel,
_moderateFrom);
}
@ -583,13 +583,13 @@ void DeleteMessagesBox::deleteAndClear() {
if (auto item = App::histItemById(itemId)) {
auto history = item->history();
auto wasOnServer = (item->id > 0);
auto wasLast = (history->lastMsg == item);
auto wasLast = (history->lastMessage() == item);
item->destroy();
if (wasOnServer) {
idsByPeer[history->peer].push_back(MTP_int(itemId.msg));
} else if (wasLast) {
App::main()->checkPeerHistory(history->peer);
Auth().api().requestDialogEntry(history);
}
}
}

View File

@ -372,6 +372,7 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(not_null<Pee
}
void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
Auth().api().requestDialogEntry(App::history(row->peer()));
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
}

View File

@ -72,8 +72,10 @@ void Feed::registerOne(not_null<ChannelData*> channel) {
if (!base::contains(_channels, history)) {
const auto invisible = (_channels.size() < 2);
_channels.push_back(history);
if (history->lastMsg) {
updateLastMessage(history->lastMsg);
if (history->lastMessageKnown()) {
recountLastMessage();
} else if (_channelsLoaded) {
_parent->session().api().requestDialogEntry(history);
}
_parent->session().storage().remove(
Storage::FeedMessagesInvalidate(_id));
@ -96,8 +98,10 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) {
if (i != end(_channels)) {
const auto visible = (_channels.size() > 1);
_channels.erase(i, end(_channels));
if (_lastMessage && _lastMessage->history() == history) {
messageRemoved(_lastMessage);
if (const auto last = lastMessage()) {
if (last->history() == history) {
recountLastMessage();
}
}
_parent->session().storage().remove(
Storage::FeedMessagesRemoveAll(_id, channel->bareId()));
@ -115,8 +119,10 @@ void Feed::unregisterOne(not_null<ChannelData*> channel) {
}
void Feed::updateLastMessage(not_null<HistoryItem*> item) {
if (justSetLastMessage(item)) {
setChatsListDate(_lastMessage->date);
if (justUpdateLastMessage(item)) {
if (_lastMessage && *_lastMessage) {
setChatsListDate((*_lastMessage)->date);
}
}
}
@ -207,8 +213,11 @@ void Feed::setChannels(std::vector<not_null<ChannelData*>> channels) {
_parent->notifyFeedUpdated(this, FeedUpdateFlag::Channels);
}
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
if (_lastMessage && item->position() <= _lastMessage->position()) {
bool Feed::justUpdateLastMessage(not_null<HistoryItem*> item) {
if (!_lastMessage) {
return false;
} else if (*_lastMessage
&& item->position() <= (*_lastMessage)->position()) {
return false;
}
_lastMessage = item;
@ -216,36 +225,106 @@ bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
}
void Feed::messageRemoved(not_null<HistoryItem*> item) {
if (_lastMessage == item) {
if (lastMessage() == item) {
_lastMessage = base::none;
recountLastMessage();
}
}
void Feed::historyCleared(not_null<History*> history) {
if (_lastMessage->history() == history) {
recountLastMessage();
if (const auto last = lastMessage()) {
if (last->history() == history) {
messageRemoved(last);
}
}
}
void Feed::recountLastMessage() {
_lastMessage = nullptr;
for (const auto history : _channels) {
if (const auto last = history->lastMsg) {
justSetLastMessage(last);
if (!history->lastMessageKnown()) {
return;
}
}
if (_lastMessage) {
setChatsListDate(_lastMessage->date);
_lastMessage = nullptr;
for (const auto history : _channels) {
if (const auto last = history->lastMessage()) {
justUpdateLastMessage(last);
}
}
updateChatsListDate();
}
void Feed::updateChatsListDate() {
if (_lastMessage && *_lastMessage) {
setChatsListDate((*_lastMessage)->date);
}
}
void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
_unreadCount = unreadCount;
HistoryItem *Feed::lastMessage() const {
return _lastMessage ? *_lastMessage : nullptr;
}
bool Feed::lastMessageKnown() const {
return !!_lastMessage;
}
void Feed::applyDialog(const MTPDdialogFeed &data) {
const auto addChannel = [&](ChannelId channelId) {
if (const auto channel = App::channelLoaded(channelId)) {
channel->setFeed(this);
}
};
for (const auto &channelId : data.vfeed_other_channels.v) {
addChannel(channelId.v);
}
_lastMessage = 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);
}
}
}
updateChatsListDate();
setUnreadCounts(
data.vunread_count.v,
data.vunread_muted_count.v);
if (data.has_read_max_position()) {
setUnreadPosition(FeedPositionFromMTP(data.vread_max_position));
}
}
void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
_unreadCount = unreadNonMutedCount + unreadMutedCount;
_unreadMutedCount = unreadMutedCount;
}
void Feed::setUnreadPosition(const MessagePosition &position) {
_unreadPosition = position;
if (_unreadPosition.current() < position) {
_unreadPosition = position;
}
}
void Feed::unreadCountChanged(
base::optional<int> unreadCountDelta,
int mutedCountDelta) {
if (!_unreadCount) {
return;
}
if (unreadCountDelta) {
*_unreadCount = std::max(*_unreadCount + *unreadCountDelta, 0);
_unreadMutedCount = snap(
_unreadMutedCount + mutedCountDelta,
0,
*_unreadCount);
updateChatListEntry();
} else {
_parent->session().api().requestDialogEntry(this);
}
}
MessagePosition Feed::unreadPosition() const {
@ -265,15 +344,15 @@ bool Feed::shouldBeInChatList() const {
}
int Feed::chatListUnreadCount() const {
return _unreadCount;
return _unreadCount ? *_unreadCount : 0;
}
bool Feed::chatListMutedBadge() const {
return _unreadCount <= _unreadMutedCount;
return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false;
}
HistoryItem *Feed::chatsListItem() const {
return _lastMessage;
return lastMessage();
}
const QString &Feed::chatsListName() const {

View File

@ -32,6 +32,7 @@ MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position);
class Feed : public Dialogs::Entry {
public:
static constexpr auto kId = 1;
static constexpr auto kChannelsLimit = 1000;
Feed(FeedId id, not_null<Data::Session*> parent);
@ -43,11 +44,18 @@ public:
void messageRemoved(not_null<HistoryItem*> item);
void historyCleared(not_null<History*> history);
void setUnreadCounts(int unreadCount, int unreadMutedCount);
void applyDialog(const MTPDdialogFeed &data);
void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount);
void setUnreadPosition(const MessagePosition &position);
void unreadCountChanged(
base::optional<int> unreadCountDelta,
int mutedCountDelta);
MessagePosition unreadPosition() const;
rpl::producer<MessagePosition> unreadPositionChanges() const;
HistoryItem *lastMessage() const;
bool lastMessageKnown() const;
bool toImportant() const override;
bool shouldBeInChatList() const override;
int chatListUnreadCount() const override;
@ -73,7 +81,8 @@ public:
private:
void indexNameParts();
void recountLastMessage();
bool justSetLastMessage(not_null<HistoryItem*> item);
bool justUpdateLastMessage(not_null<HistoryItem*> item);
void updateChatsListDate();
FeedId _id = 0;
not_null<Data::Session*> _parent;
@ -83,10 +92,10 @@ private:
QString _name;
base::flat_set<QString> _nameWords;
base::flat_set<QChar> _nameFirstLetters;
HistoryItem *_lastMessage = nullptr;
base::optional<HistoryItem*> _lastMessage;
rpl::variable<MessagePosition> _unreadPosition;
int _unreadCount = 0;
base::optional<int> _unreadCount;
int _unreadMutedCount = 0;
};

View File

@ -849,13 +849,22 @@ void ChannelData::setAvailableMinId(MsgId availableMinId) {
if (auto history = App::historyLoaded(this)) {
history->clearUpTill(availableMinId);
}
if (_pinnedMessageId <= _availableMinId) {
_pinnedMessageId = MsgId(0);
Notify::peerUpdatedDelayed(
this,
Notify::PeerUpdate::Flag::ChannelPinnedChanged);
}
}
}
void ChannelData::setPinnedMessageId(MsgId messageId) {
messageId = (messageId > _availableMinId) ? messageId : MsgId(0);
if (_pinnedMessageId != messageId) {
_pinnedMessageId = messageId;
Notify::peerUpdatedDelayed(this, Notify::PeerUpdate::Flag::ChannelPinnedChanged);
Notify::peerUpdatedDelayed(
this,
Notify::PeerUpdate::Flag::ChannelPinnedChanged);
}
}

View File

@ -1726,76 +1726,32 @@ void DialogsInner::applyDialog(const MTPDdialog &dialog) {
return;
}
const auto history = App::historyFromDialog(
peerId,
dialog.vunread_count.v,
dialog.vread_inbox_max_id.v,
dialog.vread_outbox_max_id.v);
history->setUnreadMentionsCount(dialog.vunread_mentions_count.v);
const auto peer = history->peer;
if (const auto channel = peer->asChannel()) {
if (dialog.has_pts()) {
channel->ptsReceived(dialog.vpts.v);
}
if (!channel->amCreator()) {
const auto topMsgId = FullMsgId(
peerToChannel(channel->id),
dialog.vtop_message.v);
if (const auto topMsg = App::histItemById(topMsgId)) {
if (topMsg->date <= date(channel->date)) {
Auth().api().requestSelfParticipant(channel);
}
}
}
}
App::main()->applyNotifySetting(
MTP_notifyPeer(dialog.vpeer),
dialog.vnotify_settings,
history);
const auto history = App::history(peerId);
history->applyDialog(dialog);
if (!history->isPinnedDialog() && !history->chatsListDate().isNull()) {
addSavedPeersAfter(history->chatsListDate());
}
_contactsNoDialogs->del(history);
if (peer->migrateFrom()) {
if (const auto historyFrom = App::historyLoaded(peer->migrateFrom())) {
if (const auto from = history->peer->migrateFrom()) {
if (const auto historyFrom = App::historyLoaded(from)) {
removeDialog(historyFrom);
}
} else if (peer->migrateTo() && peer->migrateTo()->amIn()) {
removeDialog(history);
}
if (dialog.has_draft() && dialog.vdraft.type() == mtpc_draftMessage) {
const auto &draft = dialog.vdraft.c_draftMessage();
Data::applyPeerCloudDraft(peerId, draft);
} else if (const auto to = history->peer->migrateTo()) {
if (to->amIn()) {
removeDialog(history);
}
}
}
void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
const auto peerId = peerFromMTP(dialog.vpeer);
const auto feedId = dialog.vfeed_id.v;
const auto feed = Auth().data().feed(feedId);
const auto addChannel = [&](ChannelId channelId) {
if (const auto channel = App::channelLoaded(channelId)) {
channel->setFeed(feed);
}
};
if (peerId && peerIsChannel(peerId)) {
addChannel(peerToChannel(peerId));
}
for (const auto &channelId : dialog.vfeed_other_channels.v) {
addChannel(channelId.v);
}
feed->setUnreadCounts(
dialog.vunread_count.v,
dialog.vunread_muted_count.v);
feed->applyDialog(dialog);
if (!feed->isPinnedDialog() && !feed->chatsListDate().isNull()) {
addSavedPeersAfter(feed->chatsListDate());
}
if (dialog.has_read_max_position()) {
feed->setUnreadPosition(
Data::FeedPositionFromMTP(dialog.vread_max_position));
}
}
void DialogsInner::addSavedPeersAfter(const QDateTime &date) {

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/flags.h"
class History;
class ChannelHistory;
class HistoryBlock;
class HistoryItem;
class HistoryMessage;
@ -50,12 +49,7 @@ enum NewMessageType : char {
class Histories {
public:
using Map = QHash<PeerId, History*>;
Map map;
Histories() : _a_typings(animation(this, &Histories::step_typings)) {
_selfDestructTimer.setCallback([this] { checkSelfDestructItems(); });
}
Histories();
void registerSendAction(
not_null<History*> history,
@ -64,20 +58,16 @@ public:
TimeId when);
void step_typings(TimeMs ms, bool timer);
History *find(const PeerId &peerId);
not_null<History*> findOrInsert(const PeerId &peerId);
not_null<History*> findOrInsert(
const PeerId &peerId,
int unreadCount,
MsgId maxInboxRead,
MsgId maxOutboxRead);
History *find(PeerId peerId) const;
not_null<History*> findOrInsert(PeerId peerId);
void clear();
void remove(const PeerId &peer);
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
typedef QMap<History*, TimeMs> TypingHistories; // when typing in this history started
// When typing in this history started.
typedef QMap<History*, TimeMs> TypingHistories;
TypingHistories typing;
BasicAnimation _a_typings;
@ -114,6 +104,8 @@ public:
private:
void checkSelfDestructItems();
std::unordered_map<PeerId, std::unique_ptr<History>> _map;
int _unreadFull = 0;
int _unreadMuted = 0;
base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated;
@ -136,29 +128,24 @@ public:
History(const History &) = delete;
History &operator=(const History &) = delete;
ChannelId channelId() const {
return peerToChannel(peer->id);
}
bool isChannel() const {
return peerIsChannel(peer->id);
}
bool isMegagroup() const {
return peer->isMegagroup();
}
ChannelHistory *asChannelHistory();
const ChannelHistory *asChannelHistory() const;
ChannelId channelId() const;
bool isChannel() const;
bool isMegagroup() const;
not_null<History*> migrateToOrMe() const;
History *migrateFrom() const;
MsgRange rangeForDifferenceRequest() const;
HistoryService *insertJoinedMessage(bool unread);
void checkJoinedMessage(bool createUnread = false);
bool isEmpty() const {
return blocks.empty();
}
bool isEmpty() const;
bool isDisplayedEmpty() const;
bool hasOrphanMediaGroupPart() const;
bool removeOrphanMediaGroupPart();
QVector<MsgId> collectMessagesFromUserToDelete(
not_null<UserData*> user) const;
void clear(bool leaveItems = false);
void clear();
void unloadBlocks();
void clearUpTill(MsgId availableMinId);
void applyGroupAdminChanges(const base::flat_map<UserId, bool> &changes);
@ -184,18 +171,17 @@ public:
void newItemAdded(not_null<HistoryItem*> item);
int countUnread(MsgId upTo);
MsgId inboxRead(MsgId upTo);
MsgId inboxRead(HistoryItem *wasRead);
MsgId outboxRead(MsgId upTo);
MsgId outboxRead(HistoryItem *wasRead);
MsgId readInbox();
void inboxRead(MsgId upTo);
void inboxRead(not_null<const HistoryItem*> wasRead);
void outboxRead(MsgId upTo);
void outboxRead(not_null<const HistoryItem*> wasRead);
bool isServerSideUnread(not_null<const HistoryItem*> item) const;
MsgId loadAroundId() const;
int unreadCount() const {
return _unreadCount;
}
int unreadCount() const;
void setUnreadCount(int newUnreadCount);
bool mute() const {
return _mute;
}
bool mute() const;
bool changeMute(bool newMute);
void addUnreadBar();
void destroyUnreadBar();
@ -212,46 +198,34 @@ public:
bool isReadyFor(MsgId msgId); // has messages for showing history at msgId
void getReadyFor(MsgId msgId);
void setLastMessage(HistoryItem *msg);
void fixLastMessage(bool wasAtBottom);
HistoryItem *lastMessage() const;
bool lastMessageKnown() const;
void unknownMessageDeleted(MsgId messageId);
void applyDialogTopMessage(MsgId topMessageId);
void applyDialog(const MTPDdialog &data);
void applyDialogFields(
int unreadCount,
MsgId maxInboxRead,
MsgId maxOutboxRead);
MsgId minMsgId() const;
MsgId maxMsgId() const;
MsgId msgIdForRead() const;
void resizeToWidth(int newWidth);
int height() const;
void removeNotification(HistoryItem *item) {
if (!notifies.isEmpty()) {
for (auto i = notifies.begin(), e = notifies.end(); i != e; ++i) {
if ((*i) == item) {
notifies.erase(i);
break;
}
}
}
}
HistoryItem *currentNotification() {
return notifies.isEmpty() ? 0 : notifies.front();
}
bool hasNotification() const {
return !notifies.isEmpty();
}
void skipNotification() {
if (!notifies.isEmpty()) {
notifies.pop_front();
}
}
void popNotification(HistoryItem *item) {
if (!notifies.isEmpty() && notifies.back() == item) notifies.pop_back();
}
void itemRemoved(not_null<HistoryItem*> item);
void itemVanished(not_null<HistoryItem*> item);
bool hasPendingResizedItems() const {
return _flags & Flag::f_has_pending_resized_items;
}
HistoryItem *currentNotification();
bool hasNotification() const;
void skipNotification();
void popNotification(HistoryItem *item);
bool hasPendingResizedItems() const;
void setHasPendingResizedItems();
void paintDialog(Painter &p, int32 w, bool sel) const;
bool mySendActionUpdated(SendAction::Type type, bool doing);
bool paintSendAction(Painter &p, int x, int y, int availableWidth, int outerWidth, style::color color, TimeMs ms);
@ -345,15 +319,9 @@ public:
// Still public data.
std::deque<std::unique_ptr<HistoryBlock>> blocks;
int height() const;
int32 msgCount = 0;
MsgId inboxReadBefore = 1;
MsgId outboxReadBefore = 1;
not_null<PeerData*> peer;
bool oldLoaded = false;
bool newLoaded = true;
HistoryItem *lastMsg = nullptr;
HistoryItem *lastSentMsg = nullptr;
typedef QList<HistoryItem*> NotifyQueue;
@ -379,7 +347,17 @@ public:
Text cloudDraftTextCache;
protected:
private:
friend class HistoryBlock;
enum class Flag {
f_has_pending_resized_items = (1 << 0),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) {
return true;
};
// when this item is destroyed scrollTopItem just points to the next one
// and scrollTopOffset remains the same
// if we are at the bottom of the window scrollTopItem == nullptr and
@ -389,7 +367,6 @@ protected:
// helper method for countScrollState(int top)
void countScrollTopItem(int top);
void clearOnDestroy();
HistoryItem *addNewToLastBlock(const MTPMessage &msg, NewMessageType type);
// this method just removes a block from the blocks list
@ -429,25 +406,18 @@ protected:
return _buildingFrontBlock != nullptr;
}
private:
friend class HistoryBlock;
enum class Flag {
f_has_pending_resized_items = (1 << 0),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) {
return true;
};
void mainViewRemoved(
not_null<HistoryBlock*> block,
not_null<Element*> view);
void removeNotification(not_null<HistoryItem*> item);
QDateTime adjustChatListDate() const override;
void changedInChatListHook(Dialogs::Mode list, bool added) override;
void changedChatListPinHook() override;
void setInboxReadTill(MsgId upTo);
void setOutboxReadTill(MsgId upTo);
void applyMessageChanges(
not_null<HistoryItem*> item,
const MTPMessage &original);
@ -455,14 +425,13 @@ private:
not_null<HistoryItem*> item,
const MTPDmessageService &data);
// After adding a new history slice check the lastMsg and newLoaded.
void checkLastMsg();
// After adding a new history slice check the lastMessage and newLoaded.
void checkLastMessage();
void setLastMessage(HistoryItem *item);
// Add all items to the unread mentions if we were not loaded at bottom and now are.
void checkAddAllToUnreadMentions();
template <int kSharedMediaTypeCount>
void addToSharedMedia(std::vector<MsgId> (&medias)[kSharedMediaTypeCount], bool force);
void addToSharedMedia(const std::vector<not_null<HistoryItem*>> &items);
void addEdgesToSharedMedia();
@ -480,14 +449,18 @@ private:
Flags _flags = 0;
bool _mute = false;
int _unreadCount = 0;
int _width = 0;
int _height = 0;
Element *_unreadBarView = nullptr;
Element *_firstUnreadView = nullptr;
HistoryService *_joinedMessage = nullptr;
base::optional<MsgId> _inboxReadBefore;
base::optional<MsgId> _outboxReadBefore;
base::optional<int> _unreadCount;
base::optional<int> _unreadMentionsCount;
base::flat_set<MsgId> _unreadMentions;
base::optional<HistoryItem*> _lastMessage;
// A pointer to the block that is currently being built.
// We hold this pointer so we can destroy it while building
@ -517,37 +490,6 @@ private:
};
class ChannelHistory : public History {
public:
using History::History;
void messageDetached(not_null<HistoryItem*> message);
void getRangeDifference();
void getRangeDifferenceNext(int32 pts);
HistoryService *insertJoinedMessage(bool unread);
void checkJoinedMessage(bool createUnread = false);
const QDateTime &maxReadMessageDate();
~ChannelHistory();
private:
friend class History;
void checkMaxReadMessageDate();
void cleared(bool leaveItems);
QDateTime _maxReadMessageDate;
HistoryService *_joinedMessage = nullptr;
MsgId _rangeDifferenceFromId, _rangeDifferenceToId;
int32 _rangeDifferencePts;
mtpRequestId _rangeDifferenceRequestId;
};
class HistoryBlock {
public:
using Element = HistoryView::Element;
@ -559,7 +501,6 @@ public:
std::vector<std::unique_ptr<Element>> messages;
void clear(bool leaveItems = false);
void remove(not_null<Element*> view);
void refreshView(not_null<Element*> view);

View File

@ -178,11 +178,15 @@ HistoryInner::HistoryInner(
}, lifetime());
}
void HistoryInner::messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages) {
void HistoryInner::messagesReceived(
PeerData *peer,
const QVector<MTPMessage> &messages) {
if (_history && _history->peer == peer) {
_history->addOlderSlice(messages);
} else if (_migrated && _migrated->peer == peer) {
bool newLoaded = (_migrated && _migrated->isEmpty() && !_history->isEmpty());
const auto newLoaded = _migrated
&& _migrated->isEmpty()
&& !_history->isEmpty();
_migrated->addOlderSlice(messages);
if (newLoaded) {
_migrated->addNewerSlice(QVector<MTPMessage>());

View File

@ -148,19 +148,7 @@ void HistoryItem::invalidateChatsListEntry() {
void HistoryItem::finishEditionToEmpty() {
finishEdition(-1);
_history->removeNotification(this);
if (auto channel = history()->peer->asChannel()) {
if (channel->pinnedMessageId() == id) {
channel->clearPinnedMessage();
}
}
if (history()->lastKeyboardId == id) {
history()->clearLastKeyboard();
}
if ((!out() || isPost()) && unread() && history()->unreadCount() > 0) {
history()->setUnreadCount(history()->unreadCount() - 1);
}
_history->itemVanished(this);
}
bool HistoryItem::isMediaUnread() const {
@ -244,7 +232,7 @@ void HistoryItem::destroy() {
if (isLogEntry()) {
Assert(!mainView());
} else {
// All this must be done for all items manually in History::clear(false)!
// All this must be done for all items manually in History::clear()!
eraseFromUnreadMentions();
if (IsServerMsgId(id)) {
if (const auto types = sharedMediaTypes()) {
@ -256,32 +244,7 @@ void HistoryItem::destroy() {
} else {
Auth().api().cancelLocalItem(this);
}
const auto wasAtBottom = history->loadedAtBottom();
history->removeNotification(this);
removeMainView();
if (history->lastMsg == this) {
history->fixLastMessage(wasAtBottom);
}
if (history->lastKeyboardId == id) {
history->clearLastKeyboard();
}
if ((!out() || isPost()) && unread() && history->unreadCount() > 0) {
history->setUnreadCount(history->unreadCount() - 1);
}
if (const auto channel = history->peer->asChannel()) {
if (channel->pinnedMessageId() == id) {
channel->clearPinnedMessage();
}
if (const auto feed = channel->feed()) {
// Must be after histroy->lastMsg is cleared.
// Otherwise feed last message will be this value again.
feed->messageRemoved(this);
// #TODO feeds unread
}
}
_history->itemRemoved(this);
}
delete this;
}
@ -295,9 +258,6 @@ void HistoryItem::refreshMainView() {
void HistoryItem::removeMainView() {
if (const auto view = mainView()) {
if (const auto channelHistory = _history->asChannelHistory()) {
channelHistory->messageDetached(this);
}
Auth().data().notifyHistoryChangeDelayed(_history);
view->removeFromBlock();
}
@ -551,11 +511,11 @@ bool HistoryItem::unread() const {
return false;
}
if (id > 0) {
if (id < history()->outboxReadBefore) {
if (IsServerMsgId(id)) {
if (!history()->isServerSideUnread(this)) {
return false;
}
if (auto user = history()->peer->asUser()) {
if (const auto user = history()->peer->asUser()) {
if (user->botInfo) {
return false;
}
@ -568,8 +528,8 @@ bool HistoryItem::unread() const {
return true;
}
if (id > 0) {
if (id < history()->inboxReadBefore) {
if (IsServerMsgId(id)) {
if (!history()->isServerSideUnread(this)) {
return false;
}
return true;
@ -587,24 +547,6 @@ bool HistoryItem::isEmpty() const {
&& !Has<HistoryMessageLogEntryOriginal>();
}
HistoryItem *HistoryItem::previousItem() const {
if (const auto view = mainView()) {
if (const auto previous = view->previousInBlocks()) {
return previous->data();
}
}
return nullptr;
}
HistoryItem *HistoryItem::nextItem() const {
if (const auto view = mainView()) {
if (const auto next = view->nextInBlocks()) {
return next->data();
}
}
return nullptr;
}
QString HistoryItem::notificationText() const {
auto getText = [this]() {
if (_media) {

View File

@ -254,9 +254,6 @@ public:
MessageGroupId groupId() const;
HistoryItem *previousItem() const;
HistoryItem *nextItem() const;
virtual std::unique_ptr<HistoryView::Element> createView(
not_null<HistoryView::ElementDelegate*> delegate) = 0;

View File

@ -2256,9 +2256,7 @@ void HistoryWidget::historyToDown(History *history) {
void HistoryWidget::unreadCountChanged(not_null<History*> history) {
if (history == _history || history == _migrated) {
updateHistoryDownVisibility();
_historyDown->setUnreadCount(
_history->unreadCount()
+ (_migrated ? _migrated->unreadCount() : 0));
_historyDown->setUnreadCount(_history->chatListUnreadCount());
}
}
@ -2402,9 +2400,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages
if (_history->loadedAtBottom() && App::wnd()) App::wnd()->checkHistoryActivation();
} else if (_firstLoadRequest == requestId) {
if (toMigrated) {
_history->clear(true);
_history->unloadBlocks();
} else if (_migrated) {
_migrated->clear(true);
_migrated->unloadBlocks();
}
addMessagesToFront(peer, *histList);
_firstLoadRequest = 0;
@ -2421,9 +2419,9 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages
historyLoaded();
} else if (_delayedShowAtRequest == requestId) {
if (toMigrated) {
_history->clear(true);
_history->unloadBlocks();
} else if (_migrated) {
_migrated->clear(true);
_migrated->unloadBlocks();
}
_delayedShowAtRequest = 0;
@ -2506,15 +2504,15 @@ void HistoryWidget::firstLoadMessages() {
auto offset = 0;
auto loadCount = kMessagesPerPage;
if (_showAtMsgId == ShowAtUnreadMsgId) {
if (_migrated && _migrated->unreadCount()) {
if (const auto around = _migrated ? _migrated->loadAroundId() : 0) {
_history->getReadyFor(_showAtMsgId);
from = _migrated->peer;
offset = -loadCount / 2;
offsetId = _migrated->inboxReadBefore;
} else if (_history->unreadCount()) {
offsetId = around;
} else if (const auto around = _history->loadAroundId()) {
_history->getReadyFor(_showAtMsgId);
offset = -loadCount / 2;
offsetId = _history->inboxReadBefore;
offsetId = around;
} else {
_history->getReadyFor(ShowAtTheEndMsgId);
}
@ -2651,13 +2649,13 @@ void HistoryWidget::delayedShowAt(MsgId showAtMsgId) {
auto offset = 0;
auto loadCount = kMessagesPerPage;
if (_delayedShowAtMsgId == ShowAtUnreadMsgId) {
if (_migrated && _migrated->unreadCount()) {
if (const auto around = _migrated ? _migrated->loadAroundId() : 0) {
from = _migrated->peer;
offset = -loadCount / 2;
offsetId = _migrated->inboxReadBefore;
} else if (_history->unreadCount()) {
offsetId = around;
} else if (const auto around = _history->loadAroundId()) {
offset = -loadCount / 2;
offsetId = _history->inboxReadBefore;
offsetId = around;
} else {
loadCount = kMessagesPerPageFirst;
}
@ -3630,8 +3628,17 @@ bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) {
}
bool HistoryWidget::isBotStart() const {
if (!_peer || !_peer->isUser() || !_peer->asUser()->botInfo || !_canSendMessages) return false;
return !_peer->asUser()->botInfo->startToken.isEmpty() || (_history->isEmpty() && !_history->lastMsg);
const auto user = _peer ? _peer->asUser() : nullptr;
if (!user
|| !user->botInfo
|| !_canSendMessages) {
return false;
} else if (!user->botInfo->startToken.isEmpty()) {
return true;
} else if (_history->isEmpty() && !_history->lastMessage()) {
return true;
}
return false;
}
bool HistoryWidget::isBlocked() const {
@ -5126,14 +5133,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
_scroll->keyPressEvent(e);
} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) {
if (_history && _history->lastMsg && !_editMsgId) {
if (const auto item = App::histItemById(_history->channelId(), _replyToId)) {
if (const auto next = item->nextItem()) {
Ui::showPeerHistory(_peer, next->id);
replyToMessage(next);
}
}
}
replyToNextMessage();
}
} else if (e->key() == Qt::Key_Up) {
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
@ -5145,17 +5145,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
}
_scroll->keyPressEvent(e);
} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) {
if (_history && _history->lastMsg && !_editMsgId) {
if (const auto item = App::histItemById(_history->channelId(), _replyToId)) {
if (const auto previous = item->previousItem()) {
Ui::showPeerHistory(_peer, previous->id);
replyToMessage(previous);
}
} else if (const auto previous = _history->lastMsg) {
Ui::showPeerHistory(_peer, previous->id);
replyToMessage(previous);
}
}
replyToPreviousMessage();
}
} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
onListEnterPressed();
@ -5164,6 +5154,45 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
}
}
void HistoryWidget::replyToPreviousMessage() {
if (!_history || _editMsgId) {
return;
}
const auto fullId = FullMsgId(
_history->channelId(),
_replyToId);
if (const auto item = App::histItemById(fullId)) {
if (const auto view = item->mainView()) {
if (const auto previousView = view->previousInBlocks()) {
const auto previous = previousView->data();
Ui::showPeerHistoryAtItem(previous);
replyToMessage(previous);
}
}
} else if (const auto previous = _history->lastMessage()) {
Ui::showPeerHistoryAtItem(previous);
replyToMessage(previous);
}
}
void HistoryWidget::replyToNextMessage() {
if (!_history || _editMsgId) {
return;
}
const auto fullId = FullMsgId(
_history->channelId(),
_replyToId);
if (const auto item = App::histItemById(fullId)) {
if (const auto view = item->mainView()) {
if (const auto nextView = view->nextInBlocks()) {
const auto next = nextView->data();
Ui::showPeerHistoryAtItem(next);
replyToMessage(next);
}
}
}
}
void HistoryWidget::onFieldTabbed() {
if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab);

View File

@ -538,6 +538,8 @@ private:
void applyInlineBotQuery(UserData *bot, const QString &query);
void cancelReplyAfterMediaSend(bool lastKeyboardUsed);
void replyToPreviousMessage();
void replyToNextMessage();
void hideSelectorControlsAnimated();
int countMembersDropdownHeightMax() const;

View File

@ -611,6 +611,9 @@ void Element::clickHandlerPressedChanged(
}
Element::~Element() {
if (_data->mainView() == this) {
_data->clearMainView();
}
Auth().data().unregisterItemView(this);
}

View File

@ -887,21 +887,27 @@ void MainWidget::deleteLayer(FullMsgId itemId) {
void MainWidget::cancelUploadLayer(not_null<HistoryItem*> item) {
const auto itemId = item->fullId();
Auth().uploader().pause(itemId);
Ui::show(Box<ConfirmBox>(lang(lng_selected_cancel_sure_this), lang(lng_selected_upload_stop), lang(lng_continue), base::lambda_guarded(this, [=] {
const auto stopUpload = [=] {
Ui::hideLayer();
if (const auto item = App::histItemById(itemId)) {
const auto history = item->history();
const auto wasLast = (history->lastMsg == item);
item->destroy();
if (wasLast && !history->lastMsg) {
checkPeerHistory(history->peer);
if (!history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history);
}
Auth().data().sendHistoryChangeNotifications();
}
Auth().uploader().unpause();
}), base::lambda_guarded(this, [] {
};
const auto continueUpload = [=] {
Auth().uploader().unpause();
})));
};
Ui::show(Box<ConfirmBox>(
lang(lng_selected_cancel_sure_this),
lang(lng_selected_upload_stop),
lang(lng_continue),
stopUpload,
continueUpload));
}
void MainWidget::deletePhotoLayer(PhotoData *photo) {
@ -1041,12 +1047,11 @@ void MainWidget::deleteConversation(
if (const auto history = App::historyLoaded(peer->id)) {
Auth().data().setPinnedDialog(history, false);
removeDialog(history);
if (peer->isMegagroup() && peer->asChannel()->mgInfo->migrateFromPtr) {
if (auto migrated = App::historyLoaded(peer->asChannel()->mgInfo->migrateFromPtr->id)) {
if (migrated->lastMsg) { // return initial dialog
migrated->setLastMessage(migrated->lastMsg);
} else {
checkPeerHistory(migrated->peer);
if (const auto channel = peer->asMegagroup()) {
channel->addFlags(MTPDchannel::Flag::f_left);
if (const auto from = channel->mgInfo->migrateFromPtr) {
if (const auto migrated = App::historyLoaded(from)) {
migrated->updateChatListExistence();
}
}
}
@ -1059,7 +1064,12 @@ void MainWidget::deleteConversation(
}
if (deleteHistory) {
DeleteHistoryRequest request = { peer, false };
MTP::send(MTPmessages_DeleteHistory(MTP_flags(0), peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, request));
MTP::send(
MTPmessages_DeleteHistory(
MTP_flags(0),
peer->input,
MTP_int(0)),
rpcDone(&MainWidget::deleteHistoryPart, request));
}
}
@ -1068,47 +1078,6 @@ void MainWidget::deleteAndExit(ChatData *chat) {
MTP::send(MTPmessages_DeleteChatUser(chat->inputChat, App::self()->inputUser), rpcDone(&MainWidget::deleteHistoryAfterLeave, peer), rpcFail(&MainWidget::leaveChatFailed, peer));
}
void MainWidget::deleteAllFromUser(ChannelData *channel, UserData *from) {
Assert(channel != nullptr && from != nullptr);
QVector<MsgId> toDestroy;
if (auto history = App::historyLoaded(channel->id)) {
for (const auto &block : history->blocks) {
for (const auto &message : block->messages) {
const auto item = message->data();
if (item->from() == from && item->canDelete()) {
toDestroy.push_back(item->id);
}
}
}
for_const (auto &msgId, toDestroy) {
if (auto item = App::histItemById(peerToChannel(channel->id), msgId)) {
item->destroy();
}
}
}
MTP::send(
MTPchannels_DeleteUserHistory(
channel->inputChannel,
from->inputUser),
rpcDone(&MainWidget::deleteAllFromUserPart, { channel, from }));
Auth().data().sendHistoryChangeNotifications();
}
void MainWidget::deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result) {
auto &d = result.c_messages_affectedHistory();
params.channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v);
auto offset = d.voffset.v;
if (offset > 0) {
MTP::send(MTPchannels_DeleteUserHistory(params.channel->inputChannel, params.from->inputUser), rpcDone(&MainWidget::deleteAllFromUserPart, params));
} else if (auto h = App::historyLoaded(params.channel)) {
if (!h->lastMsg) {
checkPeerHistory(params.channel);
}
}
}
void MainWidget::addParticipants(
not_null<PeerData*> chatOrChannel,
const std::vector<not_null<UserData*>> &users) {
@ -1194,99 +1163,6 @@ bool MainWidget::addParticipantsFail(
return false;
}
void MainWidget::checkPeerHistory(PeerData *peer) {
auto offsetId = 0;
auto offsetDate = 0;
auto addOffset = 0;
auto limit = 1;
auto maxId = 0;
auto minId = 0;
auto historyHash = 0;
MTP::send(
MTPmessages_GetHistory(
peer->input,
MTP_int(offsetId),
MTP_int(offsetDate),
MTP_int(addOffset),
MTP_int(limit),
MTP_int(maxId),
MTP_int(minId),
MTP_int(historyHash)),
rpcDone(&MainWidget::checkedHistory, peer));
}
void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &result) {
const QVector<MTPMessage> *v = 0;
switch (result.type()) {
case mtpc_messages_messages: {
auto &d(result.c_messages_messages());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.v;
} break;
case mtpc_messages_messagesSlice: {
auto &d(result.c_messages_messagesSlice());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.v;
} break;
case mtpc_messages_channelMessages: {
auto &d(result.c_messages_channelMessages());
if (peer && peer->isChannel()) {
peer->asChannel()->ptsReceived(d.vpts.v);
} else {
LOG(("API Error: received messages.channelMessages when no channel was passed! (MainWidget::checkedHistory)"));
}
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.v;
} break;
case mtpc_messages_messagesNotModified: {
LOG(("API Error: received messages.messagesNotModified! (MainWidget::checkedHistory)"));
} break;
}
if (!v || v->isEmpty()) {
if (peer->isChat() && !peer->asChat()->haveLeft()) {
if (const auto history = App::historyLoaded(peer->id)) {
Local::addSavedPeer(peer, history->chatsListDate());
}
} else if (const auto channel = peer->asChannel()) {
if (channel->inviter > 0 && channel->amIn()) {
if (const auto from = App::userLoaded(channel->inviter)) {
const auto history = App::history(peer->id);
history->clear(true);
history->addNewerSlice(QVector<MTPMessage>());
history->asChannelHistory()->insertJoinedMessage(true);
}
}
} else if (const auto history = App::historyLoaded(peer->id)) {
deleteConversation(history->peer, false);
}
} else {
const auto history = App::history(peer->id);
if (!history->lastMsg) {
history->addNewMessage((*v)[0], NewMessageLast);
}
if (!history->chatsListDate().isNull() && history->loadedAtBottom()) {
if (const auto channel = peer->asChannel()) {
if (channel->inviter > 0
&& history->chatsListDate() <= channel->inviteDate
&& channel->amIn()) {
if (const auto from = App::userLoaded(channel->inviter)) {
history->asChannelHistory()->insertJoinedMessage(true);
}
}
}
}
history->updateChatListExistence();
}
Auth().data().sendHistoryChangeNotifications();
}
bool MainWidget::sendMessageFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
@ -1562,9 +1438,9 @@ void MainWidget::messagesAffected(
ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
}
if (auto h = App::historyLoaded(peer ? peer->id : 0)) {
if (!h->lastMsg) {
checkPeerHistory(peer);
if (const auto history = App::historyLoaded(peer)) {
if (!history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history);
}
}
}
@ -3473,7 +3349,9 @@ void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) {
}
}
void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) {
void MainWidget::gotChannelDifference(
ChannelData *channel,
const MTPupdates_ChannelDifference &diff) {
_channelFailDifferenceTimeout.remove(channel);
int32 timeout = 0;
@ -3491,28 +3369,28 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
auto h = App::historyLoaded(channel->id);
if (h) {
h->setNotLoadedAtBottom();
auto history = App::historyLoaded(channel->id);
if (history) {
history->setNotLoadedAtBottom();
}
App::feedMsgs(d.vmessages, NewMessageLast);
if (h) {
if (auto item = App::histItemById(peerToChannel(channel->id), d.vtop_message.v)) {
h->setLastMessage(item);
}
if (d.vunread_count.v >= h->unreadCount()) {
h->setUnreadCount(d.vunread_count.v);
h->inboxReadBefore = d.vread_inbox_max_id.v + 1;
}
h->setUnreadMentionsCount(d.vunread_mentions_count.v);
if (history) {
history->applyDialogFields(
d.vunread_count.v,
d.vread_inbox_max_id.v,
d.vread_outbox_max_id.v);
history->applyDialogTopMessage(d.vtop_message.v);
history->setUnreadMentionsCount(d.vunread_mentions_count.v);
if (_history->peer() == channel) {
_history->updateHistoryDownVisibility();
_history->preloadHistoryIfNeeded();
}
h->asChannelHistory()->getRangeDifference();
Auth().api().requestChannelRangeDifference(history);
}
if (d.has_timeout()) timeout = d.vtimeout.v;
if (d.has_timeout()) {
timeout = d.vtimeout.v;
}
isFinal = d.is_final();
channel->ptsInit(d.vpts.v);
} break;
@ -3520,39 +3398,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
case mtpc_updates_channelDifference: {
auto &d = diff.c_updates_channelDifference();
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
_handlingChannelDifference = true;
feedMessageIds(d.vother_updates);
// feed messages and groups, copy from App::feedMsgs
auto h = App::history(channel->id);
auto &vmsgs = d.vnew_messages.v;
auto indices = base::flat_map<uint64, int>();
for (auto i = 0, l = vmsgs.size(); i != l; ++i) {
const auto &msg = vmsgs[i];
if (msg.type() == mtpc_message) {
const auto &data = msg.c_message();
if (App::checkEntitiesAndViewsUpdate(data)) { // new message, index my forwarded messages to links _overview, already in blocks
LOG(("Skipping message, because it is already in blocks!"));
continue;
}
}
const auto msgId = idFromMessage(msg);
indices.emplace((uint64(uint32(msgId)) << 32) | uint64(i), i);
}
for (const auto [position, index] : indices) {
const auto &msg = vmsgs[index];
if (channel->id != peerFromMessage(msg)) {
LOG(("API Error: message with invalid peer returned in channelDifference, channelId: %1, peer: %2").arg(peerToChannel(channel->id)).arg(peerFromMessage(msg)));
continue; // wtf
}
h->addNewMessage(msg, NewMessageUnread);
}
feedUpdateVector(d.vother_updates, true);
_handlingChannelDifference = false;
feedChannelDifference(d);
if (d.has_timeout()) timeout = d.vtimeout.v;
isFinal = d.is_final();
@ -3570,55 +3416,16 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
}
}
void MainWidget::gotRangeDifference(
ChannelData *channel,
const MTPupdates_ChannelDifference &diff) {
int32 nextRequestPts = 0;
bool isFinal = true;
switch (diff.type()) {
case mtpc_updates_channelDifferenceEmpty: {
auto &d = diff.c_updates_channelDifferenceEmpty();
nextRequestPts = d.vpts.v;
isFinal = d.is_final();
} break;
void MainWidget::feedChannelDifference(
const MTPDupdates_channelDifference &data) {
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
case mtpc_updates_channelDifferenceTooLong: {
auto &d = diff.c_updates_channelDifferenceTooLong();
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
nextRequestPts = d.vpts.v;
isFinal = d.is_final();
} break;
case mtpc_updates_channelDifference: {
auto &d = diff.c_updates_channelDifference();
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
_handlingChannelDifference = true;
feedMessageIds(d.vother_updates);
App::feedMsgs(d.vnew_messages, NewMessageUnread);
feedUpdateVector(d.vother_updates, true);
_handlingChannelDifference = false;
nextRequestPts = d.vpts.v;
isFinal = d.is_final();
} break;
}
if (!isFinal) {
if (const auto history = App::historyLoaded(channel->id)) {
MTP_LOG(0, ("getChannelDifference { "
"good - after not final channelDifference was received, "
"validating history part }%1"
).arg(cTestMode() ? " TESTMODE" : ""));
history->asChannelHistory()->getRangeDifferenceNext(
nextRequestPts);
}
}
_handlingChannelDifference = true;
feedMessageIds(data.vother_updates);
App::feedMsgs(data.vnew_messages, NewMessageUnread);
feedUpdateVector(data.vother_updates, true);
_handlingChannelDifference = false;
}
bool MainWidget::failChannelDifference(ChannelData *channel, const RPCError &error) {
@ -3985,12 +3792,12 @@ void MainWidget::onSelfParticipantUpdated(ChannelData *channel) {
history = App::history(channel);
}
if (history->isEmpty()) {
checkPeerHistory(channel);
Auth().api().requestDialogEntry(history);
} else {
history->asChannelHistory()->checkJoinedMessage(true);
history->checkJoinedMessage(true);
}
} else if (history) {
history->asChannelHistory()->checkJoinedMessage();
history->checkJoinedMessage();
}
Auth().data().sendHistoryChangeNotifications();
}
@ -4768,10 +4575,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
const auto existing = App::histItemById(channel, newId);
if (existing && !local->mainView()) {
const auto history = local->history();
const auto wasLast = (history->lastMsg == local);
local->destroy();
if (wasLast && !history->lastMsg) {
checkPeerHistory(history->peer);
if (!history->lastMessageKnown()) {
Auth().api().requestDialogEntry(history);
}
} else {
if (existing) {
@ -5211,7 +5017,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannel: {
auto &d = update.c_updateChannel();
if (auto channel = App::channelLoaded(d.vchannel_id.v)) {
if (const auto channel = App::channelLoaded(d.vchannel_id.v)) {
channel->inviter = 0;
if (!channel->amIn()) {
if (const auto history = App::historyLoaded(channel->id)) {

View File

@ -215,9 +215,6 @@ public:
not_null<ChannelData*> channel,
const RPCError &e); // for multi invite in channels
void checkPeerHistory(PeerData *peer);
void checkedHistory(PeerData *peer, const MTPmessages_Messages &result);
bool sendMessageFail(const RPCError &error);
Dialogs::IndexedList *contactsList();
@ -288,8 +285,8 @@ public:
void scheduleViewIncrement(HistoryItem *item);
void gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff);
void onSelfParticipantUpdated(ChannelData *channel);
void feedChannelDifference(const MTPDupdates_channelDifference &data);
// Mayde public for ApiWrap, while it is still here.
// Better would be for this to be moved to ApiWrap.
@ -478,7 +475,6 @@ private:
void feedUpdate(const MTPUpdate &update);
void deleteHistoryPart(DeleteHistoryRequest request, const MTPmessages_AffectedHistory &result);
void deleteAllFromUserPart(DeleteAllFromUserParams params, const MTPmessages_AffectedHistory &result);
void updateReceived(const mtpPrime *from, const mtpPrime *end);
bool updateFail(const RPCError &e);

View File

@ -593,7 +593,15 @@ void PeerMenuAddChannelMembers(not_null<ChannelData*> channel) {
}
void ToggleChannelGrouping(not_null<ChannelData*> channel, bool group) {
Auth().api().toggleChannelGrouping(channel, group);
const auto callback = [=] {
Ui::Toast::Show(lang(group
? lng_feed_channel_added
: lng_feed_channel_removed));
};
Auth().api().toggleChannelGrouping(
channel,
group,
callback);
}
base::lambda<void()> ClearHistoryHandler(not_null<PeerData*> peer) {