Handle correctly comment links for public channels.

This commit is contained in:
John Preston 2020-09-17 20:57:06 +03:00
parent 7862443fcb
commit 61d89113d4
8 changed files with 181 additions and 132 deletions

View file

@ -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<FullMsgId>();
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<FullMsgId>();
const auto done = crl::guard(controller, [=](not_null<PeerData*> 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();

View file

@ -2029,7 +2029,7 @@ bool Message::displayRightActionComments() const {
return data()->repliesAreComments()
&& media()
&& media()->isDisplayed()
&& media()->customInfoLayout();
&& !hasBubble();
}
std::optional<QSize> Message::rightActionSize() const {

View file

@ -83,9 +83,16 @@ bool CanSendFiles(not_null<const QMimeData*> data) {
} // namespace
RepliesMemento::RepliesMemento(not_null<HistoryItem*> commentsItem)
: RepliesMemento(commentsItem->history(), commentsItem->id) {
if (const auto original = commentsItem->lookupDiscussionPostOriginal()) {
RepliesMemento::RepliesMemento(
not_null<HistoryItem*> 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<RepliesMemento*> 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) {

View file

@ -271,11 +271,17 @@ private:
class RepliesMemento : public Window::SectionMemento {
public:
RepliesMemento(not_null<History*> history, MsgId rootId)
RepliesMemento(
not_null<History*> history,
MsgId rootId,
MsgId highlightId = 0)
: _history(history)
, _rootId(rootId) {
, _rootId(rootId)
, _highlightId(highlightId) {
}
explicit RepliesMemento(not_null<HistoryItem*> commentsItem);
explicit RepliesMemento(
not_null<HistoryItem*> commentsItem,
MsgId commentId = 0);
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
@ -307,10 +313,14 @@ public:
[[nodiscard]] not_null<ListMemento*> list() {
return &_list;
}
[[nodiscard]] MsgId getHighlightId() const {
return _highlightId;
}
private:
const not_null<History*> _history;
const MsgId _rootId = 0;
const MsgId _highlightId = 0;
ListMemento _list;
std::shared_ptr<Data::RepliesList> _replies;
std::vector<MsgId> _replyReturns;

View file

@ -2597,101 +2597,25 @@ void MainWidget::searchInChat(Dialogs::Key chat) {
}
}
void MainWidget::openPeerByName(
const QString &username,
void MainWidget::openPeerResolved(
not_null<PeerData*> 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<InformBox>(
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<PeerData*> 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<PeerData*> 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<void(not_null<PeerData*>)> 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<InformBox>(
tr::lng_username_not_found(tr::now, lt_user, username)));
}
}).send();
}
void MainWidget::activate() {

View file

@ -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<void(not_null<PeerData*>)> done);
void openPeerResolved(
not_null<PeerData*> 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<not_null<PeerData*>, mtpRequestId> _viewsIncrementRequests;
base::flat_map<mtpRequestId, not_null<PeerData*>> _viewsIncrementByRequest;
base::Timer _viewsIncrementTimer;
mtpRequestId _resolveRequestId = 0;
struct SettingBackground;
std::unique_ptr<SettingBackground> _background;

View file

@ -94,6 +94,7 @@ Main::Session &SessionNavigation::session() const {
void SessionNavigation::showRepliesForMessage(
not_null<History*> history,
MsgId rootId,
MsgId commentId,
const SectionShow &params) {
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) {

View file

@ -141,6 +141,7 @@ public:
void showRepliesForMessage(
not_null<History*> history,
MsgId rootId,
MsgId commentId = 0,
const SectionShow &params = SectionShow());
void showPeerInfo(