Update API scheme to layer 167.

Support quote offset passing to API.
Support simple phrases in giveaway results message.
This commit is contained in:
John Preston 2023-11-10 13:27:47 +04:00
parent f442d69cb6
commit dcc326e17f
44 changed files with 389 additions and 303 deletions

View file

@ -1667,6 +1667,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_story_mention_me_unavailable" = "The story where you mentioned {user} is no longer available.";
"lng_action_story_mention_unavailable" = "The story where {user} mentioned you is no longer available.";
"lng_action_giveaway_started" = "{from} just started a giveaway of Telegram Premium subscriptions to its followers.";
"lng_action_giveaway_results#one" = "{count} winner of the giveaway was randomly selected by Telegram and received private messages with giftcodes.";
"lng_action_giveaway_results#other" = "{count} winners of the giveaway were randomly selected by Telegram and received private messages with giftcodes.";
"lng_action_giveaway_results_some" = "Some winners of the giveaway was randomly selected by Telegram and received private messages with giftcodes.";
"lng_action_giveaway_results_none" = "No winners of the giveaway could be selected.";
"lng_premium_gift_duration_months#one" = "for {count} month";
"lng_premium_gift_duration_months#other" = "for {count} months";

View file

@ -72,7 +72,7 @@ MTPInputReplyTo ReplyToForMTP(
| (external ? Flag::f_reply_to_peer_id : Flag())
| (replyTo.quote.text.isEmpty()
? Flag()
: Flag::f_quote_text)
: (Flag::f_quote_text | Flag::f_quote_offset))
| (quoteEntities.v.isEmpty()
? Flag()
: Flag::f_quote_entities)),
@ -82,7 +82,8 @@ MTPInputReplyTo ReplyToForMTP(
? owner->peer(replyTo.messageId.peer)->input
: MTPInputPeer()),
MTP_string(replyTo.quote.text),
quoteEntities);
quoteEntities,
MTP_int(replyTo.quoteOffset));
}
return MTPInputReplyTo();
}
@ -983,6 +984,7 @@ int Histories::sendPreparedMessage(
.quote = replyTo.quote,
.storyId = replyTo.storyId,
.topicRootId = convertTopicReplyToId(history, replyTo.topicRootId),
.quoteOffset = replyTo.quoteOffset,
};
return v::match(message(history, realReplyTo), [&](const auto &request) {
const auto type = RequestType::Send;

View file

@ -161,6 +161,7 @@ struct FullReplyTo {
TextWithEntities quote;
FullStoryId storyId;
MsgId topicRootId = 0;
int quoteOffset = 0;
[[nodiscard]] bool valid() const {
return messageId || (storyId && peerIsUser(storyId.peer));

View file

@ -1316,9 +1316,9 @@ ServiceAction ParseServiceAction(
}, [&](const MTPDmessageActionSetChatWallPaper &data) {
auto content = ActionSetChatWallPaper();
// #TODO wallpapers
content.same = data.is_same();
content.both = data.is_for_both();
result.content = content;
}, [&](const MTPDmessageActionSetSameChatWallPaper &data) {
result.content = ActionSetSameChatWallPaper();
}, [&](const MTPDmessageActionRequestedPeer &data) {
auto content = ActionRequestedPeer();
content.peerId = ParsePeerId(data.vpeer());
@ -1334,9 +1334,14 @@ ServiceAction ParseServiceAction(
content.months = data.vmonths().v;
content.code = data.vslug().v;
result.content = content;
}, [&](const MTPDmessageActionGiveawayLaunch &) {
}, [&](const MTPDmessageActionGiveawayLaunch &data) {
auto content = ActionGiveawayLaunch();
result.content = content;
}, [&](const MTPDmessageActionGiveawayResults &data) {
auto content = ActionGiveawayResults();
content.winners = data.vwinners_count().v;
content.unclaimed = data.vunclaimed_count().v;
result.content = content;
}, [](const MTPDmessageActionEmpty &data) {});
return result;
}

View file

@ -533,12 +533,11 @@ struct ActionSuggestProfilePhoto {
};
struct ActionSetChatWallPaper {
bool same = false;
bool both = false;
// #TODO wallpapers
};
struct ActionSetSameChatWallPaper {
};
struct ActionGiftCode {
QByteArray code;
PeerId boostPeerId = 0;
@ -555,6 +554,11 @@ struct ActionRequestedPeer {
struct ActionGiveawayLaunch {
};
struct ActionGiveawayResults {
int winners = 0;
int unclaimed = 0;
};
struct ServiceAction {
std::variant<
v::null_t,
@ -593,9 +597,9 @@ struct ServiceAction {
ActionSuggestProfilePhoto,
ActionRequestedPeer,
ActionSetChatWallPaper,
ActionSetSameChatWallPaper,
ActionGiftCode,
ActionGiveawayLaunch> content;
ActionGiveawayLaunch,
ActionGiveawayResults> content;
};
ServiceAction ParseServiceAction(

View file

@ -1273,12 +1273,12 @@ auto HtmlWriter::Wrap::pushMessage(
}, [&](const ActionRequestedPeer &data) {
return "requested: "_q/* + data.peerId*/;
}, [&](const ActionSetChatWallPaper &data) {
return serviceFrom + " set a new background for this chat";
}, [&](const ActionSetSameChatWallPaper &data) {
return serviceFrom
+ " set "
+ wrapReplyToLink("the same background")
+ " for this chat";
+ (data.same
? (" set "
+ wrapReplyToLink("the same background")
+ " for this chat")
: " set a new background for this chat");
}, [&](const ActionGiftCode &data) {
return data.unclaimed
? ("This is an unclaimed Telegram Premium for "
@ -1297,6 +1297,10 @@ auto HtmlWriter::Wrap::pushMessage(
}, [&](const ActionGiveawayLaunch &data) {
return serviceFrom + " just started a giveaway "
"of Telegram Premium subscriptions to its followers.";
}, [&](const ActionGiveawayResults &data) {
return QByteArray::number(data.winners)
+ " of the giveaway were randomly selected by Telegram "
"and received private messages with giftcodes.";
}, [](v::null_t) { return QByteArray(); });
if (!serviceText.isEmpty()) {

View file

@ -604,12 +604,15 @@ QByteArray SerializeMessage(
push("via_giveaway", data.viaGiveaway);
}, [&](const ActionGiveawayLaunch &data) {
pushAction("giveaway_launch");
}, [&](const ActionGiveawayResults &data) {
pushAction("giveaway_results");
push("winners", data.winners);
push("unclaimed", data.unclaimed);
}, [&](const ActionSetChatWallPaper &data) {
pushActor();
pushAction("set_chat_wallpaper");
}, [&](const ActionSetSameChatWallPaper &data) {
pushActor();
pushAction("set_same_chat_wallpaper");
pushAction(data.same
? "set_same_chat_wallpaper"
: "set_chat_wallpaper");
pushReplyToMsgId("message_id");
}, [](v::null_t) {});

View file

@ -1144,7 +1144,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} else if (item->isUnreadMention()
&& !item->isUnreadMedia()) {
readContents.insert(item);
_widget->enqueueMessageHighlight(view, {});
_widget->enqueueMessageHighlight({ item });
}
}
session().data().reactions().poll(item, context.now);
@ -2410,17 +2410,25 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
const auto replyToItem = selected.item ? selected.item : item;
const auto itemId = replyToItem->fullId();
const auto quote = selected.text;
const auto quoteOffset = selected.offset;
text.replace('&', u"&&"_q);
_menu->addAction(text, [=] {
if (canSendReply) {
_widget->replyToMessage({ itemId, quote });
_widget->replyToMessage({
.messageId = itemId,
.quote = quote,
.quoteOffset = quoteOffset,
});
if (!quote.empty()) {
_widget->clearSelected();
}
} else {
HistoryView::Controls::ShowReplyToChatBox(
controller->uiShow(),
{ itemId, quote });
const auto show = controller->uiShow();
HistoryView::Controls::ShowReplyToChatBox(show, {
.messageId = itemId,
.quote = quote,
.quoteOffset = quoteOffset,
});
}
}, &st::menuIconReply);
}

View file

@ -78,6 +78,20 @@ constexpr auto kPinnedMessageTextLimit = 16;
using ItemPreview = HistoryView::ItemPreview;
template <typename T>
[[nodiscard]] PreparedServiceText PrepareEmptyText(const T &) {
return PreparedServiceText();
};
template <typename T>
[[nodiscard]] PreparedServiceText PrepareErrorText(const T &data) {
if constexpr (!std::is_same_v<T, MTPDmessageActionEmpty>) {
const auto name = QString::fromUtf8(typeid(data).name());
LOG(("API Error: %1 received.").arg(name));
}
return PreparedServiceText{ { tr::lng_message_empty(tr::now) } };
}
[[nodiscard]] TextWithEntities SpoilerLoginCode(TextWithEntities text) {
const auto r = QRegularExpression(u"([\\d\\-]{5,7})"_q);
const auto m = r.match(text.text);
@ -449,7 +463,7 @@ HistoryItem::HistoryItem(
const auto originalMedia = original->media();
const auto dropForwardInfo = original->computeDropForwardedInfo();
config.reply.messageId = config.reply.topMessageId = topicRootId;
config.reply.topicPost = (topicRootId != 0);
config.reply.topicPost = (topicRootId != 0) ? 1 : 0;
if (const auto originalReply = original->Get<HistoryMessageReply>()) {
if (originalReply->external()) {
config.reply = originalReply->fields().clone(this);
@ -3371,13 +3385,15 @@ void HistoryItem::createComponentsHelper(
&& topic->rootId() != to->topicRootId()) {
config.reply.externalPeerId = replyTo.messageId.peer;
}
config.reply.topicPost = config.reply.externalPeerId
const auto topicPost = config.reply.externalPeerId
? (replyTo.topicRootId
&& (replyTo.topicRootId != Data::ForumTopic::kGeneralId))
: (LookupReplyIsTopicPost(to)
|| (to && to->Has<HistoryServiceTopicInfo>())
|| (forum && forum->creating(config.reply.topMessageId)));
config.reply.manualQuote = !replyTo.quote.empty();
config.reply.topicPost = topicPost ? 1 : 0;
config.reply.manualQuote = replyTo.quote.empty() ? 0 : 1;
config.reply.quoteOffset = replyTo.quoteOffset;
config.reply.quote = std::move(replyTo.quote);
}
config.markup = std::move(markup);
@ -3696,8 +3712,12 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) {
}
}, call->lifetime);
}
} else if (type == mtpc_messageActionSetSameChatWallPaper) {
UpdateComponents(HistoryServiceSameBackground::Bit());
} else if (type == mtpc_messageActionSetChatWallPaper) {
if (action.c_messageActionSetChatWallPaper().is_same()) {
UpdateComponents(HistoryServiceSameBackground::Bit());
} else {
RemoveComponents(HistoryServiceSameBackground::Bit());
}
}
if (const auto replyTo = message.vreply_to()) {
replyTo->match([&](const MTPDmessageReplyHeader &data) {
@ -3877,7 +3897,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result;
};
auto prepareChatDeletePhoto = [this] {
auto prepareChatDeletePhoto = [&](const MTPDmessageActionChatDeletePhoto &action) {
auto result = PreparedServiceText();
if (isPost()) {
result.text = tr::lng_action_removed_photo_channel(
@ -3956,7 +3976,23 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result;
};
auto prepareScreenshotTaken = [this] {
auto preparePinMessage = [&](const MTPDmessageActionPinMessage &) {
return preparePinnedText();
};
auto prepareGameScore = [&](const MTPDmessageActionGameScore &) {
return prepareGameScoreText();
};
auto preparePhoneCall = [&](const MTPDmessageActionPhoneCall &) -> PreparedServiceText {
Unexpected("PhoneCall type in setServiceMessageFromMtp.");
};
auto preparePaymentSent = [&](const MTPDmessageActionPaymentSent &) {
return preparePaymentSentText();
};
auto prepareScreenshotTaken = [this](const MTPDmessageActionScreenshotTaken &) {
auto result = PreparedServiceText();
if (out()) {
result.text = tr::lng_action_you_took_screenshot(
@ -4055,7 +4091,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result;
};
auto prepareContactSignUp = [this] {
auto prepareContactSignUp = [this](const MTPDmessageActionContactSignUp &data) {
auto result = PreparedServiceText();
result.links.push_back(fromLink());
result.text = tr::lng_action_user_registered(
@ -4258,6 +4294,10 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result;
};
auto prepareGroupCallScheduled = [&](const MTPDmessageActionGroupCallScheduled &data) {
return prepareCallScheduledText(data.vschedule_date().v);
};
auto prepareSetChatTheme = [this](const MTPDmessageActionSetChatTheme &action) {
auto result = PreparedServiceText();
const auto text = qs(action.vemoticon());
@ -4459,28 +4499,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
auto prepareSetChatWallPaper = [&](
const MTPDmessageActionSetChatWallPaper &action) {
const auto isSelf = (_from->id == _from->session().userPeerId());
const auto peer = isSelf ? history()->peer : _from;
const auto user = peer->asUser();
const auto name = (user && !user->firstName.isEmpty())
? user->firstName
: peer->name();
auto result = PreparedServiceText{};
result.links.push_back(peer->createOpenLink());
result.text = isSelf
? tr::lng_action_set_wallpaper_me(
tr::now,
Ui::Text::WithEntities)
: tr::lng_action_set_wallpaper(
tr::now,
lt_user,
Ui::Text::Link(name, 1), // Link 1.
Ui::Text::WithEntities);
return result;
};
auto prepareSetSameChatWallPaper = [&](
const MTPDmessageActionSetSameChatWallPaper &action) {
const auto isSelf = (_from->id == _from->session().userPeerId());
const auto same = action.is_same();
const auto peer = isSelf ? history()->peer : _from;
const auto user = peer->asUser();
const auto name = (user && !user->firstName.isEmpty())
@ -4491,14 +4510,18 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
result.links.push_back(peer->createOpenLink());
}
result.text = isSelf
? tr::lng_action_set_same_wallpaper_me(
tr::now,
Ui::Text::WithEntities)
: tr::lng_action_set_same_wallpaper(
tr::now,
lt_user,
Ui::Text::Link(name, 1), // Link 1.
Ui::Text::WithEntities);
? (same
? tr::lng_action_set_same_wallpaper_me
: tr::lng_action_set_wallpaper_me)(
tr::now,
Ui::Text::WithEntities)
: (same
? tr::lng_action_set_same_wallpaper
: tr::lng_action_set_wallpaper)(
tr::now,
lt_user,
Ui::Text::Link(name, 1), // Link 1.
Ui::Text::WithEntities);
return result;
};
@ -4532,93 +4555,65 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result;
};
setServiceText(action.match([&](
const MTPDmessageActionChatAddUser &data) {
return prepareChatAddUserText(data);
}, [&](const MTPDmessageActionChatJoinedByLink &data) {
return prepareChatJoinedByLink(data);
}, [&](const MTPDmessageActionChatCreate &data) {
return prepareChatCreate(data);
}, [](const MTPDmessageActionChatMigrateTo &) {
return PreparedServiceText();
}, [](const MTPDmessageActionChannelMigrateFrom &) {
return PreparedServiceText();
}, [](const MTPDmessageActionHistoryClear &) {
return PreparedServiceText();
}, [&](const MTPDmessageActionChannelCreate &data) {
return prepareChannelCreate(data);
}, [&](const MTPDmessageActionChatDeletePhoto &) {
return prepareChatDeletePhoto();
}, [&](const MTPDmessageActionChatDeleteUser &data) {
return prepareChatDeleteUser(data);
}, [&](const MTPDmessageActionChatEditPhoto &data) {
return prepareChatEditPhoto(data);
}, [&](const MTPDmessageActionChatEditTitle &data) {
return prepareChatEditTitle(data);
}, [&](const MTPDmessageActionPinMessage &) {
return preparePinnedText();
}, [&](const MTPDmessageActionGameScore &) {
return prepareGameScoreText();
}, [&](const MTPDmessageActionPhoneCall &) -> PreparedServiceText {
Unexpected("PhoneCall type in setServiceMessageFromMtp.");
}, [&](const MTPDmessageActionPaymentSent &) {
return preparePaymentSentText();
}, [&](const MTPDmessageActionScreenshotTaken &) {
return prepareScreenshotTaken();
}, [&](const MTPDmessageActionCustomAction &data) {
return prepareCustomAction(data);
}, [&](const MTPDmessageActionBotAllowed &data) {
return prepareBotAllowed(data);
}, [&](const MTPDmessageActionSecureValuesSent &data) {
return prepareSecureValuesSent(data);
}, [&](const MTPDmessageActionContactSignUp &data) {
return prepareContactSignUp();
}, [&](const MTPDmessageActionGeoProximityReached &data) {
return prepareProximityReached(data);
}, [](const MTPDmessageActionPaymentSentMe &) {
LOG(("API Error: messageActionPaymentSentMe received."));
return PreparedServiceText{ { tr::lng_message_empty(tr::now) } };
}, [](const MTPDmessageActionSecureValuesSentMe &) {
LOG(("API Error: messageActionSecureValuesSentMe received."));
return PreparedServiceText{ { tr::lng_message_empty(tr::now) } };
}, [&](const MTPDmessageActionGroupCall &data) {
return prepareGroupCall(data);
}, [&](const MTPDmessageActionInviteToGroupCall &data) {
return prepareInviteToGroupCall(data);
}, [&](const MTPDmessageActionSetMessagesTTL &data) {
return prepareSetMessagesTTL(data);
}, [&](const MTPDmessageActionGroupCallScheduled &data) {
return prepareCallScheduledText(data.vschedule_date().v);
}, [&](const MTPDmessageActionSetChatTheme &data) {
return prepareSetChatTheme(data);
}, [&](const MTPDmessageActionChatJoinedByRequest &data) {
return prepareChatJoinedByRequest(data);
}, [&](const MTPDmessageActionWebViewDataSent &data) {
return prepareWebViewDataSent(data);
}, [&](const MTPDmessageActionGiftPremium &data) {
return prepareGiftPremium(data);
}, [&](const MTPDmessageActionTopicCreate &data) {
return prepareTopicCreate(data);
}, [&](const MTPDmessageActionTopicEdit &data) {
return prepareTopicEdit(data);
}, [&](const MTPDmessageActionWebViewDataSentMe &data) {
LOG(("API Error: messageActionWebViewDataSentMe received."));
return PreparedServiceText{ { tr::lng_message_empty(tr::now) } };
}, [&](const MTPDmessageActionSuggestProfilePhoto &data) {
return prepareSuggestProfilePhoto(data);
}, [&](const MTPDmessageActionRequestedPeer &data) {
return prepareRequestedPeer(data);
}, [&](const MTPDmessageActionSetChatWallPaper &data) {
return prepareSetChatWallPaper(data);
}, [&](const MTPDmessageActionSetSameChatWallPaper &data) {
return prepareSetSameChatWallPaper(data);
}, [&](const MTPDmessageActionGiftCode &data) {
return prepareGiftCode(data);
}, [&](const MTPDmessageActionGiveawayLaunch &data) {
return prepareGiveawayLaunch(data);
}, [](const MTPDmessageActionEmpty &) {
return PreparedServiceText{ { tr::lng_message_empty(tr::now) } };
}));
auto prepareGiveawayResults = [&](const MTPDmessageActionGiveawayResults &action) {
auto result = PreparedServiceText();
const auto winners = action.vwinners_count().v;
const auto unclaimed = action.vunclaimed_count().v;
result.text = {
(!winners
? tr::lng_action_giveaway_results_none(tr::now)
: unclaimed
? tr::lng_action_giveaway_results_some(tr::now)
: tr::lng_action_giveaway_results(
tr::now,
lt_count,
winners))
};
return result;
};
setServiceText(action.match(
prepareChatAddUserText,
prepareChatJoinedByLink,
prepareChatCreate,
PrepareEmptyText<MTPDmessageActionChatMigrateTo>,
PrepareEmptyText<MTPDmessageActionChannelMigrateFrom>,
PrepareEmptyText<MTPDmessageActionHistoryClear>,
prepareChannelCreate,
prepareChatDeletePhoto,
prepareChatDeleteUser,
prepareChatEditPhoto,
prepareChatEditTitle,
preparePinMessage,
prepareGameScore,
preparePhoneCall,
preparePaymentSent,
prepareScreenshotTaken,
prepareCustomAction,
prepareBotAllowed,
prepareSecureValuesSent,
prepareContactSignUp,
prepareProximityReached,
PrepareErrorText<MTPDmessageActionPaymentSentMe>,
PrepareErrorText<MTPDmessageActionSecureValuesSentMe>,
prepareGroupCall,
prepareInviteToGroupCall,
prepareSetMessagesTTL,
prepareGroupCallScheduled,
prepareSetChatTheme,
prepareChatJoinedByRequest,
prepareWebViewDataSent,
prepareGiftPremium,
prepareTopicCreate,
prepareTopicEdit,
PrepareErrorText<MTPDmessageActionWebViewDataSentMe>,
prepareSuggestProfilePhoto,
prepareRequestedPeer,
prepareSetChatWallPaper,
prepareGiftCode,
prepareGiveawayLaunch,
prepareGiveawayResults,
PrepareErrorText<MTPDmessageActionEmpty>));
// Additional information.
applyAction(action);
@ -4680,10 +4675,13 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
}, [](const MTPDphotoEmpty &) {
});
}, [&](const MTPDmessageActionSetChatWallPaper &data) {
const auto session = &history()->session();
const auto &attached = data.vwallpaper();
if (const auto paper = Data::WallPaper::Create(session, attached)) {
_media = std::make_unique<Data::MediaWallPaper>(this, *paper);
if (!data.is_same()) {
using namespace Data;
const auto session = &history()->session();
const auto &attached = data.vwallpaper();
if (const auto paper = WallPaper::Create(session, attached)) {
_media = std::make_unique<MediaWallPaper>(this, *paper);
}
}
}, [&](const MTPDmessageActionGiftCode &data) {
const auto boostedId = data.vboost_peer()

View file

@ -283,8 +283,9 @@ ReplyFields ReplyFields::clone(not_null<HistoryItem*> parent) const {
.messageId = messageId,
.topMessageId = topMessageId,
.storyId = storyId,
.topicPost = topicPost,
.quoteOffset = quoteOffset,
.manualQuote = manualQuote,
.topicPost = topicPost,
};
}
@ -303,7 +304,7 @@ ReplyFields ReplyFieldsFromMTP(
: id;
result.topMessageId
= data.vreply_to_top_id().value_or(id);
result.topicPost = data.is_forum_topic();
result.topicPost = data.is_forum_topic() ? 1 : 0;
}
if (const auto header = data.vreply_from()) {
const auto &data = header->data();
@ -324,7 +325,8 @@ ReplyFields ReplyFieldsFromMTP(
&owner->session(),
data.vquote_entities().value_or_empty()),
};
result.manualQuote = data.is_quote();
result.quoteOffset = data.vquote_offset().value_or_empty();
result.manualQuote = data.is_quote() ? 1 : 0;
return result;
}, [&](const MTPDmessageReplyStoryHeader &data) {
return ReplyFields{
@ -357,6 +359,7 @@ FullReplyTo ReplyToFromMTP(
&history->session(),
data.vquote_entities().value_or_empty()),
};
result.quoteOffset = data.vquote_offset().value_or_empty();
return result;
}, [&](const MTPDinputReplyToStory &data) {
if (const auto parsed = Data::UserFromInputMTP(
@ -461,7 +464,7 @@ void HistoryMessageReply::updateFields(
MsgId messageId,
MsgId topMessageId,
bool topicPost) {
_fields.topicPost = topicPost;
_fields.topicPost = topicPost ? 1 : 0;
if ((_fields.messageId != messageId)
&& !IsServerMsgId(_fields.messageId)) {
_fields.messageId = messageId;

View file

@ -233,7 +233,7 @@ private:
};
struct ReplyFields {
ReplyFields clone(not_null<HistoryItem*> parent) const;
[[nodiscard]] ReplyFields clone(not_null<HistoryItem*> parent) const;
TextWithEntities quote;
std::unique_ptr<Data::Media> externalMedia;
@ -244,8 +244,9 @@ struct ReplyFields {
MsgId messageId = 0;
MsgId topMessageId = 0;
StoryId storyId = 0;
bool topicPost = false;
bool manualQuote = false;
uint32 quoteOffset : 30 = 0;
uint32 manualQuote : 1 = 0;
uint32 topicPost : 1 = 0;
};
[[nodiscard]] ReplyFields ReplyFieldsFromMTP(

View file

@ -270,19 +270,22 @@ bool IsItemScheduledUntilOnline(not_null<const HistoryItem*> item) {
ClickHandlerPtr JumpToMessageClickHandler(
not_null<HistoryItem*> item,
FullMsgId returnToId,
TextWithEntities highlightPart) {
TextWithEntities highlightPart,
int highlightPartOffsetHint) {
return JumpToMessageClickHandler(
item->history()->peer,
item->id,
returnToId,
std::move(highlightPart));
std::move(highlightPart),
highlightPartOffsetHint);
}
ClickHandlerPtr JumpToMessageClickHandler(
not_null<PeerData*> peer,
MsgId msgId,
FullMsgId returnToId,
TextWithEntities highlightPart) {
TextWithEntities highlightPart,
int highlightPartOffsetHint) {
return std::make_shared<LambdaClickHandler>([=] {
const auto separate = Core::App().separateWindowForPeer(peer);
const auto controller = separate
@ -293,6 +296,7 @@ ClickHandlerPtr JumpToMessageClickHandler(
Window::SectionShow::Way::Forward
};
params.highlightPart = highlightPart;
params.highlightPartOffsetHint = highlightPartOffsetHint;
params.origin = Window::SectionShow::OriginMessage{
returnToId
};
@ -392,8 +396,11 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
MTP_flags(Flag::f_reply_to_msg_id
| (replyToTop ? Flag::f_reply_to_top_id : Flag())
| (externalPeerId ? Flag::f_reply_to_peer_id : Flag())
| (replyTo.quote.empty() ? Flag() : Flag::f_quote)
| (replyTo.quote.empty() ? Flag() : Flag::f_quote_text)
| (replyTo.quote.empty()
? Flag()
: (Flag::f_quote
| Flag::f_quote_text
| Flag::f_quote_offset))
| (quoteEntities.v.empty()
? Flag()
: Flag::f_quote_entities)),
@ -403,7 +410,8 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
MTPMessageMedia(), // reply_media
MTP_int(replyToTop),
MTP_string(replyTo.quote.text),
quoteEntities);
quoteEntities,
MTP_int(replyTo.quoteOffset));
}
return MTPMessageReplyHeader();
}

View file

@ -124,11 +124,13 @@ struct SendingErrorRequest {
not_null<PeerData*> peer,
MsgId msgId,
FullMsgId returnToId = FullMsgId(),
TextWithEntities highlightPart = {});
TextWithEntities highlightPart = {},
int highlightPartOffsetHint = 0);
[[nodiscard]] ClickHandlerPtr JumpToMessageClickHandler(
not_null<HistoryItem*> item,
FullMsgId returnToId = FullMsgId(),
TextWithEntities highlightPart = {});
TextWithEntities highlightPart = {},
int highlightPartOffsetHint = 0);
[[nodiscard]] ClickHandlerPtr JumpToStoryClickHandler(
not_null<Data::Story*> story);
ClickHandlerPtr JumpToStoryClickHandler(

View file

@ -25,10 +25,8 @@ ElementHighlighter::ElementHighlighter(
, _animation(*this) {
}
void ElementHighlighter::enqueue(
not_null<Element*> view,
const TextWithEntities &part) {
const auto data = computeHighlight(view, part);
void ElementHighlighter::enqueue(const SelectedQuote &quote) {
const auto data = computeHighlight(quote);
if (_queue.empty() && !_animation.animating()) {
highlight(data);
} else if (_highlighted != data && !base::contains(_queue, data)) {
@ -37,10 +35,8 @@ void ElementHighlighter::enqueue(
}
}
void ElementHighlighter::highlight(
not_null<Element*> view,
const TextWithEntities &part) {
highlight(computeHighlight(view, part));
void ElementHighlighter::highlight(const SelectedQuote &quote) {
highlight(computeHighlight(quote));
}
void ElementHighlighter::checkNextHighlight() {
@ -75,9 +71,10 @@ Ui::ChatPaintHighlight ElementHighlighter::state(
}
ElementHighlighter::Highlight ElementHighlighter::computeHighlight(
not_null<const Element*> view,
const TextWithEntities &part) {
const auto item = view->data();
const SelectedQuote &quote) {
Assert(quote.item != nullptr);
const auto item = not_null(quote.item);
const auto owner = &item->history()->owner();
if (const auto group = owner->groups().find(item)) {
const auto leader = group->items.front();
@ -85,20 +82,19 @@ ElementHighlighter::Highlight ElementHighlighter::computeHighlight(
const auto i = ranges::find(group->items, item);
if (i != end(group->items)) {
const auto index = int(i - begin(group->items));
if (part.empty()) {
if (quote.text.empty()) {
return { leaderId, AddGroupItemSelection({}, index) };
} else if (const auto leaderView = _viewForItem(leader)) {
return {
leaderId,
leaderView->selectionFromQuote(item, part),
};
return { leaderId, leaderView->selectionFromQuote(quote) };
}
}
return { leaderId };
} else if (part.empty()) {
} else if (quote.text.empty()) {
return { item->fullId() };
} else if (const auto view = _viewForItem(item)) {
return { item->fullId(), view->selectionFromQuote(quote) };
}
return { item->fullId(), view->selectionFromQuote(item, part) };
return { item->fullId() };
}
void ElementHighlighter::highlight(Highlight data) {

View file

@ -23,6 +23,7 @@ struct ChatPaintHighlight;
namespace HistoryView {
class Element;
struct SelectedQuote;
class ElementHighlighter final {
public:
@ -33,8 +34,8 @@ public:
ViewForItem viewForItem,
RepaintView repaintView);
void enqueue(not_null<Element*> view, const TextWithEntities &part);
void highlight(not_null<Element*> view, const TextWithEntities &part);
void enqueue(const SelectedQuote &quote);
void highlight(const SelectedQuote &quote);
void clear();
[[nodiscard]] Ui::ChatPaintHighlight state(
@ -71,9 +72,7 @@ private:
friend inline bool operator==(Highlight, Highlight) = default;
};
[[nodiscard]] Highlight computeHighlight(
not_null<const Element*> view,
const TextWithEntities &part);
[[nodiscard]] Highlight computeHighlight(const SelectedQuote &quote);
void highlight(Highlight data);
void checkNextHighlight();
void repaintHighlightedItem(not_null<const Element*> view);

View file

@ -1075,7 +1075,7 @@ void HistoryWidget::initTabbedSelector() {
if (!data.recipientOverride) {
return true;
} else if (data.recipientOverride != _peer) {
showHistory(data.recipientOverride->id, ShowAtTheEndMsgId, {});
showHistory(data.recipientOverride->id, ShowAtTheEndMsgId);
}
return (data.recipientOverride == _peer);
}) | rpl::start_with_next([=](ChatHelpers::InlineChosen data) {
@ -1270,9 +1270,8 @@ void HistoryWidget::scrollToAnimationCallback(
}
void HistoryWidget::enqueueMessageHighlight(
not_null<HistoryView::Element*> view,
const TextWithEntities &part) {
_highlighter.enqueue(view, part);
const HistoryView::SelectedQuote &quote) {
_highlighter.enqueue(quote);
}
Ui::ChatPaintHighlight HistoryWidget::itemHighlight(
@ -1973,10 +1972,12 @@ bool HistoryWidget::insideJumpToEndInsteadOfToUnread() const {
void HistoryWidget::showHistory(
const PeerId &peerId,
MsgId showAtMsgId,
const TextWithEntities &highlightPart) {
const TextWithEntities &highlightPart,
int highlightPartOffsetHint) {
_pinnedClickedId = FullMsgId();
_minPinnedId = std::nullopt;
_showAtMsgHighlightPart = {};
_showAtMsgHighlightPartOffsetHint = 0;
const auto wasDialogsEntryState = computeDialogsEntryState();
const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
@ -2024,10 +2025,16 @@ void HistoryWidget::showHistory(
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())
).arg(showAtMsgId.bare));
delayedShowAt(showAtMsgId, highlightPart);
delayedShowAt(
showAtMsgId,
highlightPart,
highlightPartOffsetHint);
} else if (_showAtMsgId != showAtMsgId) {
clearAllLoadRequests();
setMsgId(showAtMsgId, highlightPart);
setMsgId(
showAtMsgId,
highlightPart,
highlightPartOffsetHint);
firstLoadMessages();
doneShow();
}
@ -2047,7 +2054,10 @@ void HistoryWidget::showHistory(
_cornerButtons.skipReplyReturn(skipId);
}
setMsgId(showAtMsgId, highlightPart);
setMsgId(
showAtMsgId,
highlightPart,
highlightPartOffsetHint);
if (_historyInited) {
DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
"Showing instant at %4."
@ -2151,6 +2161,7 @@ void HistoryWidget::showHistory(
_showAtMsgId = showAtMsgId;
_showAtMsgHighlightPart = highlightPart;
_showAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
_historyInited = false;
_contactStatus = nullptr;
@ -3305,7 +3316,10 @@ void HistoryWidget::messagesReceived(
}
_delayedShowAtRequest = 0;
setMsgId(_delayedShowAtMsgId, _delayedShowAtMsgHighlightPart);
setMsgId(
_delayedShowAtMsgId,
_delayedShowAtMsgHighlightPart,
_delayedShowAtMsgHighlightPartOffsetHint);
historyLoaded();
}
if (session().supportMode()) {
@ -3529,13 +3543,15 @@ void HistoryWidget::loadMessagesDown() {
void HistoryWidget::delayedShowAt(
MsgId showAtMsgId,
const TextWithEntities &highlightPart) {
const TextWithEntities &highlightPart,
int highlightPartOffsetHint) {
if (!_history) {
return;
}
if (_delayedShowAtMsgHighlightPart != highlightPart) {
_delayedShowAtMsgHighlightPart = highlightPart;
}
_delayedShowAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
if (_delayedShowAtRequest && _delayedShowAtMsgId == showAtMsgId) {
return;
}
@ -4120,10 +4136,12 @@ PeerData *HistoryWidget::peer() const {
// Sometimes _showAtMsgId is set directly.
void HistoryWidget::setMsgId(
MsgId showAtMsgId,
const TextWithEntities &highlightPart) {
const TextWithEntities &highlightPart,
int highlightPartOffsetHint) {
if (_showAtMsgHighlightPart != highlightPart) {
_showAtMsgHighlightPart = highlightPart;
}
_showAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
if (_showAtMsgId != showAtMsgId) {
_showAtMsgId = showAtMsgId;
if (_history) {
@ -4244,11 +4262,11 @@ void HistoryWidget::cornerButtonsShowAtPosition(
).arg(_history->peer->name()
).arg(_history->inboxReadTillId().bare
).arg(Logs::b(_history->loadedAtBottom())));
showHistory(_peer->id, ShowAtUnreadMsgId, {});
showHistory(_peer->id, ShowAtUnreadMsgId);
} else if (_peer && position.fullId.peer == _peer->id) {
showHistory(_peer->id, position.fullId.msg, {});
showHistory(_peer->id, position.fullId.msg);
} else if (_migrated && position.fullId.peer == _migrated->peer->id) {
showHistory(_peer->id, -position.fullId.msg, {});
showHistory(_peer->id, -position.fullId.msg);
}
}
@ -5700,9 +5718,11 @@ int HistoryWidget::countInitialScrollTop() {
const auto view = item->mainView();
Assert(view != nullptr);
enqueueMessageHighlight(
view,
base::take(_showAtMsgHighlightPart));
enqueueMessageHighlight({
item,
base::take(_showAtMsgHighlightPart),
base::take(_showAtMsgHighlightPartOffsetHint),
});
const auto result = itemTopForHighlight(view);
createUnreadBarIfBelowVisibleArea(result);
return result;
@ -6386,8 +6406,7 @@ void HistoryWidget::handlePeerMigration() {
if (_peer != channel) {
showHistory(
channel->id,
(_showAtMsgId > 0) ? (-_showAtMsgId) : _showAtMsgId,
{});
(_showAtMsgId > 0) ? (-_showAtMsgId) : _showAtMsgId);
channel->session().api().chatParticipants().requestCountDelayed(
channel);
} else {
@ -6483,7 +6502,7 @@ bool HistoryWidget::showSlowmodeError() {
if (const auto item = _history->latestSendingMessage()) {
if (const auto view = item->mainView()) {
animatedScrollToItem(item->id);
enqueueMessageHighlight(view, {});
enqueueMessageHighlight({ item });
}
return tr::lng_slowmode_no_many(tr::now);
}
@ -7149,7 +7168,7 @@ void HistoryWidget::replyToMessage(
if (isJoinChannel()) {
return;
}
_processingReplyTo = { .messageId = item->fullId(), .quote = quote};
_processingReplyTo = { .messageId = item->fullId(), .quote = quote };
_processingReplyItem = item;
processReply();
}

View file

@ -95,6 +95,7 @@ class Element;
class PinnedTracker;
class TranslateBar;
class ComposeSearch;
struct SelectedQuote;
} // namespace HistoryView
namespace HistoryView::Controls {
@ -149,7 +150,8 @@ public:
void firstLoadMessages();
void delayedShowAt(
MsgId showAtMsgId,
const TextWithEntities &highlightPart);
const TextWithEntities &highlightPart,
int highlightPartOffsetHint);
bool updateReplaceMediaButton();
void updateFieldPlaceholder();
@ -165,7 +167,8 @@ public:
PeerData *peer() const;
void setMsgId(
MsgId showAtMsgId,
const TextWithEntities &highlightPart = {});
const TextWithEntities &highlightPart = {},
int highlightPartOffsetHint = 0);
MsgId msgId() const;
bool hasTopBarShadow() const {
@ -182,9 +185,7 @@ public:
bool touchScroll(const QPoint &delta);
void enqueueMessageHighlight(
not_null<HistoryView::Element*> view,
const TextWithEntities &part);
void enqueueMessageHighlight(const HistoryView::SelectedQuote &quote);
[[nodiscard]] Ui::ChatPaintHighlight itemHighlight(
not_null<const HistoryItem*> item) const;
@ -228,7 +229,8 @@ public:
void showHistory(
const PeerId &peer,
MsgId showAtMsgId,
const TextWithEntities &highlightPart);
const TextWithEntities &highlightPart = {},
int highlightPartOffsetHint = 0);
void setChooseReportMessagesDetails(
Ui::ReportReason reason,
Fn<void(MessageIdsList)> callback);
@ -693,6 +695,7 @@ private:
bool _canSendTexts = false;
MsgId _showAtMsgId = ShowAtUnreadMsgId;
TextWithEntities _showAtMsgHighlightPart;
int _showAtMsgHighlightPartOffsetHint = 0;
int _firstLoadRequest = 0; // Not real mtpRequestId.
int _preloadRequest = 0; // Not real mtpRequestId.
@ -700,6 +703,7 @@ private:
MsgId _delayedShowAtMsgId = -1;
TextWithEntities _delayedShowAtMsgHighlightPart;
int _delayedShowAtMsgHighlightPartOffsetHint = 0;
int _delayedShowAtRequest = 0; // Not real mtpRequestId.
History *_supportPreloadHistory = nullptr;

View file

@ -235,7 +235,7 @@ rpl::producer<SelectedQuote> PreviewWrap::showQuoteSelector(
initElement();
_selection = _element->selectionFromQuote(item, quote.text);
_selection = _element->selectionFromQuote(quote);
return _selection.value(
) | rpl::map([=](TextSelection selection) {
if (const auto result = _element->selectedQuote(selection)) {
@ -643,6 +643,7 @@ void DraftOptionsBox(
if (const auto current = state->quote.current()) {
result.messageId = current.item->fullId();
result.quote = current.text;
result.quoteOffset = current.offset;
} else {
result.quote = {};
}

View file

@ -581,7 +581,9 @@ bool AddReplyToMessageAction(
const ContextMenuRequest &request,
not_null<ListWidget*> list) {
const auto context = list->elementContext();
const auto item = request.quoteItem ? request.quoteItem : request.item;
const auto item = request.quote.item
? request.quote.item
: request.item;
const auto topic = item ? item->topic() : nullptr;
const auto peer = item ? item->history()->peer.get() : nullptr;
if (!item
@ -598,7 +600,7 @@ bool AddReplyToMessageAction(
}
const auto &quote = request.quote;
auto text = quote.empty()
auto text = quote.text.empty()
? tr::lng_context_reply_msg(tr::now)
: tr::lng_context_quote_and_reply(tr::now);
text.replace('&', u"&&"_q);
@ -607,7 +609,11 @@ bool AddReplyToMessageAction(
if (!item) {
return;
} else {
list->replyToMessageRequestNotify({ itemId, quote });
list->replyToMessageRequestNotify({
.messageId = itemId,
.quote = quote.text,
.quoteOffset = quote.offset,
});
}
}, &st::menuIconReply);
return true;

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "base/unique_qptr.h"
#include "history/view/history_view_element.h"
namespace Data {
struct ReactionId;
@ -47,8 +48,7 @@ struct ContextMenuRequest {
HistoryItem *item = nullptr;
SelectedItems selectedItems;
TextForMimeData selectedText;
TextWithEntities quote;
HistoryItem *quoteItem = nullptr;
SelectedQuote quote;
bool overSelection = false;
PointState pointState = PointState();
};

View file

@ -1647,29 +1647,30 @@ SelectedQuote Element::FindSelectedQuote(
++i;
}
}
return { item, result };
return { item, result, modified.from };
}
TextSelection Element::FindSelectionFromQuote(
const Ui::Text::String &text,
not_null<HistoryItem*> item,
const TextWithEntities &quote) {
if (quote.empty()) {
const SelectedQuote &quote) {
Expects(quote.item != nullptr);
if (quote.text.empty()) {
return {};
}
const auto &original = item->originalText();
const auto &original = quote.item->originalText();
auto result = TextSelection();
auto offset = 0;
while (true) {
const auto i = original.text.indexOf(quote.text, offset);
const auto i = original.text.indexOf(quote.text.text, offset);
if (i < 0) {
return {};
}
auto selection = TextSelection{
uint16(i),
uint16(i + quote.text.size()),
uint16(i + quote.text.text.size()),
};
if (CheckQuoteEntities(quote.entities, original, selection)) {
if (CheckQuoteEntities(quote.text.entities, original, selection)) {
result = selection;
break;
}

View file

@ -270,6 +270,7 @@ struct TopicButton {
struct SelectedQuote {
HistoryItem *item = nullptr;
TextWithEntities text;
int offset = 0;
explicit operator bool() const {
return item && !text.empty();
@ -401,8 +402,7 @@ public:
virtual SelectedQuote selectedQuote(
TextSelection selection) const = 0;
virtual TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const = 0;
const SelectedQuote &quote) const = 0;
[[nodiscard]] virtual TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const;
@ -413,8 +413,7 @@ public:
not_null<HistoryItem*> item);
[[nodiscard]] static TextSelection FindSelectionFromQuote(
const Ui::Text::String &text,
not_null<HistoryItem*> item,
const TextWithEntities &quote);
const SelectedQuote &quote);
[[nodiscard]] virtual auto reactionButtonParameters(
QPoint position,

View file

@ -709,9 +709,10 @@ bool ListWidget::isBelowPosition(Data::MessagePosition position) const {
void ListWidget::highlightMessage(
FullMsgId itemId,
const TextWithEntities &part) {
const TextWithEntities &part,
int partOffsetHint) {
if (const auto view = viewForItem(itemId)) {
_highlighter.highlight(view, part);
_highlighter.highlight({ view->data(), part, partOffsetHint });
}
}
@ -787,7 +788,10 @@ bool ListWidget::showAtPositionNow(
computeScrollTo(*scrollTop, position, params.animated);
if (position != Data::MaxMessagePosition
&& position != Data::UnreadMessagePosition) {
highlightMessage(position.fullId, params.highlightPart);
highlightMessage(
position.fullId,
params.highlightPart,
params.highlightPartOffsetHint);
}
if (done) {
const auto found = !position.fullId.peer
@ -2143,7 +2147,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
} else if (item->isUnreadMention()
&& !item->isUnreadMedia()) {
readContents.insert(item);
_highlighter.enqueue(view, {});
_highlighter.enqueue({ item });
}
}
session->data().reactions().poll(item, context.now);
@ -2603,12 +2607,10 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
request.view = _overElement;
request.item = overItem;
request.pointState = _overState.pointState;
const auto quote = (_overElement
request.quote = (_overElement
&& _selectedTextItem == _overElement->data())
? _overElement->selectedQuote(_selectedTextRange)
: SelectedQuote();
request.quote = quote.text;
request.quoteItem = quote.item;
request.selectedText = _selectedText;
request.selectedItems = collectSelectedItems();
const auto hasSelection = !request.selectedItems.empty()

View file

@ -233,7 +233,8 @@ public:
bool isBelowPosition(Data::MessagePosition position) const;
void highlightMessage(
FullMsgId itemId,
const TextWithEntities &part);
const TextWithEntities &part,
int partOffsetHint);
void showAtPosition(
Data::MessagePosition position,

View file

@ -2684,11 +2684,13 @@ SelectedQuote Message::selectedQuote(TextSelection selection) const {
}
TextSelection Message::selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
if (quote.empty()) {
const SelectedQuote &quote) const {
Expects(quote.item != nullptr);
if (quote.text.empty()) {
return {};
}
const auto item = quote.item;
const auto &translated = item->translatedText();
const auto &original = item->originalText();
if (&translated != &original) {
@ -2697,11 +2699,11 @@ TextSelection Message::selectionFromQuote(
const auto media = this->media();
const auto mediaDisplayed = media && media->isDisplayed();
const auto mediaBefore = mediaDisplayed && invertMedia();
const auto result = FindSelectionFromQuote(text(), item, quote);
const auto result = FindSelectionFromQuote(text(), quote);
return mediaBefore ? media->unskipSelection(result) : result;
} else if (const auto media = this->media()) {
if (media->isDisplayed() || isHiddenByGroup()) {
return media->selectionFromQuote(item, quote);
return media->selectionFromQuote(quote);
}
}
return {};

View file

@ -97,8 +97,7 @@ public:
TextForMimeData selectedText(TextSelection selection) const override;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const override;
const SelectedQuote &quote) const override;
TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const override;

View file

@ -130,11 +130,13 @@ RepliesMemento::RepliesMemento(
not_null<History*> history,
MsgId rootId,
MsgId highlightId,
const TextWithEntities &highlightPart)
const TextWithEntities &highlightPart,
int highlightPartOffsetHint)
: _history(history)
, _rootId(rootId)
, _highlightId(highlightId)
, _highlightPart(highlightPart) {
, _highlightPart(highlightPart)
, _highlightPartOffsetHint(highlightPartOffsetHint)
, _highlightId(highlightId) {
if (highlightId) {
_list.setAroundPosition({
.fullId = FullMsgId(_history->peer->id, highlightId),
@ -2149,6 +2151,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
Window::SectionShow::Way::Forward,
anim::type::instant);
params.highlightPart = memento->highlightPart();
params.highlightPartOffsetHint = memento->highlightPartOffsetHint();
showAtPosition(Data::MessagePosition{
.fullId = FullMsgId(_history->peer->id, highlight),
.date = TimeId(0),

View file

@ -381,7 +381,8 @@ public:
not_null<History*> history,
MsgId rootId,
MsgId highlightId = 0,
const TextWithEntities &highlightPart = {});
const TextWithEntities &highlightPart = {},
int highlightPartOffsetHint = 0);
explicit RepliesMemento(
not_null<HistoryItem*> commentsItem,
MsgId commentId = 0);
@ -431,14 +432,18 @@ public:
[[nodiscard]] const TextWithEntities &highlightPart() const {
return _highlightPart;
}
[[nodiscard]] int highlightPartOffsetHint() const {
return _highlightPartOffsetHint;
}
private:
void setupTopicViewer();
const not_null<History*> _history;
MsgId _rootId = 0;
const MsgId _highlightId = 0;
const TextWithEntities _highlightPart;
const int _highlightPartOffsetHint = 0;
const MsgId _highlightId = 0;
ListMemento _list;
std::shared_ptr<Data::RepliesList> _replies;
QVector<FullMsgId> _replyReturns;

View file

@ -674,8 +674,7 @@ SelectedQuote Service::selectedQuote(TextSelection selection) const {
}
TextSelection Service::selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
const SelectedQuote &quote) const {
return {};
}

View file

@ -45,8 +45,7 @@ public:
TextForMimeData selectedText(TextSelection selection) const override;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const override;
const SelectedQuote &quote) const override;
TextSelection adjustSelection(
TextSelection selection,
TextSelectType type) const override;

View file

@ -1232,12 +1232,10 @@ SelectedQuote Document::selectedQuote(TextSelection selection) const {
}
TextSelection Document::selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
const SelectedQuote &quote) const {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
const auto result = Element::FindSelectionFromQuote(
captioned->caption,
item,
quote);
if (result.empty()) {
return {};

View file

@ -48,8 +48,7 @@ public:
TextForMimeData selectedText(TextSelection selection) const override;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const override;
const SelectedQuote &quote) const override;
bool uploading() const override;

View file

@ -1205,10 +1205,8 @@ SelectedQuote Gif::selectedQuote(TextSelection selection) const {
return Element::FindSelectedQuote(_caption, selection, _realParent);
}
TextSelection Gif::selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
return Element::FindSelectionFromQuote(_caption, item, quote);
TextSelection Gif::selectionFromQuote(const SelectedQuote &quote) const {
return Element::FindSelectionFromQuote(_caption, quote);
}
bool Gif::fullFeaturedGrouped(RectParts sides) const {

View file

@ -71,8 +71,7 @@ public:
TextForMimeData selectedText(TextSelection selection) const override;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const override;
const SelectedQuote &quote) const override;
bool uploading() const override;

View file

@ -92,8 +92,7 @@ public:
[[nodiscard]] virtual SelectedQuote selectedQuote(
TextSelection selection) const;
[[nodiscard]] virtual TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
const SelectedQuote &quote) const {
return {};
}

View file

@ -622,19 +622,20 @@ SelectedQuote GroupedMedia::selectedQuote(TextSelection selection) const {
}
TextSelection GroupedMedia::selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
const SelectedQuote &quote) const {
Expects(quote.item != nullptr);
if (_mode != Mode::Column) {
return (_captionItem == item)
? Element::FindSelectionFromQuote(_caption, item, quote)
return (_captionItem == quote.item)
? Element::FindSelectionFromQuote(_caption, quote)
: TextSelection();
}
const auto i = ranges::find(_parts, item, &Part::item);
const auto i = ranges::find(_parts, not_null(quote.item), &Part::item);
if (i == end(_parts)) {
return {};
}
const auto index = int(i - begin(_parts));
auto result = i->content->selectionFromQuote(item, quote);
auto result = i->content->selectionFromQuote(quote);
if (result.empty()) {
return AddGroupItemSelection({}, index);
}

View file

@ -57,8 +57,7 @@ public:
TextForMimeData selectedText(TextSelection selection) const override;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const override;
const SelectedQuote &quote) const override;
std::vector<Ui::BubbleSelectionInterval> getBubbleSelectionIntervals(
TextSelection selection) const override;

View file

@ -1098,10 +1098,8 @@ SelectedQuote Photo::selectedQuote(TextSelection selection) const {
return Element::FindSelectedQuote(_caption, selection, _realParent);
}
TextSelection Photo::selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const {
return Element::FindSelectionFromQuote(_caption, item, quote);
TextSelection Photo::selectionFromQuote(const SelectedQuote &quote) const {
return Element::FindSelectionFromQuote(_caption, quote);
}
void Photo::hideSpoilers() {

View file

@ -59,8 +59,7 @@ public:
TextForMimeData selectedText(TextSelection selection) const override;
SelectedQuote selectedQuote(TextSelection selection) const override;
TextSelection selectionFromQuote(
not_null<HistoryItem*> item,
const TextWithEntities &quote) const override;
const SelectedQuote &quote) const override;
PhotoData *getPhoto() const override {
return _data;

View file

@ -1411,7 +1411,11 @@ void MainWidget::showHistory(
&& way != Way::Forward) {
clearBotStartToken(_history->peer());
}
_history->showHistory(peerId, showAtMsgId, params.highlightPart);
_history->showHistory(
peerId,
showAtMsgId,
params.highlightPart,
params.highlightPartOffsetHint);
if (alreadyThatPeer && params.reapplyLocalDraft) {
_history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
}
@ -1772,7 +1776,7 @@ void MainWidget::showNewSection(
} else {
_mainSection = std::move(newMainSection);
_history->finishAnimating();
_history->showHistory(0, 0, {});
_history->showHistory(PeerId(), MsgId());
if (const auto entry = _mainSection->activeChat(); entry.key) {
_controller->setActiveChatEntry(entry);

View file

@ -101,7 +101,7 @@ channel#1981ea7e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#723027bd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories = ChatFull;
channelFull#723027bd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -170,12 +170,12 @@ messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_
messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction;
messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
messageActionRequestedPeer#fe77345d button_id:int peer:Peer = MessageAction;
messageActionSetChatWallPaper#bc44a927 wallpaper:WallPaper = MessageAction;
messageActionSetSameChatWallPaper#c0787d6d wallpaper:WallPaper = MessageAction;
messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction;
messageActionGiftCode#d2cfdb0e flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string = MessageAction;
messageActionGiveawayLaunch#332ba9ed = MessageAction;
messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
photoEmpty#2331b22d id:long = Photo;
@ -392,6 +392,9 @@ updateReadStories#f74e932b peer:Peer max_id:int = Update;
updateStoryID#1bf335b9 id:int random_id:long = Update;
updateStoriesStealthMode#2c084dc1 stealth_mode:StoriesStealthMode = Update;
updateSentStoryReaction#7d627683 peer:Peer story_id:int reaction:Reaction = Update;
updateBotChatBoost#904dd49c peer:Peer boost:Boost qts:int = Update;
updateChannelViewForumAsMessages#7b68920 channel_id:long enabled:Bool = Update;
updatePeerWallpaper#ae3f101d flags:# peer:Peer wallpaper:flags.0?WallPaper = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1232,7 +1235,7 @@ statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph
messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageInteractionCounters;
stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector<MessageInteractionCounters> = stats.BroadcastStats;
stats.broadcastStats#cb303962 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph reactions_by_emotion_graph:StatsGraph story_interactions_graph:StatsGraph story_reactions_by_emotion_graph:StatsGraph recent_message_interactions:Vector<MessageInteractionCounters> = stats.BroadcastStats;
help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;
@ -1264,14 +1267,14 @@ messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> use
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#6eebcabd flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true quote:flags.9?true reply_to_msg_id:flags.4?int reply_to_peer_id:flags.0?Peer reply_from:flags.5?MessageFwdHeader reply_media:flags.8?MessageMedia reply_to_top_id:flags.1?int quote_text:flags.6?string quote_entities:flags.7?Vector<MessageEntity> = MessageReplyHeader;
messageReplyHeader#afbc09db flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true quote:flags.9?true reply_to_msg_id:flags.4?int reply_to_peer_id:flags.0?Peer reply_from:flags.5?MessageFwdHeader reply_media:flags.8?MessageMedia reply_to_top_id:flags.1?int quote_text:flags.6?string quote_entities:flags.7?Vector<MessageEntity> quote_offset:flags.10?int = MessageReplyHeader;
messageReplyStoryHeader#9c98bfc1 user_id:long story_id:int = MessageReplyHeader;
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
stats.messageStats#7fe91c14 views_graph:StatsGraph reactions_by_emotion_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true rtmp_stream:flags.12?true listeners_hidden:flags.13?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
@ -1334,7 +1337,7 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
sponsoredMessage#daafff6b flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string webpage:flags.9?SponsoredWebPage message:string entities:flags.1?Vector<MessageEntity> sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
sponsoredMessage#ed5383f7 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string webpage:flags.9?SponsoredWebPage app:flags.10?BotApp message:string entities:flags.1?Vector<MessageEntity> button_text:flags.11?string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
@ -1544,7 +1547,7 @@ storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_co
storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date:int = StoryItem;
storyItem#44c457ce flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
storyItem#af6365a1 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;
@ -1557,7 +1560,7 @@ stories.storyViewsList#46e9b9ec flags:# count:int reactions_count:int views:Vect
stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews;
inputReplyToMessage#73ec805 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> = InputReplyTo;
inputReplyToMessage#22c0f6d5 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int = InputReplyTo;
inputReplyToStory#15b0f283 user_id:InputUser story_id:int = InputReplyTo;
exportedStoryLink#3fc9053b link:string = ExportedStoryLink;
@ -1596,6 +1599,10 @@ premium.myBoosts#9ae228e2 my_boosts:Vector<MyBoost> chats:Vector<Chat> users:Vec
premium.boostsStatus#4959427a flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int gift_boosts:flags.4?int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string prepaid_giveaways:flags.3?Vector<PrepaidGiveaway> my_boost_slots:flags.2?Vector<int> = premium.BoostsStatus;
storyFwdHeader#b826e150 flags:# from:flags.0?Peer from_name:flags.1?string story_id:flags.2?int = StoryFwdHeader;
stats.storyStats#50cd067c views_graph:StatsGraph reactions_by_emotion_graph:StatsGraph = stats.StoryStats;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1936,7 +1943,8 @@ messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList;
messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool;
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult;
messages.setChatWallPaper#8ffacae1 flags:# peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates;
messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates;
messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
updates.getState#edd4882a = updates.State;
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
@ -2038,6 +2046,8 @@ channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int =
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
channels.updateColor#621a201f flags:# channel:InputChannel color:int background_emoji_id:flags.0?long = Updates;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#83b70d97 channel:InputChannel = messages.Chats;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@ -2129,6 +2139,7 @@ stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats;
chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
@ -2143,7 +2154,7 @@ chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector<P
chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
stories.canSendStory#c7dfdfdd peer:InputPeer = Bool;
stories.sendStory#bcb73644 flags:# pinned:flags.2?true noforwards:flags.4?true peer:InputPeer media:InputMedia media_areas:flags.5?Vector<MediaArea> caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int = Updates;
stories.sendStory#e4e6694b flags:# pinned:flags.2?true noforwards:flags.4?true peer:InputPeer media:InputMedia media_areas:flags.5?Vector<MediaArea> caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int fwd_from_id:flags.6?InputPeer fwd_from_story:flags.6?int = Updates;
stories.editStory#b583ba46 flags:# peer:InputPeer id:int media:flags.0?InputMedia media_areas:flags.3?Vector<MediaArea> caption:flags.1?string entities:flags.1?Vector<MessageEntity> privacy_rules:flags.2?Vector<InputPrivacyRule> = Updates;
stories.deleteStories#ae59db5f peer:InputPeer id:Vector<int> = Vector<int>;
stories.togglePinned#9a75a1ef peer:InputPeer id:Vector<int> pinned:Bool = Vector<int>;
@ -2170,3 +2181,4 @@ premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:
premium.getMyBoosts#be77b4a = premium.MyBoosts;
premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts;
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList;

View file

@ -1 +1 @@
// LAYER 166
// LAYER 167

View file

@ -806,7 +806,8 @@ void SessionNavigation::showRepliesForMessage(
history,
rootId,
commentId,
params.highlightPart);
params.highlightPart,
params.highlightPartOffsetHint);
memento->setFromTopic(topic);
showSection(std::move(memento), params);
return;

View file

@ -168,6 +168,7 @@ struct SectionShow {
}
TextWithEntities highlightPart;
int highlightPartOffsetHint = 0;
Way way = Way::Forward;
anim::type animated = anim::type::normal;
anim::activation activation = anim::activation::normal;