From 39f3ae7c6bae264aaac32ff2d4cec112e552f4df Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Jun 2026 14:03:52 +0400 Subject: [PATCH] [chat] Fix some bugs in modern chat view. --- .../data/data_history_messages.cpp | 12 +- Telegram/SourceFiles/history/history.cpp | 6 + .../controls/history_view_bottom_controls.cpp | 1 + .../history_view_compose_controls.cpp | 14 +- .../history/view/history_view_about_view.cpp | 14 +- .../history/view/history_view_about_view.h | 5 + .../view/history_view_chat_section.cpp | 254 ++++++++++++++---- .../history/view/history_view_chat_section.h | 15 +- .../view/history_view_context_menu.cpp | 1 + .../history/view/history_view_list_widget.cpp | 41 ++- .../history/view/history_view_list_widget.h | 2 + Telegram/SourceFiles/mainwidget.cpp | 11 + 12 files changed, 315 insertions(+), 61 deletions(-) diff --git a/Telegram/SourceFiles/data/data_history_messages.cpp b/Telegram/SourceFiles/data/data_history_messages.cpp index a411658714..8df0486665 100644 --- a/Telegram/SourceFiles/data/data_history_messages.cpp +++ b/Telegram/SourceFiles/data/data_history_messages.cpp @@ -257,9 +257,15 @@ void AppendHistoryClientSideMessages( } } } else { - result.query = (aroundId.fullId.peer == history->peer->id) - ? aroundId.fullId.msg - : (aroundId.fullId.msg - ServerMaxMsgId); + const auto samePeer = (aroundId.fullId.peer == history->peer->id); + if (IsServerMsgId(aroundId.fullId.msg)) { + result.query = samePeer + ? aroundId.fullId.msg + : (aroundId.fullId.msg - ServerMaxMsgId); + } else { + result.query = (ServerMaxMsgId - 1); + result.target = MaxMessagePosition; + } } return result; } diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 3066e61acb..50a3667418 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -2188,6 +2188,12 @@ std::optional History::countStillUnreadLocalFromMessages( if (snapshot.skippedAfter != 0) { return std::nullopt; } + const auto coversReadTill = (snapshot.skippedBefore == 0) + || (!snapshot.messageIds.empty() + && (snapshot.messageIds.front() <= readTillId)); + if (!coversReadTill) { + return std::nullopt; + } auto result = 0; for (const auto &id : snapshot.messageIds) { if (id <= readTillId) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_bottom_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_bottom_controls.cpp index a30f3fa8dc..b03f4bc04a 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_bottom_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_bottom_controls.cpp @@ -272,6 +272,7 @@ void BottomControls::setupButtons() { refreshGiftToChannelShown(); refreshDirectMessageShown(); refreshMuteUnmuteText(); + refreshUnblockText(); } else if (_mode == BottomControlsMode::Replies && _peer && _peer->isChannel()) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 3700e8f407..757e172d23 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1349,11 +1349,13 @@ ComposeControls::ComposeControls( ) | rpl::to_empty | rpl::start_to_stream( _botKeyboardToggleClicks, _botKeyboardHide->lifetime()); + _tabbedSelectorToggle->hide(); orderControls(); updateControlsVisibility(); updateControlsGeometry(_wrap->size()); } else if (_botKeyboardHide && !has) { _botKeyboardHide = nullptr; + _tabbedSelectorToggle->show(); updateControlsGeometry(_wrap->size()); } }, _wrap->lifetime()); @@ -4688,7 +4690,9 @@ void ComposeControls::updateControlsGeometry(QSize size) { right += _editStars->width(); } _tabbedSelectorToggle->moveToRight(right, buttonsTop); - right += _tabbedSelectorToggle->width(); + if (!_tabbedSelectorToggle->isHidden()) { + right += _tabbedSelectorToggle->width(); + } if (_like) { using Type = Controls::WriteRestrictionType; if (_writeRestriction.current().type == Type::PremiumRequired) { @@ -5041,8 +5045,6 @@ bool ComposeControls::updateBotCommandShown() { return !peer->asChat()->botCommands().empty(); } else if (peer->isMegagroup()) { return !peer->asChannel()->mgInfo->botCommands().empty(); - } else if (peer->isUser()) { - return peer->asUser()->isBot(); } return false; }(); @@ -5769,6 +5771,12 @@ void ComposeControls::initForwardProcess() { && sublist->sublistPeer()->id == _monoforumPeerId) { updateForwarding(); } + } else if (const auto history = update.entry->asHistory()) { + if (history == _history + && !_topicRootId + && !_monoforumPeerId) { + updateForwarding(); + } } }, _wrap->lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_about_view.cpp b/Telegram/SourceFiles/history/view/history_view_about_view.cpp index 6649fcb597..a4f0e62201 100644 --- a/Telegram/SourceFiles/history/view/history_view_about_view.cpp +++ b/Telegram/SourceFiles/history/view/history_view_about_view.cpp @@ -719,6 +719,16 @@ bool AboutView::aboveHistory() const { && (!_history->isEmpty() || _history->lastMessage())); } +void AboutView::setDisplayedEmptyOverride(Fn value) { + _displayedEmptyOverride = std::move(value); +} + +bool AboutView::displayedEmpty() const { + return _displayedEmptyOverride + ? _displayedEmptyOverride() + : _history->isDisplayedEmpty(); +} + bool AboutView::refresh() { if (_history->peer->isVerifyCodes()) { if (_item) { @@ -742,7 +752,7 @@ bool AboutView::refresh() { loadCommonGroups(); setItem(makeNewPeerInfo(user), nullptr); return true; - } else if (user && !user->isSelf() && _history->isDisplayedEmpty()) { + } else if (user && !user->isSelf() && displayedEmpty()) { if (_item) { return false; } else if (user->requiresPremiumToWrite() @@ -758,7 +768,7 @@ bool AboutView::refresh() { makeIntro(user); } return true; - } else if (monoforum && _history->isDisplayedEmpty()) { + } else if (monoforum && displayedEmpty()) { if (_item) { return false; } diff --git a/Telegram/SourceFiles/history/view/history_view_about_view.h b/Telegram/SourceFiles/history/view/history_view_about_view.h index e9fed1829a..4e2d9096ad 100644 --- a/Telegram/SourceFiles/history/view/history_view_about_view.h +++ b/Telegram/SourceFiles/history/view/history_view_about_view.h @@ -29,6 +29,8 @@ public: bool refresh(); + void setDisplayedEmptyOverride(Fn value); + void make(Data::ChatIntro data, bool preview = false); [[nodiscard]] auto sendIntroSticker() const @@ -62,8 +64,11 @@ private: void loadCommonGroups(); + [[nodiscard]] bool displayedEmpty() const; + const not_null _history; const not_null _delegate; + Fn _displayedEmptyOverride; AdminLog::OwnedItem _item; DocumentData *_helloChosen = nullptr; diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp index 9a4eaac1b4..12261a0852 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp @@ -175,6 +175,13 @@ constexpr auto kPsaAboutPrefix = "cloud_lng_about_psa_"; return std::nullopt; } +[[nodiscard]] bool IsSpecialShowAtMsgId(MsgId id) { + return (id == ShowAtTheEndMsgId) + || (id == ShowAndStartBotMsgId) + || (id == ShowAndMaybeStartBotMsgId) + || (id == ShowForChooseMessagesMsgId); +} + } // namespace ChatMemento::ChatMemento( @@ -182,18 +189,25 @@ ChatMemento::ChatMemento( MsgId highlightId, MessageHighlightId highlight) : _id(id) -, _highlightId((highlightId == ShowForChooseMessagesMsgId) - ? MsgId(0) - : highlightId) +, _highlightId(IsSpecialShowAtMsgId(highlightId) ? MsgId(0) : highlightId) , _highlight(std::move(highlight)) -, _activateChooseForReport(highlightId == ShowForChooseMessagesMsgId) { - if (_highlightId || _id.sublist) { +, _activateChooseForReport(highlightId == ShowForChooseMessagesMsgId) +, _sendBotStart(highlightId == ShowAndStartBotMsgId) +, _maybeSendBotStart(highlightId == ShowAndMaybeStartBotMsgId) { + if ((highlightId == ShowAtTheEndMsgId) + || _sendBotStart + || _maybeSendBotStart) { + _list.setAroundPosition(Data::MaxMessagePosition); + } else if (_highlightId || _id.sublist) { _list.setAroundPosition({ .fullId = FullMsgId(_id.history->peer->id, _highlightId), .date = TimeId(0), }); } - if (!_highlightId && !_id.repliesRootId && !_id.sublist) { + if (!_list.aroundPosition() + && !_highlightId + && !_id.repliesRootId + && !_id.sublist) { setFromHistory(_id.history); } } @@ -607,9 +621,7 @@ ChatWidget::ChatWidget( _composeControls->sendActionUpdates( ) | rpl::on_next([=](ComposeControls::SendActionUpdate &&data) { - if (!_repliesRootId) { - return; - } else if (!data.cancel) { + if (!data.cancel) { session().sendProgressManager().update( _history, _repliesRootId, @@ -661,31 +673,38 @@ ChatWidget::ChatWidget( && (action.replyTo.topicRootId == _repliesRootId) && (action.replyTo.monoforumPeerId == _monoforumPeerId); }) | rpl::on_next([=](const Api::SendAction &action) { - if (action.options.scheduled) { - if (_topic) { - _composeControls->cancelReplyMessage(); - crl::on_main(this, [=, t = _topic] { - controller->showSection( - std::make_shared(t)); - }); + if (!action.replaceMediaOf) { + const auto lastKeyboardUsed = lastForceReplyReplied( + action.replyTo.messageId); + const auto replyMatches = action.replyTo.messageId + && (action.replyTo.messageId + == _composeControls->replyingToMessage().messageId); + if (replyMatches || lastKeyboardUsed) { + cancelReply(lastKeyboardUsed); } - } else if (!action.replaceMediaOf - && action.replyTo.messageId - && (action.replyTo.messageId - == _composeControls->replyingToMessage().messageId)) { - _composeControls->cancelReplyMessage(); - refreshTopBarActiveChat(); + if (mode() == Mode::History) { + cancelSuggestPost(); + } + if (action.options.scheduled) { + if (_topic) { + crl::on_main(this, [=, t = _topic] { + controller->showSection( + std::make_shared(t)); + }); + } else if (mode() == Mode::History) { + crl::on_main(this, [=, history = action.history] { + controller->showSection( + std::make_shared( + history)); + }); + } + } + } + if ((mode() == Mode::History) + && action.options.handleSupportSwitch) { + handleSupportSwitch(action.history); } }, lifetime()); - if (mode() == Mode::History) { - session().api().sendActions( - ) | rpl::filter([=](const Api::SendAction &action) { - return (action.history == _history) - && action.options.handleSupportSwitch; - }) | rpl::on_next([=](const Api::SendAction &action) { - handleSupportSwitch(action.history); - }, lifetime()); - } if (mode() == Mode::History) { _topControls->subscribeToPinnedMessages(); } @@ -749,6 +768,11 @@ ChatWidget::ChatWidget( updateControlsGeometry(); }, lifetime()); + _bottom->isButtonActiveValue( + ) | rpl::skip(1) | rpl::on_next([=] { + updateControlsVisibility(); + }, lifetime()); + refreshCanSendMessages(); using PeerUpdateFlag = Data::PeerUpdate::Flag; @@ -764,11 +788,23 @@ ChatWidget::ChatWidget( | PeerUpdateFlag::ManagedBot | PeerUpdateFlag::StarsPerMessage | PeerUpdateFlag::Migration + | PeerUpdateFlag::UnavailableReason ) | rpl::on_next([=](const Data::PeerUpdate &update) { if (update.flags & PeerUpdateFlag::Migration) { handlePeerMigration(); return; } + if (update.flags & PeerUpdateFlag::UnavailableReason) { + const auto unavailable = _peer->computeUnavailableReason(); + if (!unavailable.isEmpty()) { + const auto account = not_null(&_peer->account()); + closeCurrent(); + if (const auto primary = Core::App().windowFor(account)) { + primary->showToast(unavailable); + } + return; + } + } _bottom->applyPeerUpdate(update.flags); if (update.flags & (PeerUpdateFlag::FullInfo | PeerUpdateFlag::Rights)) { @@ -780,7 +816,9 @@ ChatWidget::ChatWidget( | PeerUpdateFlag::StarsPerMessage)) { refreshSuggestPostToggle(); } - if (update.flags & (PeerUpdateFlag::FullInfo + if (update.flags & PeerUpdateFlag::IsBlocked) { + refreshAboutView(true); + } else if (update.flags & (PeerUpdateFlag::FullInfo | PeerUpdateFlag::Rights | PeerUpdateFlag::Members | PeerUpdateFlag::ManagedBot @@ -796,6 +834,7 @@ ChatWidget::ChatWidget( session().changes().historyUpdates( _history, HistoryUpdateFlag::BotKeyboard + | HistoryUpdateFlag::CloudDraft | HistoryUpdateFlag::UnreadMentions | HistoryUpdateFlag::UnreadReactions | HistoryUpdateFlag::UnreadPollVotes @@ -805,6 +844,10 @@ ChatWidget::ChatWidget( if (flags & HistoryUpdateFlag::BotKeyboard) { updateBotKeyboard(); } + if (flags & HistoryUpdateFlag::CloudDraft) { + _composeControls->applyCloudDraft(); + refreshSuggestFromDraft(); + } if ((flags & HistoryUpdateFlag::UnreadMentions) || (flags & HistoryUpdateFlag::UnreadReactions) || (flags & HistoryUpdateFlag::UnreadPollVotes)) { @@ -837,7 +880,9 @@ ChatWidget::ChatWidget( if (keyboardSourceId() == update.item->fullId()) { updateBotKeyboard(update.item->history(), true); } else if (_repliesLastSlice) { - maybeUpdateLastKeyboardFromSlice(*_repliesLastSlice); + maybeUpdateLastKeyboardFromSlice( + *_repliesLastSlice, + true); } else { updateBotKeyboard(update.item->history(), true); } @@ -2187,13 +2232,11 @@ void ChatWidget::sendTextWithTags( session().api().sendMessage(std::move(message), nextLocalMessageId); _composeControls->clear(); - if (_repliesRootId) { - session().sendProgressManager().update( - _history, - _repliesRootId, - Api::SendProgressType::Typing, - -1); - } + session().sendProgressManager().update( + _history, + _repliesRootId, + Api::SendProgressType::Typing, + -1); //_saveDraftText = true; //_saveDraftStart = crl::now(); @@ -2417,6 +2460,39 @@ void ChatWidget::sendBotStartCommand() { updateControlsGeometry(); } +bool ChatWidget::clearMaybeSendStart() { + if (!_maybeSendStart) { + return false; + } else if (!_peer->isFullLoaded()) { + _peer->updateFull(); + return false; + } + _maybeSendStart = false; + if (const auto user = _peer->asUser()) { + if (user->blockStatus() == PeerData::BlockStatus::NotBlocked) { + if (const auto info = user->botInfo.get()) { + if (!info->startToken.isEmpty()) { + return true; + } + } + } + } + return false; +} + +void ChatWidget::checkMaybeSendBotStart() { + if (!_maybeSendStart || mode() != Mode::History) { + return; + } + const auto empty = _inner->isEmpty(); + const auto loadedEmpty = empty + && _inner->loadedAtTop() + && _inner->loadedAtBottom(); + if ((!empty || loadedEmpty) && clearMaybeSendStart() && !empty) { + sendBotStartCommand(); + } +} + void ChatWidget::joinChannelAction() { if (const auto channel = _peer->asChannel()) { session().api().joinChannel(channel); @@ -3348,6 +3424,16 @@ bool ChatWidget::showInternal( _composeControls->applyDraft( ComposeControls::FieldHistoryAction::NewEntry); refreshSuggestFromDraft(); + } else if ((mode() == Mode::History) + && !logMemento->highlightId() + && !logMemento->sendBotStart() + && !logMemento->maybeSendBotStart()) { + if (!_history->trackUnreadMessages() + || _inner->insideJumpToEndInsteadOfToUnread()) { + showAtEnd(); + } else { + showAtPosition(Data::UnreadMessagePosition); + } } else { restoreState(logMemento); } @@ -3747,8 +3833,26 @@ void ChatWidget::restoreState(not_null memento) { } updateBotKeyboard(); if (mode() == Mode::History) { + // Must be done before unreadCountUpdated(), or we auto-close. + if (_history->unreadMark()) { + session().data().histories().changeDialogUnreadMark( + _history, + false); + } + const auto migrated = _history->migrateFrom(); + if (migrated && migrated->unreadMark()) { + session().data().histories().changeDialogUnreadMark( + migrated, + false); + } unreadCountUpdated(); } + if (memento->sendBotStart()) { + sendBotStartCommand(); + } else if (memento->maybeSendBotStart()) { + _maybeSendStart = true; + checkMaybeSendBotStart(); + } } void ChatWidget::resizeEvent(QResizeEvent *e) { @@ -4295,17 +4399,21 @@ rpl::producer ChatWidget::sublistSource( } void ChatWidget::maybeUpdateLastKeyboardFromSlice( - const Data::MessagesSlice &slice) { + const Data::MessagesSlice &slice, + bool force) { if (mode() == Mode::Sublist) { return; } if (slice.skippedAfter.value_or(1) != 0) { return; } - const auto currentId = keyboardSourceId(); const auto inited = (mode() == Mode::History) ? _history->lastKeyboardInited : _repliesKeyboardInited; + if (inited && !force) { + return; + } + const auto currentId = keyboardSourceId(); using Flag = ReplyMarkupFlag; for (auto i = slice.ids.rbegin(); i != slice.ids.rend(); ++i) { const auto item = session().data().message(*i); @@ -4594,6 +4702,9 @@ MessagesBarData ChatWidget::listMessagesBar( void ChatWidget::listContentRefreshed() { injectSponsoredMessages(); + checkMaybeSendBotStart(); + refreshAboutView(); + _bottom->updateControlsVisibility(); } void ChatWidget::listUpdateDateLink( @@ -4948,6 +5059,14 @@ bool ChatWidget::kbWasHidden() const { == FullMsgId(_peer->id, keyboardHiddenId())); } +bool ChatWidget::lastForceReplyReplied(const FullMsgId &replyTo) const { + return (replyTo.peer == _peer->id) + && _keyboard + && _keyboard->forceReply() + && (_keyboard->forMsgId() == keyboardSourceId()) + && (_keyboard->forMsgId().msg == replyTo.msg); +} + bool ChatWidget::lastForceReplyReplied() const { return _keyboard && _keyboard->forceReply() @@ -4987,7 +5106,10 @@ void ChatWidget::listSearch( } void ChatWidget::listHandleViaClick(not_null bot) { - if (_bottom->canSendTexts()) { + const auto canSendTexts = (mode() == Mode::History) + ? Data::CanSend(_peer, ChatRestriction::SendOther) + : _bottom->canSendTexts(); + if (canSendTexts) { _composeControls->setText({ '@' + bot->username() + ' ' }); } } @@ -5185,20 +5307,34 @@ void ChatWidget::refreshAboutView(bool force) { if (mode() != Mode::History) { return; } + const auto refreshExisting = [&] { + const auto was = _aboutView->view(); + if (_aboutView->refresh()) { + _inner->aboutViewReplaced(was); + _inner->updateSize(); + _inner->update(); + } + }; const auto refresh = [&] { if (force) { - _aboutView = nullptr; _inner->setAboutView(nullptr); + _aboutView = nullptr; } if (!_aboutView) { _aboutView = std::make_unique( _history, - _history->delegateMixin()->delegate()); + _inner.data()); + _aboutView->setDisplayedEmptyOverride([=] { + return _inner->isEmpty(); + }); _aboutView->refreshRequests() | rpl::on_next([=] { if (_aboutView) { - _aboutView->refresh(); - _inner->updateSize(); - _inner->update(); + const auto was = _aboutView->view(); + if (_aboutView->refresh()) { + _inner->aboutViewReplaced(was); + _inner->updateSize(); + _inner->update(); + } } }, _aboutView->lifetime()); _aboutView->destroyRequests() | rpl::on_next([=] { @@ -5207,9 +5343,23 @@ void ChatWidget::refreshAboutView(bool force) { update(); }); }, _aboutView->lifetime()); + _aboutView->sendIntroSticker() | rpl::on_next([=]( + not_null sticker) { + sendExistingDocument( + sticker, + Api::MessageToSend(prepareSendAction({})), + std::nullopt); + }, _aboutView->lifetime()); _inner->setAboutView(_aboutView.get()); } - if (_aboutView && _aboutView->refresh()) { + if (_aboutView) { + refreshExisting(); + } + }; + const auto destroy = [&] { + if (_aboutView) { + _inner->setAboutView(nullptr); + _aboutView = nullptr; _inner->updateSize(); _inner->update(); } @@ -5232,11 +5382,17 @@ void ChatWidget::refreshAboutView(bool force) { } else { session().api().requestFullPeer(user); } + } else { + destroy(); } } else if (const auto monoforum = _peer->asChannel()) { if (monoforum->isMonoforum() && !monoforum->amMonoforumAdmin()) { refresh(); + } else { + destroy(); } + } else { + destroy(); } } diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.h b/Telegram/SourceFiles/history/view/history_view_chat_section.h index dda20dceba..f968954e41 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.h +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.h @@ -415,9 +415,11 @@ private: void updateBotKeyboard(History *h = nullptr, bool force = false); void toggleBotKeyboard(bool manual = true); void maybeUpdateLastKeyboardFromSlice( - const Data::MessagesSlice &slice); + const Data::MessagesSlice &slice, + bool force = false); void botCallbackSent(not_null item); [[nodiscard]] bool kbWasHidden() const; + [[nodiscard]] bool lastForceReplyReplied(const FullMsgId &replyTo) const; [[nodiscard]] bool lastForceReplyReplied() const; void cancelReply(bool lastKeyboardUsed = false); void sendBotCommand( @@ -498,6 +500,8 @@ private: void updateControlsVisibility(); void unblockUser(); void sendBotStartCommand(); + bool clearMaybeSendStart(); + void checkMaybeSendBotStart(); void joinChannelAction(); void joinGroupAction(); void toggleMuteUnmute(); @@ -595,6 +599,7 @@ private: bool _choosingAttach = false; bool _loaded = false; + bool _maybeSendStart = false; History *_supportPreloadHistory = nullptr; int _supportPreloadRequest = 0; @@ -663,6 +668,12 @@ public: [[nodiscard]] bool activateChooseForReport() const { return _activateChooseForReport; } + [[nodiscard]] bool sendBotStart() const { + return _sendBotStart; + } + [[nodiscard]] bool maybeSendBotStart() const { + return _maybeSendBotStart; + } private: void setupTopicViewer(); @@ -674,6 +685,8 @@ private: std::shared_ptr _replies; QVector _replyReturns; bool _activateChooseForReport = false; + bool _sendBotStart = false; + bool _maybeSendBotStart = false; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 4fc89f2b2d..df1ecdf2a4 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -1901,6 +1901,7 @@ void FillContextMenuItems( }, &st::menuIconCopy); } if (request.overSelection + && view && !Ui::SkipTranslate(list->getSelectedText().rich)) { const auto owner = &view->history()->owner(); result->addAction(tr::lng_context_translate_selected(tr::now), [=] { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index ae7fe613bc..f6c948526e 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -812,6 +812,7 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) { _emptyInfo->setVisible(isEmpty()); } checkActivation(); + _delegate->listContentRefreshed(); } std::optional ListWidget::scrollTopForPosition( @@ -1166,6 +1167,9 @@ Element *ListWidget::viewForItem(FullMsgId itemId) const { Element *ListWidget::viewForItem(const HistoryItem *item) const { if (item) { + if (_aboutView && _aboutView->item() == item) { + return _aboutView->view(); + } if (const auto i = _views.find(item); i != _views.end()) { return i->second.get(); } @@ -1777,6 +1781,14 @@ bool ListWidget::loadedAtTop() const { return skippedAtTop() == 0; } +bool ListWidget::insideJumpToEndInsteadOfToUnread() const { + if (_session->supportMode()) { + return true; + } + const auto unread = _bar.element; + return unread && (itemTop(unread) <= _visibleBottom); +} + bool ListWidget::loadedAtBottomKnown() const { return !!skippedAtBottom(); } @@ -2676,11 +2688,15 @@ void ListWidget::paintEvent(QPaintEvent *e) { || clip.y() + clip.height() <= about->top) { return; } + const auto view = about->view(); const auto top = about->top; auto aboutContext = context.translated(0, -top); - aboutContext.selection = TextSelection(); + const auto selection = itemRenderSelection(view); + aboutContext.selection = selection.selection; + aboutContext.fullMessageSelected = selection.fullMessageSelected; + aboutContext.messageSelection = selection.messageSelection; p.translate(0, top); - about->view()->draw(p, aboutContext); + view->draw(p, aboutContext); p.translate(0, -top); }; drawAboutView(); @@ -4501,10 +4517,18 @@ void ListWidget::mouseActionUpdate() { : TextState(); const auto replyBtnItem = session().data().message(replyBtnState.itemId); const auto replyBtnView = viewForItem(replyBtnItem); + const auto aboutView = (_aboutView + && _aboutView->view() + && point.y() >= _aboutView->top + && point.y() < _aboutView->top + _aboutView->view()->height()) + ? _aboutView->view() + : nullptr; const auto view = reactionView ? reactionView : replyBtnView ? replyBtnView + : aboutView + ? aboutView : strictFindItemByY(point.y()); const auto item = view ? view->data().get() : nullptr; const auto itemPoint = mapPointToItem(point, view); @@ -4849,6 +4873,9 @@ void ListWidget::performDrag() { } int ListWidget::itemTop(not_null view) const { + if (_aboutView && view == _aboutView->view()) { + return _aboutView->top; + } return _itemsTop + view->y(); } @@ -5122,6 +5149,12 @@ void ListWidget::showItemHighlight(not_null item) { } } +void ListWidget::aboutViewReplaced(const Element *was) { + if (was) { + viewReplaced(was, nullptr); + } +} + void ListWidget::viewReplaced(not_null was, Element *now) { if (_activeColumnsView == was.get()) { _activeColumnsView = nullptr; @@ -5329,7 +5362,9 @@ void ListWidget::setAboutView(AboutView *view) { if (_aboutView == view) { return; } - // TODO: port hit-test delegation from HistoryInner. + if (const auto was = _aboutView ? _aboutView->view() : nullptr) { + viewReplaced(was, nullptr); + } _aboutView = view; updateSize(); update(); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 866dbf3ae4..e21245c32f 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -377,6 +377,7 @@ public: [[nodiscard]] bool loadedAtTop() const; [[nodiscard]] bool loadedAtBottomKnown() const; [[nodiscard]] bool loadedAtBottom() const; + [[nodiscard]] bool insideJumpToEndInsteadOfToUnread() const; [[nodiscard]] bool isEmpty() const; [[nodiscard]] bool markingContentsRead() const; @@ -516,6 +517,7 @@ public: void setEmptyInfoWidget(base::unique_qptr &&w); void setAboutView(AboutView *view); + void aboutViewReplaced(const Element *was); void updateSize(); void overrideChatMode(std::optional mode); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 0212fdb112..27f4f5fda0 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1470,6 +1470,17 @@ void MainWidget::showHistory( if (peerId && OptionUseNewChatView.value()) { const auto history = session().data().history(peerId); + if (showAtMsgId == ShowAndStartBotMsgId) { + if (const auto user = history->peer->asUser()) { + if (const auto &info = user->botInfo) { + const auto wasState + = _controller->dialogsEntryStateCurrent(); + if (wasState.key) { + info->inlineReturnTo = wasState; + } + } + } + } using namespace HistoryView; auto memento = std::make_shared( ChatViewId{ .history = history },