From 537c656ee14912961e1915fc5599b6105b19b53f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 3 Nov 2023 16:03:08 +0400 Subject: [PATCH] Show external reply media preview. --- .../SourceFiles/data/data_file_origin.cpp | 8 ++++ .../SourceFiles/data/data_media_types.cpp | 47 +++++++++---------- Telegram/SourceFiles/data/data_media_types.h | 1 + Telegram/SourceFiles/history/history_item.cpp | 6 +-- .../history/history_item_components.cpp | 29 ++++++++++-- .../history/history_item_components.h | 5 +- .../history/view/history_view_item_preview.h | 1 + .../history/view/history_view_reply.cpp | 35 ++++++++++++-- 8 files changed, 96 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/data/data_file_origin.cpp b/Telegram/SourceFiles/data/data_file_origin.cpp index d37663208..9cac2ec4b 100644 --- a/Telegram/SourceFiles/data/data_file_origin.cpp +++ b/Telegram/SourceFiles/data/data_file_origin.cpp @@ -86,9 +86,16 @@ struct FileReferenceAccumulator { }, [](const auto &data) { }); } + void push(const MTPMessageReplyHeader &data) { + data.match([&](const MTPDmessageReplyHeader &data) { + push(data.vreply_media()); + }, [](const MTPDmessageReplyStoryHeader &data) { + }); + } void push(const MTPMessage &data) { data.match([&](const MTPDmessage &data) { push(data.vmedia()); + push(data.vreply_to()); }, [&](const MTPDmessageService &data) { data.vaction().match( [&](const MTPDmessageActionChatEditPhoto &data) { @@ -99,6 +106,7 @@ struct FileReferenceAccumulator { push(data.vwallpaper()); }, [](const auto &data) { }); + push(data.vreply_to()); }, [](const MTPDmessageEmpty &data) { }); } diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index cb0278a27..79de11506 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -705,7 +705,7 @@ ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const { } } const auto type = tr::lng_in_dlg_photo(tr::now); - const auto caption = options.hideCaption + const auto caption = (options.hideCaption || options.ignoreMessageText) ? TextWithEntities() : options.translated ? parent()->translatedText() @@ -951,7 +951,7 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const { } return tr::lng_in_dlg_file(tr::now); }(); - const auto caption = options.hideCaption + const auto caption = (options.hideCaption || options.ignoreMessageText) ? TextWithEntities() : options.translated ? parent()->translatedText() @@ -1500,7 +1500,9 @@ bool MediaWebPage::replyPreviewLoaded() const { } ItemPreview MediaWebPage::toPreview(ToPreviewOptions options) const { - auto text = options.translated + auto text = options.ignoreMessageText + ? TextWithEntities() + : options.translated ? parent()->translatedText() : parent()->originalText(); if (text.empty()) { @@ -2038,28 +2040,23 @@ MediaStory::MediaStory( owner->registerStoryItem(storyId, parent); const auto stories = &owner->stories(); - if (const auto maybeStory = stories->lookup(storyId)) { - if (!_mention) { - parent->setText((*maybeStory)->caption()); - } - } else { - if (maybeStory.error() == NoStory::Unknown) { - stories->resolve(storyId, crl::guard(this, [=] { - if (const auto maybeStory = stories->lookup(storyId)) { - if (!_mention) { - parent->setText((*maybeStory)->caption()); - } - } else { - _expired = true; + const auto maybeStory = stories->lookup(storyId); + if (!maybeStory && maybeStory.error() == NoStory::Unknown) { + stories->resolve(storyId, crl::guard(this, [=] { + if (const auto maybeStory = stories->lookup(storyId)) { + if (!_mention && _viewMayExist) { + parent->setText((*maybeStory)->caption()); } - if (_mention) { - parent->updateStoryMentionText(); - } - parent->history()->owner().requestItemViewRefresh(parent); - })); - } else { - _expired = true; - } + } else { + _expired = true; + } + if (_mention) { + parent->updateStoryMentionText(); + } + parent->history()->owner().requestItemViewRefresh(parent); + })); + } else if (!maybeStory) { + _expired = true; } } @@ -2154,6 +2151,7 @@ std::unique_ptr MediaStory::createView( if (_mention) { return nullptr; } + _viewMayExist = true; return std::make_unique( message, realParent, @@ -2161,6 +2159,7 @@ std::unique_ptr MediaStory::createView( spoiler); } _expired = false; + _viewMayExist = true; const auto story = *maybeStory; if (_mention) { return std::make_unique( diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 9af199b4d..b62cf9825 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -623,6 +623,7 @@ public: private: const FullStoryId _storyId; const bool _mention = false; + bool _viewMayExist = false; bool _expired = false; }; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index f233b08d0..0a3d3563d 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -451,7 +451,7 @@ HistoryItem::HistoryItem( config.reply.topicPost = (topicRootId != 0); if (const auto originalReply = original->Get()) { if (originalReply->external()) { - config.reply = originalReply->fields(); + config.reply = originalReply->fields().clone(this); if (!config.reply.externalPeerId) { config.reply.messageId = 0; } @@ -3133,7 +3133,7 @@ void HistoryItem::createComponents(CreateConfig &&config) { UpdateComponents(mask); if (const auto reply = Get()) { - reply->set(config.reply); + reply->set(std::move(config.reply)); if (!reply->updateData(this)) { if (const auto messageId = reply->messageId()) { RequestDependentMessageItem( @@ -3456,7 +3456,7 @@ void HistoryItem::createComponents(const MTPDmessage &data) { }); } if (const auto reply = data.vreply_to()) { - config.reply = ReplyFieldsFromMTP(history(), *reply); + config.reply = ReplyFieldsFromMTP(this, *reply); } config.viaBotId = data.vvia_bot_id().value_or_empty(); config.viewsCount = data.vviews().value_or(-1); diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index c92f9f855..e4438b1c5 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -270,15 +270,33 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const { } } +ReplyFields ReplyFields::clone(not_null parent) const { + return { + .quote = quote, + .externalMedia = (externalMedia + ? externalMedia->clone(parent) + : nullptr), + .externalSenderId = externalSenderId, + .externalSenderName = externalSenderName, + .externalPostAuthor = externalPostAuthor, + .externalPeerId = externalPeerId, + .messageId = messageId, + .topMessageId = topMessageId, + .storyId = storyId, + .topicPost = topicPost, + .manualQuote = manualQuote, + }; +} + ReplyFields ReplyFieldsFromMTP( - not_null history, + not_null item, const MTPMessageReplyHeader &reply) { return reply.match([&](const MTPDmessageReplyHeader &data) { auto result = ReplyFields(); if (const auto peer = data.vreply_to_peer_id()) { result.externalPeerId = peerFromMTP(*peer); } - const auto owner = &history->owner(); + const auto owner = &item->history()->owner(); if (const auto id = data.vreply_to_msg_id().value_or_empty()) { result.messageId = data.is_reply_to_scheduled() ? owner->scheduledMessages().localMessageId(id) @@ -297,6 +315,9 @@ ReplyFields ReplyFieldsFromMTP( result.externalSenderName = qs(data.vfrom_name().value_or_empty()); } + if (const auto media = data.vreply_media()) { + result.externalMedia = HistoryItem::CreateMedia(item, *media); + } result.quote = TextWithEntities{ qs(data.vquote_text().value_or_empty()), Api::EntitiesFromMTP( @@ -357,6 +378,7 @@ HistoryMessageReply &HistoryMessageReply::operator=( HistoryMessageReply::~HistoryMessageReply() { // clearData() should be called by holder. Expects(resolvedMessage.empty()); + _fields.externalMedia = nullptr; } bool HistoryMessageReply::updateData( @@ -407,7 +429,8 @@ bool HistoryMessageReply::updateData( const auto displaying = resolvedMessage || resolvedStory - || (!_fields.quote.empty() && (!_fields.messageId || force)); + || ((!_fields.quote.empty() || _fields.externalMedia) + && (!_fields.messageId || force)); _displaying = displaying ? 1 : 0; const auto unavailable = !resolvedMessage diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index b23b509b0..f78671ba3 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -233,7 +233,10 @@ private: }; struct ReplyFields { + ReplyFields clone(not_null parent) const; + TextWithEntities quote; + std::unique_ptr externalMedia; PeerId externalSenderId = 0; QString externalSenderName; QString externalPostAuthor; @@ -246,7 +249,7 @@ struct ReplyFields { }; [[nodiscard]] ReplyFields ReplyFieldsFromMTP( - not_null history, + not_null item, const MTPMessageReplyHeader &reply); [[nodiscard]] FullReplyTo ReplyToFromMTP( diff --git a/Telegram/SourceFiles/history/view/history_view_item_preview.h b/Telegram/SourceFiles/history/view/history_view_item_preview.h index f2304c49e..b29da9442 100644 --- a/Telegram/SourceFiles/history/view/history_view_item_preview.h +++ b/Telegram/SourceFiles/history/view/history_view_item_preview.h @@ -40,6 +40,7 @@ struct ToPreviewOptions { const std::vector *existing = nullptr; bool hideSender = false; bool hideCaption = false; + bool ignoreMessageText = false; bool generateImages = true; bool ignoreGroup = false; bool ignoreTopic = true; diff --git a/Telegram/SourceFiles/history/view/history_view_reply.cpp b/Telegram/SourceFiles/history/view/history_view_reply.cpp index 5d9722231..8ed051297 100644 --- a/Telegram/SourceFiles/history/view/history_view_reply.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reply.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_story.h" #include "data/data_user.h" +#include "history/view/history_view_item_preview.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_item_components.h" @@ -176,6 +177,7 @@ void Reply::update( const auto &fields = data->fields(); const auto message = data->resolvedMessage.get(); const auto story = data->resolvedStory.get(); + const auto externalMedia = fields.externalMedia.get(); if (!_externalSender) { if (const auto id = fields.externalSenderId) { _externalSender = view->history()->owner().peer(id); @@ -195,7 +197,8 @@ void Reply::update( const auto hasPreview = (story && story->hasReplyPreview()) || (message && message->media() - && message->media()->hasReplyPreview()); + && message->media()->hasReplyPreview()) + || (externalMedia && externalMedia->hasReplyPreview()); _hasPreview = hasPreview ? 1 : 0; _displaying = data->displaying() ? 1 : 0; _multiline = data->multiline() ? 1 : 0; @@ -213,6 +216,15 @@ void Reply::update( ? message->inReplyText() : story ? story->inReplyText() + : externalMedia + ? externalMedia->toPreview({ + .hideSender = true, + .hideCaption = true, + .ignoreMessageText = true, + .generateImages = false, + .ignoreGroup = true, + .ignoreTopic = true, + }).text : TextWithEntities(); const auto repaint = [=] { item->customEmojiRepaint(); }; const auto context = Core::MarkedTextContext{ @@ -222,7 +234,7 @@ void Reply::update( _text.setMarkedText( st::defaultTextStyle, text, - Ui::DialogTextOptions(), + _multiline ? Ui::ItemTextDefaultOptions() : Ui::DialogTextOptions(), context); updateName(view, data); @@ -388,7 +400,9 @@ void Reply::updateName( const auto externalPeer = fields.externalPeerId ? view->history()->owner().peer(fields.externalPeerId).get() : nullptr; - const auto groupNameAdded = (externalPeer && externalPeer != sender); + const auto groupNameAdded = externalPeer + && (externalPeer != sender) + && (externalPeer->isChat() || externalPeer->isMegagroup()); const auto shorten = !viaBotUsername.isEmpty() || groupNameAdded; const auto name = sender ? senderName(sender, shorten) @@ -508,10 +522,16 @@ int Reply::resizeToWidth(int width) const { auto elided = false; const auto texth = _text.countDimensions( textGeometry(innerw, firstLineSkip, &lineCounter, &elided)).height; + const auto useh = elided + ? (kNonExpandedLinesLimit * st::normalFont->height) + : std::max(texth, st::normalFont->height); + if (!texth) { + int a = 0; + } _expandable = (_multiline && elided) ? 1 : 0; _height = st::historyReplyPadding.top() + nameh - + (elided ? kNonExpandedLinesLimit * st::normalFont->height : texth) + + useh + st::historyReplyPadding.bottom(); return height(); } @@ -551,7 +571,10 @@ QSize Reply::countMultilineOptimalSize( const auto max = previewSkip + _text.maxWidth(); const auto result = _text.countDimensions( textGeometry(max, previewSkip, &lineCounter, &elided)); - return { result.width, result.height }; + return { + result.width, + std::max(result.height, st::normalFont->height), + }; } void Reply::paint( @@ -663,6 +686,8 @@ void Reply::paint( ? nullptr : data->resolvedStory ? data->resolvedStory->replyPreview() + : data->fields().externalMedia + ? data->fields().externalMedia->replyPreview() : nullptr; if (image) { auto to = style::rtlrect(