diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index fbc6553cf..932990d62 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3607,7 +3607,13 @@ void ApiWrap::sendMessage(MessageToSend &&message) { 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( peer->id, _session->data().nextLocalMessageId()); @@ -3627,17 +3633,23 @@ void ApiWrap::sendMessage(MessageToSend &&message) { sendFlags |= MTPmessages_SendMessage::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); MTPMessageMedia media = MTP_messageMediaEmpty(); - if (message.webPage.removed) { + if (ignoreWebPage) { sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage; - } else if (const auto fields = message.webPage; fields.id) { + } else if (exactWebPage) { using PageFlag = MTPDmessageMediaWebPage::Flag; using PendingFlag = MTPDwebPagePending::Flag; + const auto &fields = message.webPage; const auto page = _session->data().webpage(fields.id); media = MTP_messageMediaWebPage( MTP_flags(PageFlag() - | (fields.manual ? PageFlag::f_manual : PageFlag()) + | (manualWebPage ? PageFlag::f_manual : PageFlag()) | (fields.forceLargeMedia ? PageFlag::f_force_large_media : PageFlag()) @@ -3645,17 +3657,15 @@ void ApiWrap::sendMessage(MessageToSend &&message) { ? PageFlag::f_force_small_media : PageFlag())), MTP_webPagePending( - MTP_flags(page->url.isEmpty() - ? PendingFlag() - : PendingFlag::f_url), - MTP_long(page->id), - MTP_string(page->url), + MTP_flags(PendingFlag::f_url), + MTP_long(fields.id), + MTP_string(fields.url), MTP_int(page->pendingTill))); } const auto anonymousPost = peer->amAnonymous(); const auto silentPost = ShouldSendSilent(peer, action.options); FillMessagePostFlags(action, peer, flags); - if (message.webPage.id && message.webPage.invert) { + if (exactWebPage && !ignoreWebPage && message.webPage.invert) { flags |= MessageFlag::InvertMedia; sendFlags |= MTPmessages_SendMessage::Flag::f_invert_media; mediaFlags |= MTPmessages_SendMedia::Flag::f_invert_media; @@ -3732,9 +3742,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) { UnixtimeFromMsgId(response.outerMsgId)); } }; - if (!message.webPage.removed - && (message.webPage.manual || sending.empty()) - && !message.webPage.url.isEmpty()) { + if (exactWebPage + && !ignoreWebPage + && (manualWebPage || sending.empty())) { histories.sendPreparedMessage( history, action.replyTo, @@ -3743,7 +3753,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { MTP_flags(mediaFlags), peer->input, Data::Histories::ReplyToPlaceholder(), - Data::WebPageForMTP(message.webPage), + Data::WebPageForMTP(message.webPage, true), msgText, MTP_long(randomId), MTPReplyMarkup(), @@ -3768,6 +3778,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { (sendAs ? sendAs->input : MTP_inputPeerEmpty()) ), done, fail); } + isFirst = false; } finishForwarding(action); diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index b988a9b3f..0ef3bceca 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -601,6 +601,12 @@ MessageLinksParser::MessageLinksParser(not_null field) _lifetime = _field->changes( ) | rpl::start_with_next([=] { const auto length = _field->getTextWithTags().text.size(); + if (!length) { + _lastLength = 0; + _timer.cancel(); + parse(); + return; + } const auto timeout = (std::abs(length - _lastLength) > 2) ? 0 : kParseLinksTimeout; @@ -652,6 +658,7 @@ void MessageLinksParser::parse() { const auto &tags = textWithTags.tags; const auto &markdownTags = _field->getMarkdownTags(); if (_disabled || text.isEmpty()) { + _ranges = {}; _list = QStringList(); return; } @@ -663,7 +670,7 @@ void MessageLinksParser::parse() { || (tag == Ui::InputField::kTagSpoiler); }; - auto ranges = QVector(); + _ranges.clear(); auto tag = tags.begin(); const auto tagsEnd = tags.end(); @@ -672,7 +679,7 @@ void MessageLinksParser::parse() { if (Ui::InputField::IsValidMarkdownLink(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; }; @@ -782,20 +789,18 @@ void MessageLinksParser::parse() { processTagsBefore(domainOffset); if (!hasTagsIntersection(range.start + range.length)) { if (markdownTagsAllow(range.start, range.length)) { - ranges.push_back(range); + _ranges.push_back(range); } } offset = matchOffset = p - start; } processTagsBefore(Ui::kQFixedMax); - apply(text, ranges); + applyRanges(text); } -void MessageLinksParser::apply( - const QString &text, - const QVector &ranges) { - const auto count = int(ranges.size()); +void MessageLinksParser::applyRanges(const QString &text) { + const auto count = int(_ranges.size()); const auto current = _list.current(); const auto computeLink = [&](const LinkRange &range) { return range.custom.isEmpty() @@ -807,7 +812,7 @@ void MessageLinksParser::apply( return true; } for (auto i = 0; i != count; ++i) { - if (computeLink(ranges[i]) != current[i]) { + if (computeLink(_ranges[i]) != current[i]) { return true; } } @@ -818,7 +823,7 @@ void MessageLinksParser::apply( } auto parsed = QStringList(); parsed.reserve(count); - for (const auto &range : ranges) { + for (const auto &range : _ranges) { parsed.push_back(computeLink(range).toString()); } _list = std::move(parsed); diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h index 9c4ba031f..d52d1d670 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.h +++ b/Telegram/SourceFiles/chat_helpers/message_field.h @@ -96,38 +96,38 @@ AutocompleteQuery ParseMentionHashtagBotCommandQuery( not_null field, ChatHelpers::ComposeFeatures features); -class MessageLinksParser : private QObject { +class MessageLinksParser final : private QObject { public: MessageLinksParser(not_null field); void parseNow(); void setDisabled(bool disabled); - [[nodiscard]] const rpl::variable &list() const; + struct LinkRange { + int start = 0; + int length = 0; + QString custom; -protected: - bool eventFilter(QObject *object, QEvent *event) override; + friend inline auto operator<=>( + const LinkRange&, + const LinkRange&) = default; + friend inline bool operator==( + const LinkRange&, + const LinkRange&) = default; + }; + + [[nodiscard]] const rpl::variable &list() const; + [[nodiscard]] const std::vector &ranges() const; private: - struct LinkRange { - 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); - } + bool eventFilter(QObject *object, QEvent *event) override; void parse(); - void apply(const QString &text, const QVector &ranges); + void applyRanges(const QString &text); not_null _field; rpl::variable _list; + std::vector _ranges; int _lastLength = 0; bool _disabled = false; base::Timer _timer; diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index fb20655a5..b1ba12e13 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -73,10 +73,12 @@ MTPInputReplyTo ReplyToForMTP( return MTPInputReplyTo(); } -MTPInputMedia WebPageForMTP(const Data::WebPageDraft &draft) { +MTPInputMedia WebPageForMTP( + const Data::WebPageDraft &draft, + bool required) { using Flag = MTPDinputMediaWebPage::Flag; return MTP_inputMediaWebPage( - MTP_flags(Flag::f_optional + MTP_flags((required ? Flag() : Flag::f_optional) | (draft.forceLargeMedia ? Flag::f_force_large_media : Flag()) | (draft.forceSmallMedia ? Flag::f_force_small_media : Flag())), MTP_string(draft.url)); diff --git a/Telegram/SourceFiles/data/data_histories.h b/Telegram/SourceFiles/data/data_histories.h index 3d9472bb0..dd2900a41 100644 --- a/Telegram/SourceFiles/data/data_histories.h +++ b/Telegram/SourceFiles/data/data_histories.h @@ -30,7 +30,9 @@ struct WebPageDraft; [[nodiscard]] MTPInputReplyTo ReplyToForMTP( not_null history, FullReplyTo replyTo); -[[nodiscard]] MTPInputMedia WebPageForMTP(const Data::WebPageDraft &draft); +[[nodiscard]] MTPInputMedia WebPageForMTP( + const Data::WebPageDraft &draft, + bool required = false); class Histories final { public: diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9a595520b..6d4985d97 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3941,6 +3941,9 @@ void HistoryWidget::send(Api::SendOptions options) { session().api().sendMessage(std::move(message)); clearFieldText(); + if (_preview) { + _preview->apply({ .removed = true }); + } _saveDraftText = true; _saveDraftStart = crl::now(); saveDraft(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 9150c5e50..c55046a1f 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -530,7 +530,7 @@ void FieldHeader::paintWebPage(Painter &p, not_null context) { Expects(!!_preview.parsed); const auto textTop = st::msgReplyPadding.top(); - auto previewLeft = st::historyReplySkip + st::msgReplyBarSkip; + auto previewLeft = st::historyReplySkip; const QRect to( previewLeft, @@ -1204,6 +1204,9 @@ void ComposeControls::clear() { {}, saveTextDraft ? TextUpdateEvent::SaveDraft : TextUpdateEvent()); cancelReplyMessage(); + if (_preview) { + _preview->apply({ .removed = true }); + } } void ComposeControls::setText(const TextWithTags &textWithTags) { @@ -1755,8 +1758,8 @@ void ComposeControls::fieldChanged() { && (_textUpdateEvents & TextUpdateEvent::SendTyping)); updateSendButtonType(); _hasSendText = HasSendText(_field); - if (!_hasSendText.current() && _preview) { - _preview->apply({}); + if (!_hasSendText.current() && _preview && !_preview->draft().manual) { + _preview->apply({ .removed = true }); } if (updateBotCommandShown() || updateLikeShown()) { updateControlsVisibility(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_webpage_processor.cpp b/Telegram/SourceFiles/history/view/controls/history_view_webpage_processor.cpp index 909e3508e..a3b402b29 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_webpage_processor.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_webpage_processor.cpp @@ -155,6 +155,9 @@ void WebpageProcessor::apply(Data::WebPageDraft draft, bool reparse) { _api.request(base::take(_requestId)).cancel(); if (draft.removed) { _draft = draft; + if (_parsedLinks.empty()) { + _draft.removed = false; + } _data = nullptr; _links = QStringList(); _link = QString(); @@ -272,6 +275,9 @@ void WebpageProcessor::checkNow(bool force) { void WebpageProcessor::checkPreview() { const auto previewRestricted = _history->peer && _history->peer->amRestricted(ChatRestriction::EmbedLinks); + if (_parsedLinks.empty()) { + _draft.removed = false; + } if (_draft.removed) { return; } else if (previewRestricted) { diff --git a/Telegram/lib_ui b/Telegram/lib_ui index b05f7eb91..383b5b8f7 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit b05f7eb915a86f67249904061d70f293066de618 +Subproject commit 383b5b8f7e629475e5f22445167aaa7669c5cdd6