From 61d89113d45937a231071ad77a39eae81371133d Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 17 Sep 2020 20:57:06 +0300 Subject: [PATCH] Handle correctly comment links for public channels. --- .../SourceFiles/core/local_url_handlers.cpp | 36 +++- .../history/view/history_view_message.cpp | 2 +- .../view/history_view_replies_section.cpp | 38 +++- .../view/history_view_replies_section.h | 16 +- Telegram/SourceFiles/mainwidget.cpp | 190 +++++++++--------- Telegram/SourceFiles/mainwidget.h | 19 +- .../window/window_session_controller.cpp | 11 +- .../window/window_session_controller.h | 1 + 8 files changed, 181 insertions(+), 132 deletions(-) diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index c0ff8b80db..450b21c9c8 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -267,21 +267,31 @@ bool ResolveUsername( auto post = (start == qsl("startgroup")) ? ShowAtProfileMsgId : ShowAtUnreadMsgId; - auto postParam = params.value(qsl("post")); - if (auto postId = postParam.toInt()) { + const auto postParam = params.value(qsl("post")); + if (const auto postId = postParam.toInt()) { post = postId; } + const auto commentParam = params.value(qsl("comment")); + const auto commentId = commentParam.toInt(); const auto gameParam = params.value(qsl("game")); if (!gameParam.isEmpty() && valid(gameParam)) { startToken = gameParam; post = ShowAtGameShareMsgId; } const auto clickFromMessageId = context.value(); - controller->content()->openPeerByName( - domain, - post, - startToken, - clickFromMessageId); + if (commentId) { + controller->content()->openCommentByName( + domain, + post, + commentId, + clickFromMessageId); + } else { + controller->content()->openPeerByName( + domain, + post, + startToken, + clickFromMessageId); + } return true; } @@ -300,7 +310,14 @@ bool ResolvePrivatePost( if (!channelId || !IsServerMsgId(msgId)) { return false; } + const auto clickFromMessageId = context.value(); const auto done = crl::guard(controller, [=](not_null peer) { + auto params = Window::SectionShow{ + Window::SectionShow::Way::Forward + }; + params.origin = Window::SectionShow::OriginMessage{ + clickFromMessageId + }; controller->showPeerHistory( peer->id, Window::SectionShow::Way::Forward, @@ -545,8 +562,9 @@ QString TryConvertUrlToLocal(QString url) { } else if (auto bgMatch = regex_match(qsl("^bg/([a-zA-Z0-9\\.\\_\\-]+)(\\?(.+)?)?$"), query, matchOptions)) { const auto params = bgMatch->captured(3); return qsl("tg://bg?slug=") + bgMatch->captured(1) + (params.isEmpty() ? QString() : '&' + params); - } else if (auto postMatch = regex_match(qsl("^c/(\\-?\\d+)/(\\d+)(#|$)"), query, matchOptions)) { - return qsl("tg://privatepost?channel=%1&post=%2").arg(postMatch->captured(1)).arg(postMatch->captured(2)); + } else if (auto postMatch = regex_match(qsl("^c/(\\-?\\d+)/(\\d+)(/?\\?|/?$)"), query, matchOptions)) { + auto params = query.mid(postMatch->captured(0).size()).toString(); + return qsl("tg://privatepost?channel=%1&post=%2").arg(postMatch->captured(1)).arg(postMatch->captured(2)) + (params.isEmpty() ? QString() : '&' + params); } else if (auto usernameMatch = regex_match(qsl("^([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), query, matchOptions)) { auto params = query.mid(usernameMatch->captured(0).size()).toString(); auto postParam = QString(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 7d9841a94f..1e4057e58a 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2029,7 +2029,7 @@ bool Message::displayRightActionComments() const { return data()->repliesAreComments() && media() && media()->isDisplayed() - && media()->customInfoLayout(); + && !hasBubble(); } std::optional Message::rightActionSize() const { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 88a1caad16..0035eb50e9 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -83,9 +83,16 @@ bool CanSendFiles(not_null data) { } // namespace -RepliesMemento::RepliesMemento(not_null commentsItem) -: RepliesMemento(commentsItem->history(), commentsItem->id) { - if (const auto original = commentsItem->lookupDiscussionPostOriginal()) { +RepliesMemento::RepliesMemento( + not_null commentsItem, + MsgId commentId) +: RepliesMemento(commentsItem->history(), commentsItem->id, commentId) { + if (commentId) { + _list.setAroundPosition({ + TimeId(0), + FullMsgId(commentsItem->history()->channelId(), commentId) + }); + } else if (const auto original = commentsItem->lookupDiscussionPostOriginal()) { if (original->commentsReadTill() == MsgId(1)) { _list.setAroundPosition(Data::MinMessagePosition); _list.setScrollTopState(ListMemento::ScrollTopState{ @@ -1085,8 +1092,12 @@ void RepliesWidget::showAtPosition( bool RepliesWidget::showAtPositionNow( Data::MessagePosition position, HistoryItem *originItem) { - if (const auto scrollTop = _inner->scrollTopForPosition(position)) { - while (_replyReturn && position.fullId.msg == _replyReturn->id) { + const auto item = position.fullId + ? _history->owner().message(position.fullId) + : nullptr; + const auto use = item ? item->position() : position; + if (const auto scrollTop = _inner->scrollTopForPosition(use)) { + while (_replyReturn && use.fullId.msg == _replyReturn->id) { calculateNextReplyReturn(); } const auto currentScrollTop = _scroll->scrollTop(); @@ -1096,14 +1107,14 @@ bool RepliesWidget::showAtPositionNow( const auto scrollDelta = snap(fullDelta, -limit, limit); _inner->animatedScrollTo( wanted, - position, + use, scrollDelta, (std::abs(fullDelta) > limit ? HistoryView::ListWidget::AnimatedScroll::Part : HistoryView::ListWidget::AnimatedScroll::Full)); - if (position != Data::MaxMessagePosition - && position != Data::UnreadMessagePosition) { - _inner->highlightMessage(position.fullId); + if (use != Data::MaxMessagePosition + && use != Data::UnreadMessagePosition) { + _inner->highlightMessage(use.fullId); } if (originItem) { pushReplyReturn(originItem); @@ -1303,6 +1314,15 @@ void RepliesWidget::restoreState(not_null memento) { } restoreReplyReturns(memento->replyReturns()); _inner->restoreState(memento->list()); + if (const auto highlight = memento->getHighlightId()) { + const auto position = Data::MessagePosition{ + TimeId(0), + FullMsgId(_history->channelId(), highlight) + }; + _inner->showAroundPosition(position, [=] { + return showAtPositionNow(position, nullptr); + }); + } } void RepliesWidget::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 0702940b66..d70ee35bc0 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -271,11 +271,17 @@ private: class RepliesMemento : public Window::SectionMemento { public: - RepliesMemento(not_null history, MsgId rootId) + RepliesMemento( + not_null history, + MsgId rootId, + MsgId highlightId = 0) : _history(history) - , _rootId(rootId) { + , _rootId(rootId) + , _highlightId(highlightId) { } - explicit RepliesMemento(not_null commentsItem); + explicit RepliesMemento( + not_null commentsItem, + MsgId commentId = 0); object_ptr createWidget( QWidget *parent, @@ -307,10 +313,14 @@ public: [[nodiscard]] not_null list() { return &_list; } + [[nodiscard]] MsgId getHighlightId() const { + return _highlightId; + } private: const not_null _history; const MsgId _rootId = 0; + const MsgId _highlightId = 0; ListMemento _list; std::shared_ptr _replies; std::vector _replyReturns; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 8c9163236f..d91f4d9739 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2597,101 +2597,25 @@ void MainWidget::searchInChat(Dialogs::Key chat) { } } -void MainWidget::openPeerByName( - const QString &username, +void MainWidget::openPeerResolved( + not_null peer, MsgId msgId, const QString &startToken, FullMsgId clickFromMessageId) { - Core::App().hideMediaView(); - - if (const auto peer = session().data().peerByUsername(username)) { - if (msgId == ShowAtGameShareMsgId) { - if (peer->isUser() && peer->asUser()->isBot() && !startToken.isEmpty()) { - peer->asUser()->botInfo->shareGameShortName = startToken; - AddBotToGroupBoxController::Start( - _controller, - peer->asUser()); - } else { - InvokeQueued(this, [this, peer] { - _controller->showPeerHistory( - peer->id, - SectionShow::Way::Forward); - }); - } - } else if (msgId == ShowAtProfileMsgId && !peer->isChannel()) { - if (peer->isUser() && peer->asUser()->isBot() && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { - peer->asUser()->botInfo->startGroupToken = startToken; - AddBotToGroupBoxController::Start( - _controller, - peer->asUser()); - } else if (peer->isUser() && peer->asUser()->isBot()) { - // Always open bot chats, even from mention links. - InvokeQueued(this, [this, peer] { - _controller->showPeerHistory( - peer->id, - SectionShow::Way::Forward); - }); - } else { - _controller->showPeerInfo(peer); - } + if (msgId == ShowAtGameShareMsgId) { + if (peer->isUser() && peer->asUser()->isBot() && !startToken.isEmpty()) { + peer->asUser()->botInfo->shareGameShortName = startToken; + AddBotToGroupBoxController::Start( + _controller, + peer->asUser()); } else { - if (msgId == ShowAtProfileMsgId || !peer->isChannel()) { // show specific posts only in channels / supergroups - msgId = ShowAtUnreadMsgId; - } - if (peer->isUser() && peer->asUser()->isBot()) { - peer->asUser()->botInfo->startToken = startToken; - if (peer == _history->peer()) { - _history->updateControlsVisibility(); - _history->updateControlsGeometry(); - } - } - const auto returnToId = clickFromMessageId; - InvokeQueued(this, [=] { - auto params = SectionShow{ - SectionShow::Way::Forward - }; - params.origin = SectionShow::OriginMessage{ - returnToId - }; - _controller->showPeerHistory(peer->id, params, msgId); + InvokeQueued(this, [this, peer] { + _controller->showPeerHistory( + peer->id, + SectionShow::Way::Forward); }); } - } else { - _api.request(MTPcontacts_ResolveUsername( - MTP_string(username) - )).done([=](const MTPcontacts_ResolvedPeer &result) { - usernameResolveDone(result, msgId, startToken); - }).fail([=](const RPCError &error) { - usernameResolveFail(error, username); - }).send(); - } -} - -bool MainWidget::contentOverlapped(const QRect &globalRect) { - return (_history->contentOverlapped(globalRect) - || _playerPlaylist->overlaps(globalRect) - || (_playerVolume && _playerVolume->overlaps(globalRect))); -} - -void MainWidget::usernameResolveDone( - const MTPcontacts_ResolvedPeer &result, - MsgId msgId, - const QString &startToken) { - Ui::hideLayer(); - if (result.type() != mtpc_contacts_resolvedPeer) { - return; - } - - const auto &d(result.c_contacts_resolvedPeer()); - session().data().processUsers(d.vusers()); - session().data().processChats(d.vchats()); - const auto peerId = peerFromMTP(d.vpeer()); - if (!peerId) { - return; - } - - const auto peer = session().data().peer(peerId); - if (msgId == ShowAtProfileMsgId && !peer->isChannel()) { + } else if (msgId == ShowAtProfileMsgId && !peer->isChannel()) { if (peer->isUser() && peer->asUser()->isBot() && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { peer->asUser()->botInfo->startGroupToken = startToken; AddBotToGroupBoxController::Start( @@ -2708,8 +2632,7 @@ void MainWidget::usernameResolveDone( _controller->showPeerInfo(peer); } } else { - // show specific posts only in channels / supergroups - if (msgId == ShowAtProfileMsgId || !peer->isChannel()) { + if (msgId == ShowAtProfileMsgId || !peer->isChannel()) { // show specific posts only in channels / supergroups msgId = ShowAtUnreadMsgId; } if (peer->isUser() && peer->asUser()->isBot()) { @@ -2720,19 +2643,88 @@ void MainWidget::usernameResolveDone( } } InvokeQueued(this, [=] { - _controller->showPeerHistory( - peer->id, - SectionShow::Way::Forward, - msgId); + auto params = SectionShow{ + SectionShow::Way::Forward + }; + params.origin = SectionShow::OriginMessage{ + clickFromMessageId + }; + _controller->showPeerHistory(peer->id, params, msgId); }); } } -void MainWidget::usernameResolveFail(const RPCError &error, const QString &username) { - if (error.code() == 400) { - Ui::show(Box( - tr::lng_username_not_found(tr::now, lt_user, username))); +void MainWidget::openPeerByName( + const QString &username, + MsgId msgId, + const QString &startToken, + FullMsgId clickFromMessageId) { + Core::App().hideMediaView(); + resolveUsername(username, [=](not_null peer) { + openPeerResolved(peer, msgId, startToken, clickFromMessageId); + }); +} + +void MainWidget::openCommentByName( + const QString &username, + MsgId msgId, + MsgId commentId, + FullMsgId clickFromMessageId) { + Core::App().hideMediaView(); + resolveUsername(username, [=](not_null peer) { + InvokeQueued(this, [=] { + auto params = SectionShow{ + SectionShow::Way::Forward + }; + params.origin = SectionShow::OriginMessage{ + clickFromMessageId + }; + _controller->showRepliesForMessage( + session().data().history(peer), + msgId, + commentId, + params); + }); + }); + Core::App().hideMediaView(); +} + +bool MainWidget::contentOverlapped(const QRect &globalRect) { + return (_history->contentOverlapped(globalRect) + || _playerPlaylist->overlaps(globalRect) + || (_playerVolume && _playerVolume->overlaps(globalRect))); +} + +void MainWidget::resolveUsername( + const QString &username, + Fn)> done) { + if (const auto peer = session().data().peerByUsername(username)) { + done(peer); + return; } + _api.request(base::take(_resolveRequestId)).cancel(); + _resolveRequestId = _api.request(MTPcontacts_ResolveUsername( + MTP_string(username) + )).done([=](const MTPcontacts_ResolvedPeer &result) { + _resolveRequestId = 0; + Ui::hideLayer(); + if (result.type() != mtpc_contacts_resolvedPeer) { + return; + } + + const auto &d(result.c_contacts_resolvedPeer()); + session().data().processUsers(d.vusers()); + session().data().processChats(d.vchats()); + if (const auto peerId = peerFromMTP(d.vpeer())) { + done(session().data().peer(peerId)); + } + }).fail([=](const RPCError &error) { + _resolveRequestId = 0; + if (error.code() == 400) { + Ui::show(Box( + tr::lng_username_not_found(tr::now, lt_user, username))); + } + }).send(); } void MainWidget::activate() { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index b67f80c02f..1d459ed471 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -127,6 +127,11 @@ public: MsgId msgId = ShowAtUnreadMsgId, const QString &startToken = QString(), FullMsgId clickFromMessageId = FullMsgId()); + void openCommentByName( + const QString &name, + MsgId msgId, + MsgId commentId, + FullMsgId clickFromMessageId = FullMsgId()); void activate(); @@ -289,13 +294,14 @@ private: void saveSectionInStack(); - void usernameResolveDone( - const MTPcontacts_ResolvedPeer &result, + void resolveUsername( + const QString &username, + Fn)> done); + void openPeerResolved( + not_null peer, MsgId msgId, - const QString &startToken); - void usernameResolveFail( - const RPCError &error, - const QString &username); + const QString &startToken, + FullMsgId clickFromMessageId); int getMainSectionTop() const; int getThirdSectionTop() const; @@ -400,6 +406,7 @@ private: base::flat_map, mtpRequestId> _viewsIncrementRequests; base::flat_map> _viewsIncrementByRequest; base::Timer _viewsIncrementTimer; + mtpRequestId _resolveRequestId = 0; struct SettingBackground; std::unique_ptr _background; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 167ebbee0b..78894b270a 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -94,6 +94,7 @@ Main::Session &SessionNavigation::session() const { void SessionNavigation::showRepliesForMessage( not_null history, MsgId rootId, + MsgId commentId, const SectionShow ¶ms) { if (_showingRepliesRequestId && _showingRepliesHistory == history.get() @@ -104,13 +105,13 @@ void SessionNavigation::showRepliesForMessage( const auto channelId = history->channelId(); const auto item = _session->data().message(channelId, rootId); - if (!item || !item->repliesAreComments()) { + if (!commentId && (!item || !item->repliesAreComments())) { showSection(HistoryView::RepliesMemento(history, rootId)); return; - } else if (const auto id = item->commentsItemId()) { - if (const auto item = _session->data().message(id)) { + } else if (const auto id = item ? item->commentsItemId() : FullMsgId()) { + if (const auto commentsItem = _session->data().message(id)) { showSection( - HistoryView::RepliesMemento(item)); + HistoryView::RepliesMemento(commentsItem)); return; } } @@ -155,7 +156,7 @@ void SessionNavigation::showRepliesForMessage( } } showSection( - HistoryView::RepliesMemento(item)); + HistoryView::RepliesMemento(item, commentId)); } }); }).fail([=](const RPCError &error) { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index abf12066e3..f0976210b4 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -141,6 +141,7 @@ public: void showRepliesForMessage( not_null history, MsgId rootId, + MsgId commentId = 0, const SectionShow ¶ms = SectionShow()); void showPeerInfo(