Track local messages, restore on history jump.

This commit is contained in:
John Preston 2019-07-17 14:41:48 +02:00
parent 04bf24288a
commit 0005e0a3ce
9 changed files with 107 additions and 116 deletions

View File

@ -1797,7 +1797,7 @@ void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) {
channel->inviteDate = inviteDate; channel->inviteDate = inviteDate;
if (const auto history = _session->data().historyLoaded(channel)) { if (const auto history = _session->data().historyLoaded(channel)) {
if (history->lastMessageKnown()) { if (history->lastMessageKnown()) {
history->checkJoinedMessage(true); history->checkLocalMessages();
history->owner().sendHistoryChangeNotifications(); history->owner().sendHistoryChangeNotifications();
} else { } else {
requestDialogEntry(history); requestDialogEntry(history);

View File

@ -131,6 +131,9 @@ void History::setHasPendingResizedItems() {
} }
void History::itemRemoved(not_null<HistoryItem*> item) { void History::itemRemoved(not_null<HistoryItem*> item) {
if (item == _joinedMessage) {
_joinedMessage = nullptr;
}
item->removeMainView(); item->removeMainView();
if (lastMessage() == item) { if (lastMessage() == item) {
_lastMessage = std::nullopt; _lastMessage = std::nullopt;
@ -142,6 +145,9 @@ void History::itemRemoved(not_null<HistoryItem*> item) {
} }
checkChatListMessageRemoved(item); checkChatListMessageRemoved(item);
itemVanished(item); itemVanished(item);
if (IsClientMsgId(item->id)) {
unregisterLocalMessage(item);
}
if (const auto chat = peer->asChat()) { if (const auto chat = peer->asChat()) {
if (const auto to = chat->getMigrateToChannel()) { if (const auto to = chat->getMigrateToChannel()) {
if (const auto history = owner().historyLoaded(to)) { if (const auto history = owner().historyLoaded(to)) {
@ -679,7 +685,7 @@ void History::checkForLoadedAtTop(not_null<HistoryItem*> added) {
} else if (peer->isChannel()) { } else if (peer->isChannel()) {
if (added->id == 1) { if (added->id == 1) {
_loadedAtTop = true; _loadedAtTop = true;
checkJoinedMessage(); checkLocalMessages();
addEdgesToSharedMedia(); addEdgesToSharedMedia();
} }
} }
@ -1208,9 +1214,8 @@ void History::clearSendAction(not_null<UserData*> from) {
void History::mainViewRemoved( void History::mainViewRemoved(
not_null<HistoryBlock*> block, not_null<HistoryBlock*> block,
not_null<HistoryView::Element*> view) { not_null<HistoryView::Element*> view) {
if (_joinedMessage == view->data()) { Expects(_joinedMessage != view->data());
_joinedMessage = nullptr;
}
if (_firstUnreadView == view) { if (_firstUnreadView == view) {
getNextFirstUnreadMessage(); getNextFirstUnreadMessage();
} }
@ -1253,6 +1258,14 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
} }
} }
void History::registerLocalMessage(not_null<HistoryItem*> item) {
_localMessages.emplace(item);
}
void History::unregisterLocalMessage(not_null<HistoryItem*> item) {
_localMessages.remove(item);
}
HistoryBlock *History::prepareBlockForAddingItem() { HistoryBlock *History::prepareBlockForAddingItem() {
if (isBuildingFrontBlock()) { if (isBuildingFrontBlock()) {
if (_buildingFrontBlock->block) { if (_buildingFrontBlock->block) {
@ -1318,7 +1331,7 @@ void History::addEdgesToSharedMedia() {
void History::addOlderSlice(const QVector<MTPMessage> &slice) { void History::addOlderSlice(const QVector<MTPMessage> &slice) {
if (slice.isEmpty()) { if (slice.isEmpty()) {
_loadedAtTop = true; _loadedAtTop = true;
checkJoinedMessage(); checkLocalMessages();
return; return;
} }
@ -1340,7 +1353,7 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
addEdgesToSharedMedia(); addEdgesToSharedMedia();
} }
checkJoinedMessage(); checkLocalMessages();
checkLastMessage(); checkLastMessage();
} }
@ -1372,7 +1385,7 @@ void History::addNewerSlice(const QVector<MTPMessage> &slice) {
checkAddAllToUnreadMentions(); checkAddAllToUnreadMentions();
} }
checkJoinedMessage(); checkLocalMessages();
checkLastMessage(); checkLastMessage();
} }
@ -2195,7 +2208,8 @@ void History::getReadyFor(MsgId msgId) {
migrated->clear(ClearType::Unload); migrated->clear(ClearType::Unload);
} }
} }
if (msgId == ShowAtTheEndMsgId) { if ((msgId == ShowAtTheEndMsgId)
|| (msgId == ShowAtUnreadMsgId && !unreadCount())) {
_loadedAtBottom = true; _loadedAtBottom = true;
} }
} }
@ -2562,7 +2576,7 @@ void History::dialogEntryApplied() {
if (const auto from = owner().userLoaded(inviter)) { if (const auto from = owner().userLoaded(inviter)) {
clear(ClearType::Unload); clear(ClearType::Unload);
addNewerSlice(QVector<MTPMessage>()); addNewerSlice(QVector<MTPMessage>());
insertJoinedMessage(true); insertJoinedMessage();
} }
} }
} else { } else {
@ -2578,7 +2592,7 @@ void History::dialogEntryApplied() {
&& chatListTimeId() <= channel->inviteDate && chatListTimeId() <= channel->inviteDate
&& channel->amIn()) { && channel->amIn()) {
if (const auto from = owner().userLoaded(inviter)) { if (const auto from = owner().userLoaded(inviter)) {
insertJoinedMessage(true); insertJoinedMessage();
} }
} }
} }
@ -2791,7 +2805,7 @@ MsgRange History::rangeForDifferenceRequest() const {
return MsgRange(); return MsgRange();
} }
HistoryService *History::insertJoinedMessage(bool unread) { HistoryService *History::insertJoinedMessage() {
if (!isChannel() if (!isChannel()
|| _joinedMessage || _joinedMessage
|| !peer->asChannel()->amIn() || !peer->asChannel()->amIn()
@ -2807,109 +2821,79 @@ HistoryService *History::insertJoinedMessage(bool unread) {
return nullptr; return nullptr;
} }
MTPDmessage::Flags flags = 0; if (peer->isMegagroup()
if (inviter->id == session().userPeerId()) { && peer->migrateFrom()
unread = false; && !blocks.empty()
//} else if (unread) { && blocks.front()->messages.front()->data()->id == 1) {
// flags |= MTPDmessage::Flag::f_unread; peer->asChannel()->mgInfo->joinedMessageFound = true;
return nullptr;
} }
const auto flags = MTPDmessage::Flags();
const auto inviteDate = peer->asChannel()->inviteDate; const auto inviteDate = peer->asChannel()->inviteDate;
_joinedMessage = GenerateJoinedMessage(this, inviteDate, inviter, flags);
insertLocalMessage(_joinedMessage);
return _joinedMessage;
}
void History::insertLocalMessage(not_null<HistoryItem*> item) {
Expects(item->mainView() == nullptr);
if (isEmpty()) { if (isEmpty()) {
_joinedMessage = GenerateJoinedMessage( addNewItem(item, false);
this, return;
inviteDate,
inviter,
flags);
addNewItem(_joinedMessage, unread);
return _joinedMessage;
} }
const auto itemDate = item->date();
for (auto blockIndex = blocks.size(); blockIndex > 0;) { for (auto blockIndex = blocks.size(); blockIndex > 0;) {
const auto &block = blocks[--blockIndex]; const auto &block = blocks[--blockIndex];
for (auto itemIndex = block->messages.size(); itemIndex > 0;) { for (auto itemIndex = block->messages.size(); itemIndex > 0;) {
const auto item = block->messages[--itemIndex]->data(); if (block->messages[--itemIndex]->data()->date() <= itemDate) {
// 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->id == 1)
&& peer->isMegagroup()
&& peer->migrateFrom()) {
peer->asChannel()->mgInfo->joinedMessageFound = true;
return nullptr;
}
if (item->date() <= inviteDate) {
++itemIndex; ++itemIndex;
_joinedMessage = GenerateJoinedMessage( addNewInTheMiddle(item, blockIndex, itemIndex);
this,
inviteDate,
inviter,
flags);
addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex);
const auto lastDate = chatListTimeId(); const auto lastDate = chatListTimeId();
if (!lastDate || inviteDate >= lastDate) { if (!lastDate || itemDate >= lastDate) {
setLastMessage(_joinedMessage); setLastMessage(item);
if (unread) {
newItemAdded(_joinedMessage);
}
} }
return _joinedMessage; return;
} }
} }
} }
startBuildingFrontBlock(); startBuildingFrontBlock();
_joinedMessage = GenerateJoinedMessage( addItemToBlock(item);
this,
inviteDate,
inviter,
flags);
addItemToBlock(_joinedMessage);
finishBuildingFrontBlock(); finishBuildingFrontBlock();
return _joinedMessage;
} }
void History::checkJoinedMessage(bool createUnread) { void History::checkLocalMessages() {
if (!isChannel() || _joinedMessage || peer->asChannel()->inviter <= 0) { if (isEmpty() && (!loadedAtTop() || !loadedAtBottom())) {
return; return;
} }
if (isEmpty()) { const auto firstDate = loadedAtTop()
if (loadedAtTop() && loadedAtBottom()) { ? 0
if (insertJoinedMessage(createUnread)) { : blocks.front()->messages.front()->data()->date();
if (_joinedMessage->mainView()) { const auto lastDate = loadedAtBottom()
setLastMessage(_joinedMessage); ? std::numeric_limits<TimeId>::max()
} : blocks.back()->messages.back()->data()->date();
} const auto goodDate = [&](TimeId date) {
return; return (date >= firstDate && date < lastDate);
};
for (const auto &item : _localMessages) {
if (!item->mainView() && goodDate(item->date())) {
insertLocalMessage(item);
} }
} }
if (isChannel()
const auto inviteDate = peer->asChannel()->inviteDate; && !_joinedMessage
auto firstDate = TimeId(0); && (peer->asChannel()->inviter > 0)
auto lastDate = TimeId(0); && goodDate(peer->asChannel()->inviteDate)) {
if (!blocks.empty()) { insertJoinedMessage();
firstDate = blocks.front()->messages.front()->data()->date();
lastDate = blocks.back()->messages.back()->data()->date();
}
if (firstDate
&& lastDate
&& (firstDate <= inviteDate || loadedAtTop())
&& (lastDate > inviteDate || loadedAtBottom())) {
const auto willBeLastMsg = (inviteDate >= lastDate);
if (insertJoinedMessage(createUnread && willBeLastMsg)
&& willBeLastMsg) {
if (_joinedMessage->mainView()) {
setLastMessage(_joinedMessage);
}
}
} }
} }
void History::removeJoinedMessage() { void History::removeJoinedMessage() {
if (_joinedMessage) { if (_joinedMessage) {
base::take(_joinedMessage)->destroy(); _joinedMessage->destroy();
} }
} }
@ -3025,7 +3009,7 @@ QVector<MsgId> History::collectMessagesFromUserToDelete(
void History::clear(ClearType type) { void History::clear(ClearType type) {
_unreadBarView = nullptr; _unreadBarView = nullptr;
_firstUnreadView = nullptr; _firstUnreadView = nullptr;
_joinedMessage = nullptr; removeJoinedMessage();
forgetScrollState(); forgetScrollState();
if (type == ClearType::Unload) { if (type == ClearType::Unload) {
@ -3035,6 +3019,7 @@ void History::clear(ClearType type) {
_loadedAtTop = _loadedAtBottom = false; _loadedAtTop = _loadedAtBottom = false;
} else { } else {
_notifications.clear(); _notifications.clear();
_localMessages.clear();
owner().notifyHistoryCleared(this); owner().notifyHistoryCleared(this);
if (unreadCountKnown()) { if (unreadCountKnown()) {
setUnreadCount(0); setUnreadCount(0);

View File

@ -70,8 +70,7 @@ public:
not_null<History*> migrateToOrMe() const; not_null<History*> migrateToOrMe() const;
History *migrateFrom() const; History *migrateFrom() const;
MsgRange rangeForDifferenceRequest() const; MsgRange rangeForDifferenceRequest() const;
HistoryService *insertJoinedMessage(bool unread); void checkLocalMessages();
void checkJoinedMessage(bool createUnread = false);
void removeJoinedMessage(); void removeJoinedMessage();
bool isEmpty() const; bool isEmpty() const;
@ -154,6 +153,9 @@ public:
void newItemAdded(not_null<HistoryItem*> item); void newItemAdded(not_null<HistoryItem*> item);
void registerLocalMessage(not_null<HistoryItem*> item);
void unregisterLocalMessage(not_null<HistoryItem*> item);
MsgId readInbox(); MsgId readInbox();
void applyInboxReadUpdate( void applyInboxReadUpdate(
FolderId folderId, FolderId folderId,
@ -470,6 +472,9 @@ private:
void createLocalDraftFromCloud(); void createLocalDraftFromCloud();
HistoryService *insertJoinedMessage();
void insertLocalMessage(not_null<HistoryItem*> item);
void setFolderPointer(Data::Folder *folder); void setFolderPointer(Data::Folder *folder);
Flags _flags = 0; Flags _flags = 0;
@ -490,6 +495,7 @@ private:
std::optional<int> _unreadMentionsCount; std::optional<int> _unreadMentionsCount;
base::flat_set<MsgId> _unreadMentions; base::flat_set<MsgId> _unreadMentions;
std::optional<HistoryItem*> _lastMessage; std::optional<HistoryItem*> _lastMessage;
base::flat_set<not_null<HistoryItem*>> _localMessages;
// This almost always is equal to _lastMessage. The only difference is // This almost always is equal to _lastMessage. The only difference is
// for a group that migrated to a supergroup. Then _lastMessage can // for a group that migrated to a supergroup. Then _lastMessage can

View File

@ -172,6 +172,9 @@ HistoryItem::HistoryItem(
, _from(from ? history->owner().user(from) : history->peer) , _from(from ? history->owner().user(from) : history->peer)
, _flags(flags) , _flags(flags)
, _date(date) { , _date(date) {
if (IsClientMsgId(id)) {
_history->registerLocalMessage(this);
}
} }
TimeId HistoryItem::date() const { TimeId HistoryItem::date() const {
@ -432,9 +435,14 @@ void HistoryItem::indexAsNewItem() {
} }
void HistoryItem::setRealId(MsgId newId) { void HistoryItem::setRealId(MsgId newId) {
Expects(!IsServerMsgId(id)); Expects(_flags & MTPDmessage_ClientFlag::f_sending);
Expects(IsClientMsgId(id));
const auto oldId = std::exchange(id, newId); const auto oldId = std::exchange(id, newId);
_flags &= ~MTPDmessage_ClientFlag::f_sending;
if (IsServerMsgId(id)) {
_history->unregisterLocalMessage(this);
}
_history->owner().notifyItemIdChange({ this, oldId }); _history->owner().notifyItemIdChange({ this, oldId });
// We don't call Notify::replyMarkupUpdated(this) and update keyboard // We don't call Notify::replyMarkupUpdated(this) and update keyboard

View File

@ -223,7 +223,6 @@ void FastShareMessage(not_null<HistoryItem*> item) {
MTP_vector<MTPint>(msgIds), MTP_vector<MTPint>(msgIds),
MTP_vector<MTPlong>(generateRandom()), MTP_vector<MTPlong>(generateRandom()),
peer->input); peer->input);
auto callback = doneCallback;
history->sendRequestId = MTP::send( history->sendRequestId = MTP::send(
request, request,
rpcDone(base::duplicate(doneCallback)), rpcDone(base::duplicate(doneCallback)),
@ -262,7 +261,7 @@ Fn<void(ChannelData*, MsgId)> HistoryDependentItemCallback(
} }
MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer) { MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer) {
MTPDmessage::Flags result = 0; MTPDmessage::Flags result = MTPDmessage_ClientFlag::f_sending | 0;
if (!peer->isSelf()) { if (!peer->isSelf()) {
result |= MTPDmessage::Flag::f_out; result |= MTPDmessage::Flag::f_out;
//if (p->isChat() || (p->isUser() && !p->asUser()->botInfo)) { //if (p->isChat() || (p->isUser() && !p->asUser()->botInfo)) {

View File

@ -787,7 +787,7 @@ HistoryService::PreparedText GenerateJoinedText(
return { tr::lng_action_you_joined(tr::now) }; return { tr::lng_action_you_joined(tr::now) };
} }
HistoryService *GenerateJoinedMessage( not_null<HistoryService*> GenerateJoinedMessage(
not_null<History*> history, not_null<History*> history,
TimeId inviteDate, TimeId inviteDate,
not_null<UserData*> inviter, not_null<UserData*> inviter,

View File

@ -150,7 +150,7 @@ private:
}; };
HistoryService *GenerateJoinedMessage( not_null<HistoryService*> GenerateJoinedMessage(
not_null<History*> history, not_null<History*> history,
TimeId inviteDate, TimeId inviteDate,
not_null<UserData*> inviter, not_null<UserData*> inviter,

View File

@ -4310,7 +4310,9 @@ void HistoryWidget::sendFileConfirmed(
file->edit = isEditing; file->edit = isEditing;
session().uploader().upload(newId, file); session().uploader().upload(newId, file);
const auto itemToEdit = isEditing ? session().data().message(newId) : nullptr; const auto itemToEdit = isEditing
? session().data().message(newId)
: nullptr;
const auto history = session().data().history(file->to.peer); const auto history = session().data().history(file->to.peer);
const auto peer = history->peer; const auto peer = history->peer;
@ -4338,7 +4340,7 @@ void HistoryWidget::sendFileConfirmed(
} }
} }
auto flags = NewMessageFlags(peer) auto flags = (isEditing ? MTPDmessage::Flags() : NewMessageFlags(peer))
| MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_entities
| MTPDmessage::Flag::f_media; | MTPDmessage::Flag::f_media;
if (file->to.replyTo) { if (file->to.replyTo) {
@ -5547,8 +5549,8 @@ bool HistoryWidget::sendExistingPhoto(
options.generateLocal = true; options.generateLocal = true;
session().api().sendAction(options); session().api().sendAction(options);
uint64 randomId = rand_value<uint64>(); const auto randomId = rand_value<uint64>();
FullMsgId newId(_channel, clientMsgId()); const auto newId = FullMsgId(_channel, clientMsgId());
auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media; auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media;
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);

View File

@ -41,36 +41,27 @@ enum class MTPDmessage_ClientFlag : uint32 {
// message is a group / channel create or migrate service message // message is a group / channel create or migrate service message
f_is_group_essential = (1U << 29), f_is_group_essential = (1U << 29),
//// message needs initDimensions() + resize() + paint()
//f_pending_init_dimensions = (1U << 28),
//// message needs resize() + paint()
//f_pending_resize = (1U << 27),
//// message needs paint()
//f_pending_paint = (1U << 26),
//// message is attached to previous one when displaying the history
//f_attach_to_previous = (1U << 25),
// message's edited media is generated on the client // message's edited media is generated on the client
// and should not update media from server // and should not update media from server
f_is_local_update_media = (1U << 24), f_is_local_update_media = (1U << 28),
// message was sent from inline bot, need to re-set media when sent // message was sent from inline bot, need to re-set media when sent
f_from_inline_bot = (1U << 23), f_from_inline_bot = (1U << 27),
// message has a switch inline keyboard button, need to return to inline // message has a switch inline keyboard button, need to return to inline
f_has_switch_inline_button = (1U << 22), f_has_switch_inline_button = (1U << 26),
// message is generated on the client side and should be unread // message is generated on the client side and should be unread
f_clientside_unread = (1U << 21), f_clientside_unread = (1U << 25),
// message has an admin badge in supergroup // message has an admin badge in supergroup
f_has_admin_badge = (1U << 20), f_has_admin_badge = (1U << 24),
// message is an outgoing message that is being sent
f_sending = (1U << 23),
// update this when adding new client side flags // update this when adding new client side flags
MIN_FIELD = (1U << 20), MIN_FIELD = (1U << 23),
}; };
DEFINE_MTP_CLIENT_FLAGS(MTPDmessage) DEFINE_MTP_CLIENT_FLAGS(MTPDmessage)