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) {
result.messageId = { replyToPeer, id };
result.quote = fields.quote;
result.quoteOffset = fields.quoteOffset;
}
if (const auto id = fields.storyId) {
result.storyId = { replyToPeer, id };

View file

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

View file

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

View file

@ -601,7 +601,11 @@ void DraftOptionsBox(
rpl::lifetime resolveLifetime;
};
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->preview = previewData;
state->shown = previewData ? Section::Link : Section::Reply;

View file

@ -1659,22 +1659,71 @@ TextSelection Element::FindSelectionFromQuote(
return {};
}
const auto &original = quote.item->originalText();
auto result = TextSelection();
auto offset = 0;
while (true) {
const auto i = original.text.indexOf(quote.text.text, offset);
if (i < 0) {
return {};
}
auto selection = TextSelection{
uint16(i),
uint16(i + quote.text.text.size()),
const auto length = int(original.text.size());
const auto qlength = int(quote.text.text.size());
const auto checkAt = [&](int offset) {
const auto selection = TextSelection{
uint16(offset),
uint16(offset + qlength),
};
if (CheckQuoteEntities(quote.text.entities, original, selection)) {
result = selection;
break;
return CheckQuoteEntities(quote.text.entities, original, selection)
? selection
: 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()) {
if (modification.position >= result.to) {

View file

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

View file

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

View file

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