Allow sending just webpage preview.

This commit is contained in:
John Preston 2023-10-26 09:26:45 +04:00
parent 041ec1157f
commit c035ec6917
9 changed files with 81 additions and 49 deletions

View File

@ -3607,7 +3607,13 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { const auto exactWebPage = !message.webPage.url.isEmpty();
auto isFirst = true;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)
|| (isFirst && exactWebPage)) {
TextUtilities::Trim(left);
const auto isLast = left.empty();
auto newId = FullMsgId( auto newId = FullMsgId(
peer->id, peer->id,
_session->data().nextLocalMessageId()); _session->data().nextLocalMessageId());
@ -3627,17 +3633,23 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to; sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to;
mediaFlags |= MTPmessages_SendMedia::Flag::f_reply_to; mediaFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
} }
const auto ignoreWebPage = message.webPage.removed
|| (exactWebPage && !isLast);
const auto manualWebPage = exactWebPage
&& !ignoreWebPage
&& (message.webPage.manual || (isLast && !isFirst));
const auto replyHeader = NewMessageReplyHeader(action); const auto replyHeader = NewMessageReplyHeader(action);
MTPMessageMedia media = MTP_messageMediaEmpty(); MTPMessageMedia media = MTP_messageMediaEmpty();
if (message.webPage.removed) { if (ignoreWebPage) {
sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage; sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage;
} else if (const auto fields = message.webPage; fields.id) { } else if (exactWebPage) {
using PageFlag = MTPDmessageMediaWebPage::Flag; using PageFlag = MTPDmessageMediaWebPage::Flag;
using PendingFlag = MTPDwebPagePending::Flag; using PendingFlag = MTPDwebPagePending::Flag;
const auto &fields = message.webPage;
const auto page = _session->data().webpage(fields.id); const auto page = _session->data().webpage(fields.id);
media = MTP_messageMediaWebPage( media = MTP_messageMediaWebPage(
MTP_flags(PageFlag() MTP_flags(PageFlag()
| (fields.manual ? PageFlag::f_manual : PageFlag()) | (manualWebPage ? PageFlag::f_manual : PageFlag())
| (fields.forceLargeMedia | (fields.forceLargeMedia
? PageFlag::f_force_large_media ? PageFlag::f_force_large_media
: PageFlag()) : PageFlag())
@ -3645,17 +3657,15 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
? PageFlag::f_force_small_media ? PageFlag::f_force_small_media
: PageFlag())), : PageFlag())),
MTP_webPagePending( MTP_webPagePending(
MTP_flags(page->url.isEmpty() MTP_flags(PendingFlag::f_url),
? PendingFlag() MTP_long(fields.id),
: PendingFlag::f_url), MTP_string(fields.url),
MTP_long(page->id),
MTP_string(page->url),
MTP_int(page->pendingTill))); MTP_int(page->pendingTill)));
} }
const auto anonymousPost = peer->amAnonymous(); const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, action.options); const auto silentPost = ShouldSendSilent(peer, action.options);
FillMessagePostFlags(action, peer, flags); FillMessagePostFlags(action, peer, flags);
if (message.webPage.id && message.webPage.invert) { if (exactWebPage && !ignoreWebPage && message.webPage.invert) {
flags |= MessageFlag::InvertMedia; flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMessage::Flag::f_invert_media; sendFlags |= MTPmessages_SendMessage::Flag::f_invert_media;
mediaFlags |= MTPmessages_SendMedia::Flag::f_invert_media; mediaFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
@ -3732,9 +3742,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
UnixtimeFromMsgId(response.outerMsgId)); UnixtimeFromMsgId(response.outerMsgId));
} }
}; };
if (!message.webPage.removed if (exactWebPage
&& (message.webPage.manual || sending.empty()) && !ignoreWebPage
&& !message.webPage.url.isEmpty()) { && (manualWebPage || sending.empty())) {
histories.sendPreparedMessage( histories.sendPreparedMessage(
history, history,
action.replyTo, action.replyTo,
@ -3743,7 +3753,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_flags(mediaFlags), MTP_flags(mediaFlags),
peer->input, peer->input,
Data::Histories::ReplyToPlaceholder(), Data::Histories::ReplyToPlaceholder(),
Data::WebPageForMTP(message.webPage), Data::WebPageForMTP(message.webPage, true),
msgText, msgText,
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
@ -3768,6 +3778,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty())
), done, fail); ), done, fail);
} }
isFirst = false;
} }
finishForwarding(action); finishForwarding(action);

View File

@ -601,6 +601,12 @@ MessageLinksParser::MessageLinksParser(not_null<Ui::InputField*> field)
_lifetime = _field->changes( _lifetime = _field->changes(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
const auto length = _field->getTextWithTags().text.size(); const auto length = _field->getTextWithTags().text.size();
if (!length) {
_lastLength = 0;
_timer.cancel();
parse();
return;
}
const auto timeout = (std::abs(length - _lastLength) > 2) const auto timeout = (std::abs(length - _lastLength) > 2)
? 0 ? 0
: kParseLinksTimeout; : kParseLinksTimeout;
@ -652,6 +658,7 @@ void MessageLinksParser::parse() {
const auto &tags = textWithTags.tags; const auto &tags = textWithTags.tags;
const auto &markdownTags = _field->getMarkdownTags(); const auto &markdownTags = _field->getMarkdownTags();
if (_disabled || text.isEmpty()) { if (_disabled || text.isEmpty()) {
_ranges = {};
_list = QStringList(); _list = QStringList();
return; return;
} }
@ -663,7 +670,7 @@ void MessageLinksParser::parse() {
|| (tag == Ui::InputField::kTagSpoiler); || (tag == Ui::InputField::kTagSpoiler);
}; };
auto ranges = QVector<LinkRange>(); _ranges.clear();
auto tag = tags.begin(); auto tag = tags.begin();
const auto tagsEnd = tags.end(); const auto tagsEnd = tags.end();
@ -672,7 +679,7 @@ void MessageLinksParser::parse() {
if (Ui::InputField::IsValidMarkdownLink(tag->id) if (Ui::InputField::IsValidMarkdownLink(tag->id)
&& !TextUtilities::IsMentionLink(tag->id)) { && !TextUtilities::IsMentionLink(tag->id)) {
ranges.push_back({ tag->offset, tag->length, tag->id }); _ranges.push_back({ tag->offset, tag->length, tag->id });
} }
++tag; ++tag;
}; };
@ -782,20 +789,18 @@ void MessageLinksParser::parse() {
processTagsBefore(domainOffset); processTagsBefore(domainOffset);
if (!hasTagsIntersection(range.start + range.length)) { if (!hasTagsIntersection(range.start + range.length)) {
if (markdownTagsAllow(range.start, range.length)) { if (markdownTagsAllow(range.start, range.length)) {
ranges.push_back(range); _ranges.push_back(range);
} }
} }
offset = matchOffset = p - start; offset = matchOffset = p - start;
} }
processTagsBefore(Ui::kQFixedMax); processTagsBefore(Ui::kQFixedMax);
apply(text, ranges); applyRanges(text);
} }
void MessageLinksParser::apply( void MessageLinksParser::applyRanges(const QString &text) {
const QString &text, const auto count = int(_ranges.size());
const QVector<LinkRange> &ranges) {
const auto count = int(ranges.size());
const auto current = _list.current(); const auto current = _list.current();
const auto computeLink = [&](const LinkRange &range) { const auto computeLink = [&](const LinkRange &range) {
return range.custom.isEmpty() return range.custom.isEmpty()
@ -807,7 +812,7 @@ void MessageLinksParser::apply(
return true; return true;
} }
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
if (computeLink(ranges[i]) != current[i]) { if (computeLink(_ranges[i]) != current[i]) {
return true; return true;
} }
} }
@ -818,7 +823,7 @@ void MessageLinksParser::apply(
} }
auto parsed = QStringList(); auto parsed = QStringList();
parsed.reserve(count); parsed.reserve(count);
for (const auto &range : ranges) { for (const auto &range : _ranges) {
parsed.push_back(computeLink(range).toString()); parsed.push_back(computeLink(range).toString());
} }
_list = std::move(parsed); _list = std::move(parsed);

View File

@ -96,38 +96,38 @@ AutocompleteQuery ParseMentionHashtagBotCommandQuery(
not_null<const Ui::InputField*> field, not_null<const Ui::InputField*> field,
ChatHelpers::ComposeFeatures features); ChatHelpers::ComposeFeatures features);
class MessageLinksParser : private QObject { class MessageLinksParser final : private QObject {
public: public:
MessageLinksParser(not_null<Ui::InputField*> field); MessageLinksParser(not_null<Ui::InputField*> field);
void parseNow(); void parseNow();
void setDisabled(bool disabled); void setDisabled(bool disabled);
[[nodiscard]] const rpl::variable<QStringList> &list() const; struct LinkRange {
int start = 0;
int length = 0;
QString custom;
protected: friend inline auto operator<=>(
bool eventFilter(QObject *object, QEvent *event) override; const LinkRange&,
const LinkRange&) = default;
friend inline bool operator==(
const LinkRange&,
const LinkRange&) = default;
};
[[nodiscard]] const rpl::variable<QStringList> &list() const;
[[nodiscard]] const std::vector<LinkRange> &ranges() const;
private: private:
struct LinkRange { bool eventFilter(QObject *object, QEvent *event) override;
int start;
int length;
QString custom;
};
friend inline bool operator==(const LinkRange &a, const LinkRange &b) {
return (a.start == b.start)
&& (a.length == b.length)
&& (a.custom == b.custom);
}
friend inline bool operator!=(const LinkRange &a, const LinkRange &b) {
return !(a == b);
}
void parse(); void parse();
void apply(const QString &text, const QVector<LinkRange> &ranges); void applyRanges(const QString &text);
not_null<Ui::InputField*> _field; not_null<Ui::InputField*> _field;
rpl::variable<QStringList> _list; rpl::variable<QStringList> _list;
std::vector<LinkRange> _ranges;
int _lastLength = 0; int _lastLength = 0;
bool _disabled = false; bool _disabled = false;
base::Timer _timer; base::Timer _timer;

View File

@ -73,10 +73,12 @@ MTPInputReplyTo ReplyToForMTP(
return MTPInputReplyTo(); return MTPInputReplyTo();
} }
MTPInputMedia WebPageForMTP(const Data::WebPageDraft &draft) { MTPInputMedia WebPageForMTP(
const Data::WebPageDraft &draft,
bool required) {
using Flag = MTPDinputMediaWebPage::Flag; using Flag = MTPDinputMediaWebPage::Flag;
return MTP_inputMediaWebPage( return MTP_inputMediaWebPage(
MTP_flags(Flag::f_optional MTP_flags((required ? Flag() : Flag::f_optional)
| (draft.forceLargeMedia ? Flag::f_force_large_media : Flag()) | (draft.forceLargeMedia ? Flag::f_force_large_media : Flag())
| (draft.forceSmallMedia ? Flag::f_force_small_media : Flag())), | (draft.forceSmallMedia ? Flag::f_force_small_media : Flag())),
MTP_string(draft.url)); MTP_string(draft.url));

View File

@ -30,7 +30,9 @@ struct WebPageDraft;
[[nodiscard]] MTPInputReplyTo ReplyToForMTP( [[nodiscard]] MTPInputReplyTo ReplyToForMTP(
not_null<History*> history, not_null<History*> history,
FullReplyTo replyTo); FullReplyTo replyTo);
[[nodiscard]] MTPInputMedia WebPageForMTP(const Data::WebPageDraft &draft); [[nodiscard]] MTPInputMedia WebPageForMTP(
const Data::WebPageDraft &draft,
bool required = false);
class Histories final { class Histories final {
public: public:

View File

@ -3941,6 +3941,9 @@ void HistoryWidget::send(Api::SendOptions options) {
session().api().sendMessage(std::move(message)); session().api().sendMessage(std::move(message));
clearFieldText(); clearFieldText();
if (_preview) {
_preview->apply({ .removed = true });
}
_saveDraftText = true; _saveDraftText = true;
_saveDraftStart = crl::now(); _saveDraftStart = crl::now();
saveDraft(); saveDraft();

View File

@ -530,7 +530,7 @@ void FieldHeader::paintWebPage(Painter &p, not_null<PeerData*> context) {
Expects(!!_preview.parsed); Expects(!!_preview.parsed);
const auto textTop = st::msgReplyPadding.top(); const auto textTop = st::msgReplyPadding.top();
auto previewLeft = st::historyReplySkip + st::msgReplyBarSkip; auto previewLeft = st::historyReplySkip;
const QRect to( const QRect to(
previewLeft, previewLeft,
@ -1204,6 +1204,9 @@ void ComposeControls::clear() {
{}, {},
saveTextDraft ? TextUpdateEvent::SaveDraft : TextUpdateEvent()); saveTextDraft ? TextUpdateEvent::SaveDraft : TextUpdateEvent());
cancelReplyMessage(); cancelReplyMessage();
if (_preview) {
_preview->apply({ .removed = true });
}
} }
void ComposeControls::setText(const TextWithTags &textWithTags) { void ComposeControls::setText(const TextWithTags &textWithTags) {
@ -1755,8 +1758,8 @@ void ComposeControls::fieldChanged() {
&& (_textUpdateEvents & TextUpdateEvent::SendTyping)); && (_textUpdateEvents & TextUpdateEvent::SendTyping));
updateSendButtonType(); updateSendButtonType();
_hasSendText = HasSendText(_field); _hasSendText = HasSendText(_field);
if (!_hasSendText.current() && _preview) { if (!_hasSendText.current() && _preview && !_preview->draft().manual) {
_preview->apply({}); _preview->apply({ .removed = true });
} }
if (updateBotCommandShown() || updateLikeShown()) { if (updateBotCommandShown() || updateLikeShown()) {
updateControlsVisibility(); updateControlsVisibility();

View File

@ -155,6 +155,9 @@ void WebpageProcessor::apply(Data::WebPageDraft draft, bool reparse) {
_api.request(base::take(_requestId)).cancel(); _api.request(base::take(_requestId)).cancel();
if (draft.removed) { if (draft.removed) {
_draft = draft; _draft = draft;
if (_parsedLinks.empty()) {
_draft.removed = false;
}
_data = nullptr; _data = nullptr;
_links = QStringList(); _links = QStringList();
_link = QString(); _link = QString();
@ -272,6 +275,9 @@ void WebpageProcessor::checkNow(bool force) {
void WebpageProcessor::checkPreview() { void WebpageProcessor::checkPreview() {
const auto previewRestricted = _history->peer const auto previewRestricted = _history->peer
&& _history->peer->amRestricted(ChatRestriction::EmbedLinks); && _history->peer->amRestricted(ChatRestriction::EmbedLinks);
if (_parsedLinks.empty()) {
_draft.removed = false;
}
if (_draft.removed) { if (_draft.removed) {
return; return;
} else if (previewRestricted) { } else if (previewRestricted) {

@ -1 +1 @@
Subproject commit b05f7eb915a86f67249904061d70f293066de618 Subproject commit 383b5b8f7e629475e5f22445167aaa7669c5cdd6