Support highlighting correct quoted part.

This commit is contained in:
John Preston 2023-11-10 16:12:38 +04:00
parent dcc326e17f
commit 3a67e4f1f4
8 changed files with 101 additions and 24 deletions

View file

@ -2929,6 +2929,7 @@ FullReplyTo HistoryItem::replyTo() const {
if (const auto id = fields.messageId) { if (const auto id = fields.messageId) {
result.messageId = { replyToPeer, id }; result.messageId = { replyToPeer, id };
result.quote = fields.quote; result.quote = fields.quote;
result.quoteOffset = fields.quoteOffset;
} }
if (const auto id = fields.storyId) { if (const auto id = fields.storyId) {
result.storyId = { replyToPeer, id }; result.storyId = { replyToPeer, id };

View file

@ -6331,7 +6331,12 @@ void HistoryWidget::editDraftOptions() {
void HistoryWidget::jumpToReply(FullReplyTo to) { void HistoryWidget::jumpToReply(FullReplyTo to) {
if (const auto item = session().data().message(to.messageId)) { if (const auto item = session().data().message(to.messageId)) {
JumpToMessageClickHandler(item, {}, to.quote)->onClick({}); JumpToMessageClickHandler(
item,
{},
to.quote,
to.quoteOffset
)->onClick({});
} }
} }
@ -7158,17 +7163,22 @@ void HistoryWidget::clearFieldText(
void HistoryWidget::replyToMessage(FullReplyTo id) { void HistoryWidget::replyToMessage(FullReplyTo id) {
if (const auto item = session().data().message(id.messageId)) { if (const auto item = session().data().message(id.messageId)) {
replyToMessage(item, id.quote); replyToMessage(item, id.quote, id.quoteOffset);
} }
} }
void HistoryWidget::replyToMessage( void HistoryWidget::replyToMessage(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
TextWithEntities quote) { TextWithEntities quote,
int quoteOffset) {
if (isJoinChannel()) { if (isJoinChannel()) {
return; return;
} }
_processingReplyTo = { .messageId = item->fullId(), .quote = quote }; _processingReplyTo = {
.messageId = item->fullId(),
.quote = quote,
.quoteOffset = quoteOffset,
};
_processingReplyItem = item; _processingReplyItem = item;
processReply(); processReply();
} }

View file

@ -195,7 +195,8 @@ public:
void replyToMessage(FullReplyTo id); void replyToMessage(FullReplyTo id);
void replyToMessage( void replyToMessage(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
TextWithEntities quote = {}); TextWithEntities quote = {},
int quoteOffset = 0);
void editMessage(FullMsgId itemId); void editMessage(FullMsgId itemId);
void editMessage(not_null<HistoryItem*> item); void editMessage(not_null<HistoryItem*> item);

View file

@ -601,7 +601,11 @@ void DraftOptionsBox(
rpl::lifetime resolveLifetime; rpl::lifetime resolveLifetime;
}; };
const auto state = box->lifetime().make_state<State>(); const auto state = box->lifetime().make_state<State>();
state->quote = SelectedQuote{ replyItem, draft.reply.quote }; state->quote = SelectedQuote{
replyItem,
draft.reply.quote,
draft.reply.quoteOffset,
};
state->webpage = draft.webpage; state->webpage = draft.webpage;
state->preview = previewData; state->preview = previewData;
state->shown = previewData ? Section::Link : Section::Reply; state->shown = previewData ? Section::Link : Section::Reply;

View file

@ -1659,22 +1659,71 @@ TextSelection Element::FindSelectionFromQuote(
return {}; return {};
} }
const auto &original = quote.item->originalText(); const auto &original = quote.item->originalText();
auto result = TextSelection(); const auto length = int(original.text.size());
auto offset = 0; const auto qlength = int(quote.text.text.size());
while (true) { const auto checkAt = [&](int offset) {
const auto i = original.text.indexOf(quote.text.text, offset); const auto selection = TextSelection{
if (i < 0) { uint16(offset),
return {}; uint16(offset + qlength),
}
auto selection = TextSelection{
uint16(i),
uint16(i + quote.text.text.size()),
}; };
if (CheckQuoteEntities(quote.text.entities, original, selection)) { return CheckQuoteEntities(quote.text.entities, original, selection)
result = selection; ? selection
break; : TextSelection{ uint16(offset + 1), uint16(offset + 1) };
};
const auto findOneAfter = [&](int offset) {
if (offset > length - qlength) {
return TextSelection();
} }
offset = i + 1; const auto i = original.text.indexOf(quote.text.text, offset);
return (i >= 0) ? checkAt(i) : TextSelection();
};
const auto findOneBefore = [&](int offset) {
if (!offset) {
return TextSelection();
}
const auto end = std::min(offset + qlength - 1, length);
const auto from = end - length - 1;
const auto i = original.text.lastIndexOf(quote.text.text, from);
return (i >= 0) ? checkAt(i) : TextSelection();
};
const auto findAfter = [&](int offset) {
while (true) {
const auto result = findOneAfter(offset);
if (!result.empty() || result == TextSelection()) {
return result;
}
offset = result.from;
}
};
const auto findBefore = [&](int offset) {
while (true) {
const auto result = findOneBefore(offset);
if (!result.empty() || result == TextSelection()) {
return result;
}
offset = result.from - 2;
if (offset < 0) {
return result;
}
}
};
const auto findTwoWays = [&](int offset) {
const auto after = findAfter(offset);
if (after.empty()) {
return findBefore(offset);
} else if (after.from == offset) {
return after;
}
const auto before = findBefore(offset);
return before.empty()
? after
: (offset - before.from < after.from - offset)
? before
: after;
};
auto result = findTwoWays(quote.offset);
if (result.empty()) {
return {};
} }
for (const auto &modification : text.modifications()) { for (const auto &modification : text.modifications()) {
if (modification.position >= result.to) { if (modification.position >= result.to) {

View file

@ -797,7 +797,12 @@ void RepliesWidget::setupComposeControls() {
_composeControls->jumpToItemRequests( _composeControls->jumpToItemRequests(
) | rpl::start_with_next([=](FullReplyTo to) { ) | rpl::start_with_next([=](FullReplyTo to) {
if (const auto item = session().data().message(to.messageId)) { if (const auto item = session().data().message(to.messageId)) {
JumpToMessageClickHandler(item, {}, to.quote)->onClick({}); JumpToMessageClickHandler(
item,
{},
to.quote,
to.quoteOffset
)->onClick({});
} }
}, lifetime()); }, lifetime());

View file

@ -270,6 +270,7 @@ void Reply::setLinkFrom(
const auto quote = fields.manualQuote const auto quote = fields.manualQuote
? fields.quote ? fields.quote
: TextWithEntities(); : TextWithEntities();
const auto quoteOffset = fields.quoteOffset;
const auto returnToId = view->data()->fullId(); const auto returnToId = view->data()->fullId();
const auto externalLink = [=](ClickContext context) { const auto externalLink = [=](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>(); const auto my = context.other.value<ClickHandlerContext>();
@ -292,7 +293,8 @@ void Reply::setLinkFrom(
channel, channel,
messageId, messageId,
returnToId, returnToId,
quote quote,
quoteOffset
)->onClick(context); )->onClick(context);
} else { } else {
controller->showPeerInfo(channel); controller->showPeerInfo(channel);
@ -313,7 +315,7 @@ void Reply::setLinkFrom(
const auto message = data->resolvedMessage.get(); const auto message = data->resolvedMessage.get();
const auto story = data->resolvedStory.get(); const auto story = data->resolvedStory.get();
_link = message _link = message
? JumpToMessageClickHandler(message, returnToId, quote) ? JumpToMessageClickHandler(message, returnToId, quote, quoteOffset)
: story : story
? JumpToStoryClickHandler(story) ? JumpToStoryClickHandler(story)
: (data->external() : (data->external()

View file

@ -281,7 +281,12 @@ void ScheduledWidget::setupComposeControls() {
if (item->isScheduled() && item->history() == _history) { if (item->isScheduled() && item->history() == _history) {
showAtPosition(item->position()); showAtPosition(item->position());
} else { } else {
JumpToMessageClickHandler(item, {}, to.quote)->onClick({}); JumpToMessageClickHandler(
item,
{},
to.quote,
to.quoteOffset
)->onClick({});
} }
} }
}, lifetime()); }, lifetime());