diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 704633ec2..582422b19 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1797,7 +1797,7 @@ void ApiWrap::requestSelfParticipant(not_null channel) { channel->inviteDate = inviteDate; if (const auto history = _session->data().historyLoaded(channel)) { if (history->lastMessageKnown()) { - history->checkJoinedMessage(true); + history->checkLocalMessages(); history->owner().sendHistoryChangeNotifications(); } else { requestDialogEntry(history); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 6b22c5a18..a1a46b96a 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -131,6 +131,9 @@ void History::setHasPendingResizedItems() { } void History::itemRemoved(not_null item) { + if (item == _joinedMessage) { + _joinedMessage = nullptr; + } item->removeMainView(); if (lastMessage() == item) { _lastMessage = std::nullopt; @@ -142,6 +145,9 @@ void History::itemRemoved(not_null item) { } checkChatListMessageRemoved(item); itemVanished(item); + if (IsClientMsgId(item->id)) { + unregisterLocalMessage(item); + } if (const auto chat = peer->asChat()) { if (const auto to = chat->getMigrateToChannel()) { if (const auto history = owner().historyLoaded(to)) { @@ -679,7 +685,7 @@ void History::checkForLoadedAtTop(not_null added) { } else if (peer->isChannel()) { if (added->id == 1) { _loadedAtTop = true; - checkJoinedMessage(); + checkLocalMessages(); addEdgesToSharedMedia(); } } @@ -1208,9 +1214,8 @@ void History::clearSendAction(not_null from) { void History::mainViewRemoved( not_null block, not_null view) { - if (_joinedMessage == view->data()) { - _joinedMessage = nullptr; - } + Expects(_joinedMessage != view->data()); + if (_firstUnreadView == view) { getNextFirstUnreadMessage(); } @@ -1253,6 +1258,14 @@ void History::newItemAdded(not_null item) { } } +void History::registerLocalMessage(not_null item) { + _localMessages.emplace(item); +} + +void History::unregisterLocalMessage(not_null item) { + _localMessages.remove(item); +} + HistoryBlock *History::prepareBlockForAddingItem() { if (isBuildingFrontBlock()) { if (_buildingFrontBlock->block) { @@ -1318,7 +1331,7 @@ void History::addEdgesToSharedMedia() { void History::addOlderSlice(const QVector &slice) { if (slice.isEmpty()) { _loadedAtTop = true; - checkJoinedMessage(); + checkLocalMessages(); return; } @@ -1340,7 +1353,7 @@ void History::addOlderSlice(const QVector &slice) { addEdgesToSharedMedia(); } - checkJoinedMessage(); + checkLocalMessages(); checkLastMessage(); } @@ -1372,7 +1385,7 @@ void History::addNewerSlice(const QVector &slice) { checkAddAllToUnreadMentions(); } - checkJoinedMessage(); + checkLocalMessages(); checkLastMessage(); } @@ -2195,7 +2208,8 @@ void History::getReadyFor(MsgId msgId) { migrated->clear(ClearType::Unload); } } - if (msgId == ShowAtTheEndMsgId) { + if ((msgId == ShowAtTheEndMsgId) + || (msgId == ShowAtUnreadMsgId && !unreadCount())) { _loadedAtBottom = true; } } @@ -2562,7 +2576,7 @@ void History::dialogEntryApplied() { if (const auto from = owner().userLoaded(inviter)) { clear(ClearType::Unload); addNewerSlice(QVector()); - insertJoinedMessage(true); + insertJoinedMessage(); } } } else { @@ -2578,7 +2592,7 @@ void History::dialogEntryApplied() { && chatListTimeId() <= channel->inviteDate && channel->amIn()) { if (const auto from = owner().userLoaded(inviter)) { - insertJoinedMessage(true); + insertJoinedMessage(); } } } @@ -2791,7 +2805,7 @@ MsgRange History::rangeForDifferenceRequest() const { return MsgRange(); } -HistoryService *History::insertJoinedMessage(bool unread) { +HistoryService *History::insertJoinedMessage() { if (!isChannel() || _joinedMessage || !peer->asChannel()->amIn() @@ -2807,109 +2821,79 @@ HistoryService *History::insertJoinedMessage(bool unread) { return nullptr; } - MTPDmessage::Flags flags = 0; - if (inviter->id == session().userPeerId()) { - unread = false; - //} else if (unread) { - // flags |= MTPDmessage::Flag::f_unread; + if (peer->isMegagroup() + && peer->migrateFrom() + && !blocks.empty() + && blocks.front()->messages.front()->data()->id == 1) { + peer->asChannel()->mgInfo->joinedMessageFound = true; + return nullptr; } + const auto flags = MTPDmessage::Flags(); const auto inviteDate = peer->asChannel()->inviteDate; + _joinedMessage = GenerateJoinedMessage(this, inviteDate, inviter, flags); + insertLocalMessage(_joinedMessage); + return _joinedMessage; +} + +void History::insertLocalMessage(not_null item) { + Expects(item->mainView() == nullptr); + if (isEmpty()) { - _joinedMessage = GenerateJoinedMessage( - this, - inviteDate, - inviter, - flags); - addNewItem(_joinedMessage, unread); - return _joinedMessage; + addNewItem(item, false); + return; } + const auto itemDate = item->date(); for (auto blockIndex = blocks.size(); blockIndex > 0;) { const auto &block = blocks[--blockIndex]; for (auto itemIndex = block->messages.size(); itemIndex > 0;) { - const auto item = block->messages[--itemIndex]->data(); - - // 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) { + if (block->messages[--itemIndex]->data()->date() <= itemDate) { ++itemIndex; - _joinedMessage = GenerateJoinedMessage( - this, - inviteDate, - inviter, - flags); - addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex); + addNewInTheMiddle(item, blockIndex, itemIndex); const auto lastDate = chatListTimeId(); - if (!lastDate || inviteDate >= lastDate) { - setLastMessage(_joinedMessage); - if (unread) { - newItemAdded(_joinedMessage); - } + if (!lastDate || itemDate >= lastDate) { + setLastMessage(item); } - return _joinedMessage; + return; } } } startBuildingFrontBlock(); - _joinedMessage = GenerateJoinedMessage( - this, - inviteDate, - inviter, - flags); - addItemToBlock(_joinedMessage); + addItemToBlock(item); finishBuildingFrontBlock(); - - return _joinedMessage; } -void History::checkJoinedMessage(bool createUnread) { - if (!isChannel() || _joinedMessage || peer->asChannel()->inviter <= 0) { +void History::checkLocalMessages() { + if (isEmpty() && (!loadedAtTop() || !loadedAtBottom())) { return; } - if (isEmpty()) { - if (loadedAtTop() && loadedAtBottom()) { - if (insertJoinedMessage(createUnread)) { - if (_joinedMessage->mainView()) { - setLastMessage(_joinedMessage); - } - } - return; + const auto firstDate = loadedAtTop() + ? 0 + : blocks.front()->messages.front()->data()->date(); + const auto lastDate = loadedAtBottom() + ? std::numeric_limits::max() + : blocks.back()->messages.back()->data()->date(); + const auto goodDate = [&](TimeId date) { + return (date >= firstDate && date < lastDate); + }; + for (const auto &item : _localMessages) { + if (!item->mainView() && goodDate(item->date())) { + insertLocalMessage(item); } } - - const auto inviteDate = peer->asChannel()->inviteDate; - auto firstDate = TimeId(0); - auto lastDate = TimeId(0); - if (!blocks.empty()) { - 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); - } - } + if (isChannel() + && !_joinedMessage + && (peer->asChannel()->inviter > 0) + && goodDate(peer->asChannel()->inviteDate)) { + insertJoinedMessage(); } } void History::removeJoinedMessage() { if (_joinedMessage) { - base::take(_joinedMessage)->destroy(); + _joinedMessage->destroy(); } } @@ -3025,7 +3009,7 @@ QVector History::collectMessagesFromUserToDelete( void History::clear(ClearType type) { _unreadBarView = nullptr; _firstUnreadView = nullptr; - _joinedMessage = nullptr; + removeJoinedMessage(); forgetScrollState(); if (type == ClearType::Unload) { @@ -3035,6 +3019,7 @@ void History::clear(ClearType type) { _loadedAtTop = _loadedAtBottom = false; } else { _notifications.clear(); + _localMessages.clear(); owner().notifyHistoryCleared(this); if (unreadCountKnown()) { setUnreadCount(0); diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 6d3246a5d..14c852cc4 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -70,8 +70,7 @@ public: not_null migrateToOrMe() const; History *migrateFrom() const; MsgRange rangeForDifferenceRequest() const; - HistoryService *insertJoinedMessage(bool unread); - void checkJoinedMessage(bool createUnread = false); + void checkLocalMessages(); void removeJoinedMessage(); bool isEmpty() const; @@ -154,6 +153,9 @@ public: void newItemAdded(not_null item); + void registerLocalMessage(not_null item); + void unregisterLocalMessage(not_null item); + MsgId readInbox(); void applyInboxReadUpdate( FolderId folderId, @@ -470,6 +472,9 @@ private: void createLocalDraftFromCloud(); + HistoryService *insertJoinedMessage(); + void insertLocalMessage(not_null item); + void setFolderPointer(Data::Folder *folder); Flags _flags = 0; @@ -490,6 +495,7 @@ private: std::optional _unreadMentionsCount; base::flat_set _unreadMentions; std::optional _lastMessage; + base::flat_set> _localMessages; // This almost always is equal to _lastMessage. The only difference is // for a group that migrated to a supergroup. Then _lastMessage can diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 3f9a1af37..fa7790a26 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -172,6 +172,9 @@ HistoryItem::HistoryItem( , _from(from ? history->owner().user(from) : history->peer) , _flags(flags) , _date(date) { + if (IsClientMsgId(id)) { + _history->registerLocalMessage(this); + } } TimeId HistoryItem::date() const { @@ -432,9 +435,14 @@ void HistoryItem::indexAsNewItem() { } void HistoryItem::setRealId(MsgId newId) { - Expects(!IsServerMsgId(id)); + Expects(_flags & MTPDmessage_ClientFlag::f_sending); + Expects(IsClientMsgId(id)); const auto oldId = std::exchange(id, newId); + _flags &= ~MTPDmessage_ClientFlag::f_sending; + if (IsServerMsgId(id)) { + _history->unregisterLocalMessage(this); + } _history->owner().notifyItemIdChange({ this, oldId }); // We don't call Notify::replyMarkupUpdated(this) and update keyboard diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index d4db3fe4b..01c6676ab 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -223,7 +223,6 @@ void FastShareMessage(not_null item) { MTP_vector(msgIds), MTP_vector(generateRandom()), peer->input); - auto callback = doneCallback; history->sendRequestId = MTP::send( request, rpcDone(base::duplicate(doneCallback)), @@ -262,7 +261,7 @@ Fn HistoryDependentItemCallback( } MTPDmessage::Flags NewMessageFlags(not_null peer) { - MTPDmessage::Flags result = 0; + MTPDmessage::Flags result = MTPDmessage_ClientFlag::f_sending | 0; if (!peer->isSelf()) { result |= MTPDmessage::Flag::f_out; //if (p->isChat() || (p->isUser() && !p->asUser()->botInfo)) { diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index da044efc3..39e6c9a61 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -787,7 +787,7 @@ HistoryService::PreparedText GenerateJoinedText( return { tr::lng_action_you_joined(tr::now) }; } -HistoryService *GenerateJoinedMessage( +not_null GenerateJoinedMessage( not_null history, TimeId inviteDate, not_null inviter, diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index ab92ce55a..3673b709f 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -150,7 +150,7 @@ private: }; -HistoryService *GenerateJoinedMessage( +not_null GenerateJoinedMessage( not_null history, TimeId inviteDate, not_null inviter, diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 86a183f84..59fb7266c 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4310,7 +4310,9 @@ void HistoryWidget::sendFileConfirmed( file->edit = isEditing; 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 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_media; if (file->to.replyTo) { @@ -5547,8 +5549,8 @@ bool HistoryWidget::sendExistingPhoto( options.generateLocal = true; session().api().sendAction(options); - uint64 randomId = rand_value(); - FullMsgId newId(_channel, clientMsgId()); + const auto randomId = rand_value(); + const auto newId = FullMsgId(_channel, clientMsgId()); auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media; auto sendFlags = MTPmessages_SendMedia::Flags(0); diff --git a/Telegram/SourceFiles/mtproto/type_utils.h b/Telegram/SourceFiles/mtproto/type_utils.h index 949fa884f..6ddc2e86a 100644 --- a/Telegram/SourceFiles/mtproto/type_utils.h +++ b/Telegram/SourceFiles/mtproto/type_utils.h @@ -41,36 +41,27 @@ enum class MTPDmessage_ClientFlag : uint32 { // 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), - - //// 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 // 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 - f_from_inline_bot = (1U << 23), + f_from_inline_bot = (1U << 27), // 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 - f_clientside_unread = (1U << 21), + f_clientside_unread = (1U << 25), // 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 - MIN_FIELD = (1U << 20), + MIN_FIELD = (1U << 23), }; DEFINE_MTP_CLIENT_FLAGS(MTPDmessage)