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

View file

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

View file

@ -1316,9 +1316,9 @@ ServiceAction ParseServiceAction(
}, [&](const MTPDmessageActionSetChatWallPaper &data) { }, [&](const MTPDmessageActionSetChatWallPaper &data) {
auto content = ActionSetChatWallPaper(); auto content = ActionSetChatWallPaper();
// #TODO wallpapers // #TODO wallpapers
content.same = data.is_same();
content.both = data.is_for_both();
result.content = content; result.content = content;
}, [&](const MTPDmessageActionSetSameChatWallPaper &data) {
result.content = ActionSetSameChatWallPaper();
}, [&](const MTPDmessageActionRequestedPeer &data) { }, [&](const MTPDmessageActionRequestedPeer &data) {
auto content = ActionRequestedPeer(); auto content = ActionRequestedPeer();
content.peerId = ParsePeerId(data.vpeer()); content.peerId = ParsePeerId(data.vpeer());
@ -1334,9 +1334,14 @@ ServiceAction ParseServiceAction(
content.months = data.vmonths().v; content.months = data.vmonths().v;
content.code = data.vslug().v; content.code = data.vslug().v;
result.content = content; result.content = content;
}, [&](const MTPDmessageActionGiveawayLaunch &) { }, [&](const MTPDmessageActionGiveawayLaunch &data) {
auto content = ActionGiveawayLaunch(); auto content = ActionGiveawayLaunch();
result.content = content; 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) {}); }, [](const MTPDmessageActionEmpty &data) {});
return result; return result;
} }

View file

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

View file

@ -1273,12 +1273,12 @@ auto HtmlWriter::Wrap::pushMessage(
}, [&](const ActionRequestedPeer &data) { }, [&](const ActionRequestedPeer &data) {
return "requested: "_q/* + data.peerId*/; return "requested: "_q/* + data.peerId*/;
}, [&](const ActionSetChatWallPaper &data) { }, [&](const ActionSetChatWallPaper &data) {
return serviceFrom + " set a new background for this chat";
}, [&](const ActionSetSameChatWallPaper &data) {
return serviceFrom return serviceFrom
+ " set " + (data.same
+ wrapReplyToLink("the same background") ? (" set "
+ " for this chat"; + wrapReplyToLink("the same background")
+ " for this chat")
: " set a new background for this chat");
}, [&](const ActionGiftCode &data) { }, [&](const ActionGiftCode &data) {
return data.unclaimed return data.unclaimed
? ("This is an unclaimed Telegram Premium for " ? ("This is an unclaimed Telegram Premium for "
@ -1297,6 +1297,10 @@ auto HtmlWriter::Wrap::pushMessage(
}, [&](const ActionGiveawayLaunch &data) { }, [&](const ActionGiveawayLaunch &data) {
return serviceFrom + " just started a giveaway " return serviceFrom + " just started a giveaway "
"of Telegram Premium subscriptions to its followers."; "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(); }); }, [](v::null_t) { return QByteArray(); });
if (!serviceText.isEmpty()) { if (!serviceText.isEmpty()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1411,7 +1411,11 @@ void MainWidget::showHistory(
&& way != Way::Forward) { && way != Way::Forward) {
clearBotStartToken(_history->peer()); clearBotStartToken(_history->peer());
} }
_history->showHistory(peerId, showAtMsgId, params.highlightPart); _history->showHistory(
peerId,
showAtMsgId,
params.highlightPart,
params.highlightPartOffsetHint);
if (alreadyThatPeer && params.reapplyLocalDraft) { if (alreadyThatPeer && params.reapplyLocalDraft) {
_history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry); _history->applyDraft(HistoryWidget::FieldHistoryAction::NewEntry);
} }
@ -1772,7 +1776,7 @@ void MainWidget::showNewSection(
} else { } else {
_mainSection = std::move(newMainSection); _mainSection = std::move(newMainSection);
_history->finishAnimating(); _history->finishAnimating();
_history->showHistory(0, 0, {}); _history->showHistory(PeerId(), MsgId());
if (const auto entry = _mainSection->activeChat(); entry.key) { if (const auto entry = _mainSection->activeChat(); entry.key) {
_controller->setActiveChatEntry(entry); _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; 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; 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; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = 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; 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; messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
messageActionRequestedPeer#fe77345d button_id:int peer:Peer = MessageAction; messageActionRequestedPeer#fe77345d button_id:int peer:Peer = MessageAction;
messageActionSetChatWallPaper#bc44a927 wallpaper:WallPaper = MessageAction; messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction;
messageActionSetSameChatWallPaper#c0787d6d 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; 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; 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; 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; 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; updateStoryID#1bf335b9 id:int random_id:long = Update;
updateStoriesStealthMode#2c084dc1 stealth_mode:StoriesStealthMode = Update; updateStoriesStealthMode#2c084dc1 stealth_mode:StoriesStealthMode = Update;
updateSentStoryReaction#7d627683 peer:Peer story_id:int reaction:Reaction = 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; 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; 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.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; 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; 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; 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; 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; 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; 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; 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.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = 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.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessagesEmpty#1839490f = 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; storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date: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.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; 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; 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; inputReplyToStory#15b0f283 user_id:InputUser story_id:int = InputReplyTo;
exportedStoryLink#3fc9053b link:string = ExportedStoryLink; 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; 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--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; 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.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool;
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp; 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.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.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; 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.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool; 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.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.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; 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.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.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.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.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool; 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; chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
stories.canSendStory#c7dfdfdd peer:InputPeer = Bool; 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.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.deleteStories#ae59db5f peer:InputPeer id:Vector<int> = Vector<int>;
stories.togglePinned#9a75a1ef peer:InputPeer id:Vector<int> pinned:Bool = 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.getMyBoosts#be77b4a = premium.MyBoosts;
premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts; premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts;
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus; 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, history,
rootId, rootId,
commentId, commentId,
params.highlightPart); params.highlightPart,
params.highlightPartOffsetHint);
memento->setFromTopic(topic); memento->setFromTopic(topic);
showSection(std::move(memento), params); showSection(std::move(memento), params);
return; return;

View file

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