Show external reply media preview.

This commit is contained in:
John Preston 2023-11-03 16:03:08 +04:00
parent 4e0490494e
commit 537c656ee1
8 changed files with 96 additions and 36 deletions

View file

@ -86,9 +86,16 @@ struct FileReferenceAccumulator {
}, [](const auto &data) { }, [](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) { void push(const MTPMessage &data) {
data.match([&](const MTPDmessage &data) { data.match([&](const MTPDmessage &data) {
push(data.vmedia()); push(data.vmedia());
push(data.vreply_to());
}, [&](const MTPDmessageService &data) { }, [&](const MTPDmessageService &data) {
data.vaction().match( data.vaction().match(
[&](const MTPDmessageActionChatEditPhoto &data) { [&](const MTPDmessageActionChatEditPhoto &data) {
@ -99,6 +106,7 @@ struct FileReferenceAccumulator {
push(data.vwallpaper()); push(data.vwallpaper());
}, [](const auto &data) { }, [](const auto &data) {
}); });
push(data.vreply_to());
}, [](const MTPDmessageEmpty &data) { }, [](const MTPDmessageEmpty &data) {
}); });
} }

View file

@ -705,7 +705,7 @@ ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const {
} }
} }
const auto type = tr::lng_in_dlg_photo(tr::now); const auto type = tr::lng_in_dlg_photo(tr::now);
const auto caption = options.hideCaption const auto caption = (options.hideCaption || options.ignoreMessageText)
? TextWithEntities() ? TextWithEntities()
: options.translated : options.translated
? parent()->translatedText() ? parent()->translatedText()
@ -951,7 +951,7 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
} }
return tr::lng_in_dlg_file(tr::now); return tr::lng_in_dlg_file(tr::now);
}(); }();
const auto caption = options.hideCaption const auto caption = (options.hideCaption || options.ignoreMessageText)
? TextWithEntities() ? TextWithEntities()
: options.translated : options.translated
? parent()->translatedText() ? parent()->translatedText()
@ -1500,7 +1500,9 @@ bool MediaWebPage::replyPreviewLoaded() const {
} }
ItemPreview MediaWebPage::toPreview(ToPreviewOptions options) const { ItemPreview MediaWebPage::toPreview(ToPreviewOptions options) const {
auto text = options.translated auto text = options.ignoreMessageText
? TextWithEntities()
: options.translated
? parent()->translatedText() ? parent()->translatedText()
: parent()->originalText(); : parent()->originalText();
if (text.empty()) { if (text.empty()) {
@ -2038,28 +2040,23 @@ MediaStory::MediaStory(
owner->registerStoryItem(storyId, parent); owner->registerStoryItem(storyId, parent);
const auto stories = &owner->stories(); const auto stories = &owner->stories();
if (const auto maybeStory = stories->lookup(storyId)) { const auto maybeStory = stories->lookup(storyId);
if (!_mention) { if (!maybeStory && maybeStory.error() == NoStory::Unknown) {
parent->setText((*maybeStory)->caption()); stories->resolve(storyId, crl::guard(this, [=] {
} if (const auto maybeStory = stories->lookup(storyId)) {
} else { if (!_mention && _viewMayExist) {
if (maybeStory.error() == NoStory::Unknown) { parent->setText((*maybeStory)->caption());
stories->resolve(storyId, crl::guard(this, [=] {
if (const auto maybeStory = stories->lookup(storyId)) {
if (!_mention) {
parent->setText((*maybeStory)->caption());
}
} else {
_expired = true;
} }
if (_mention) { } else {
parent->updateStoryMentionText(); _expired = true;
} }
parent->history()->owner().requestItemViewRefresh(parent); if (_mention) {
})); parent->updateStoryMentionText();
} else { }
_expired = true; parent->history()->owner().requestItemViewRefresh(parent);
} }));
} else if (!maybeStory) {
_expired = true;
} }
} }
@ -2154,6 +2151,7 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
if (_mention) { if (_mention) {
return nullptr; return nullptr;
} }
_viewMayExist = true;
return std::make_unique<HistoryView::Photo>( return std::make_unique<HistoryView::Photo>(
message, message,
realParent, realParent,
@ -2161,6 +2159,7 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
spoiler); spoiler);
} }
_expired = false; _expired = false;
_viewMayExist = true;
const auto story = *maybeStory; const auto story = *maybeStory;
if (_mention) { if (_mention) {
return std::make_unique<HistoryView::ServiceBox>( return std::make_unique<HistoryView::ServiceBox>(

View file

@ -623,6 +623,7 @@ public:
private: private:
const FullStoryId _storyId; const FullStoryId _storyId;
const bool _mention = false; const bool _mention = false;
bool _viewMayExist = false;
bool _expired = false; bool _expired = false;
}; };

View file

@ -451,7 +451,7 @@ HistoryItem::HistoryItem(
config.reply.topicPost = (topicRootId != 0); config.reply.topicPost = (topicRootId != 0);
if (const auto originalReply = original->Get<HistoryMessageReply>()) { if (const auto originalReply = original->Get<HistoryMessageReply>()) {
if (originalReply->external()) { if (originalReply->external()) {
config.reply = originalReply->fields(); config.reply = originalReply->fields().clone(this);
if (!config.reply.externalPeerId) { if (!config.reply.externalPeerId) {
config.reply.messageId = 0; config.reply.messageId = 0;
} }
@ -3133,7 +3133,7 @@ void HistoryItem::createComponents(CreateConfig &&config) {
UpdateComponents(mask); UpdateComponents(mask);
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
reply->set(config.reply); reply->set(std::move(config.reply));
if (!reply->updateData(this)) { if (!reply->updateData(this)) {
if (const auto messageId = reply->messageId()) { if (const auto messageId = reply->messageId()) {
RequestDependentMessageItem( RequestDependentMessageItem(
@ -3456,7 +3456,7 @@ void HistoryItem::createComponents(const MTPDmessage &data) {
}); });
} }
if (const auto reply = data.vreply_to()) { 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.viaBotId = data.vvia_bot_id().value_or_empty();
config.viewsCount = data.vviews().value_or(-1); config.viewsCount = data.vviews().value_or(-1);

View file

@ -270,15 +270,33 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {
} }
} }
ReplyFields ReplyFields::clone(not_null<HistoryItem*> 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( ReplyFields ReplyFieldsFromMTP(
not_null<History*> history, not_null<HistoryItem*> item,
const MTPMessageReplyHeader &reply) { const MTPMessageReplyHeader &reply) {
return reply.match([&](const MTPDmessageReplyHeader &data) { return reply.match([&](const MTPDmessageReplyHeader &data) {
auto result = ReplyFields(); auto result = ReplyFields();
if (const auto peer = data.vreply_to_peer_id()) { if (const auto peer = data.vreply_to_peer_id()) {
result.externalPeerId = peerFromMTP(*peer); 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()) { if (const auto id = data.vreply_to_msg_id().value_or_empty()) {
result.messageId = data.is_reply_to_scheduled() result.messageId = data.is_reply_to_scheduled()
? owner->scheduledMessages().localMessageId(id) ? owner->scheduledMessages().localMessageId(id)
@ -297,6 +315,9 @@ ReplyFields ReplyFieldsFromMTP(
result.externalSenderName result.externalSenderName
= qs(data.vfrom_name().value_or_empty()); = qs(data.vfrom_name().value_or_empty());
} }
if (const auto media = data.vreply_media()) {
result.externalMedia = HistoryItem::CreateMedia(item, *media);
}
result.quote = TextWithEntities{ result.quote = TextWithEntities{
qs(data.vquote_text().value_or_empty()), qs(data.vquote_text().value_or_empty()),
Api::EntitiesFromMTP( Api::EntitiesFromMTP(
@ -357,6 +378,7 @@ HistoryMessageReply &HistoryMessageReply::operator=(
HistoryMessageReply::~HistoryMessageReply() { HistoryMessageReply::~HistoryMessageReply() {
// clearData() should be called by holder. // clearData() should be called by holder.
Expects(resolvedMessage.empty()); Expects(resolvedMessage.empty());
_fields.externalMedia = nullptr;
} }
bool HistoryMessageReply::updateData( bool HistoryMessageReply::updateData(
@ -407,7 +429,8 @@ bool HistoryMessageReply::updateData(
const auto displaying = resolvedMessage const auto displaying = resolvedMessage
|| resolvedStory || resolvedStory
|| (!_fields.quote.empty() && (!_fields.messageId || force)); || ((!_fields.quote.empty() || _fields.externalMedia)
&& (!_fields.messageId || force));
_displaying = displaying ? 1 : 0; _displaying = displaying ? 1 : 0;
const auto unavailable = !resolvedMessage const auto unavailable = !resolvedMessage

View file

@ -233,7 +233,10 @@ private:
}; };
struct ReplyFields { struct ReplyFields {
ReplyFields clone(not_null<HistoryItem*> parent) const;
TextWithEntities quote; TextWithEntities quote;
std::unique_ptr<Data::Media> externalMedia;
PeerId externalSenderId = 0; PeerId externalSenderId = 0;
QString externalSenderName; QString externalSenderName;
QString externalPostAuthor; QString externalPostAuthor;
@ -246,7 +249,7 @@ struct ReplyFields {
}; };
[[nodiscard]] ReplyFields ReplyFieldsFromMTP( [[nodiscard]] ReplyFields ReplyFieldsFromMTP(
not_null<History*> history, not_null<HistoryItem*> item,
const MTPMessageReplyHeader &reply); const MTPMessageReplyHeader &reply);
[[nodiscard]] FullReplyTo ReplyToFromMTP( [[nodiscard]] FullReplyTo ReplyToFromMTP(

View file

@ -40,6 +40,7 @@ struct ToPreviewOptions {
const std::vector<ItemPreviewImage> *existing = nullptr; const std::vector<ItemPreviewImage> *existing = nullptr;
bool hideSender = false; bool hideSender = false;
bool hideCaption = false; bool hideCaption = false;
bool ignoreMessageText = false;
bool generateImages = true; bool generateImages = true;
bool ignoreGroup = false; bool ignoreGroup = false;
bool ignoreTopic = true; bool ignoreTopic = true;

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_story.h" #include "data/data_story.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "history/view/history_view_item_preview.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_item_components.h" #include "history/history_item_components.h"
@ -176,6 +177,7 @@ void Reply::update(
const auto &fields = data->fields(); const auto &fields = data->fields();
const auto message = data->resolvedMessage.get(); const auto message = data->resolvedMessage.get();
const auto story = data->resolvedStory.get(); const auto story = data->resolvedStory.get();
const auto externalMedia = fields.externalMedia.get();
if (!_externalSender) { if (!_externalSender) {
if (const auto id = fields.externalSenderId) { if (const auto id = fields.externalSenderId) {
_externalSender = view->history()->owner().peer(id); _externalSender = view->history()->owner().peer(id);
@ -195,7 +197,8 @@ void Reply::update(
const auto hasPreview = (story && story->hasReplyPreview()) const auto hasPreview = (story && story->hasReplyPreview())
|| (message || (message
&& message->media() && message->media()
&& message->media()->hasReplyPreview()); && message->media()->hasReplyPreview())
|| (externalMedia && externalMedia->hasReplyPreview());
_hasPreview = hasPreview ? 1 : 0; _hasPreview = hasPreview ? 1 : 0;
_displaying = data->displaying() ? 1 : 0; _displaying = data->displaying() ? 1 : 0;
_multiline = data->multiline() ? 1 : 0; _multiline = data->multiline() ? 1 : 0;
@ -213,6 +216,15 @@ void Reply::update(
? message->inReplyText() ? message->inReplyText()
: story : story
? story->inReplyText() ? story->inReplyText()
: externalMedia
? externalMedia->toPreview({
.hideSender = true,
.hideCaption = true,
.ignoreMessageText = true,
.generateImages = false,
.ignoreGroup = true,
.ignoreTopic = true,
}).text
: TextWithEntities(); : TextWithEntities();
const auto repaint = [=] { item->customEmojiRepaint(); }; const auto repaint = [=] { item->customEmojiRepaint(); };
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
@ -222,7 +234,7 @@ void Reply::update(
_text.setMarkedText( _text.setMarkedText(
st::defaultTextStyle, st::defaultTextStyle,
text, text,
Ui::DialogTextOptions(), _multiline ? Ui::ItemTextDefaultOptions() : Ui::DialogTextOptions(),
context); context);
updateName(view, data); updateName(view, data);
@ -388,7 +400,9 @@ void Reply::updateName(
const auto externalPeer = fields.externalPeerId const auto externalPeer = fields.externalPeerId
? view->history()->owner().peer(fields.externalPeerId).get() ? view->history()->owner().peer(fields.externalPeerId).get()
: nullptr; : 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 shorten = !viaBotUsername.isEmpty() || groupNameAdded;
const auto name = sender const auto name = sender
? senderName(sender, shorten) ? senderName(sender, shorten)
@ -508,10 +522,16 @@ int Reply::resizeToWidth(int width) const {
auto elided = false; auto elided = false;
const auto texth = _text.countDimensions( const auto texth = _text.countDimensions(
textGeometry(innerw, firstLineSkip, &lineCounter, &elided)).height; 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; _expandable = (_multiline && elided) ? 1 : 0;
_height = st::historyReplyPadding.top() _height = st::historyReplyPadding.top()
+ nameh + nameh
+ (elided ? kNonExpandedLinesLimit * st::normalFont->height : texth) + useh
+ st::historyReplyPadding.bottom(); + st::historyReplyPadding.bottom();
return height(); return height();
} }
@ -551,7 +571,10 @@ QSize Reply::countMultilineOptimalSize(
const auto max = previewSkip + _text.maxWidth(); const auto max = previewSkip + _text.maxWidth();
const auto result = _text.countDimensions( const auto result = _text.countDimensions(
textGeometry(max, previewSkip, &lineCounter, &elided)); textGeometry(max, previewSkip, &lineCounter, &elided));
return { result.width, result.height }; return {
result.width,
std::max(result.height, st::normalFont->height),
};
} }
void Reply::paint( void Reply::paint(
@ -663,6 +686,8 @@ void Reply::paint(
? nullptr ? nullptr
: data->resolvedStory : data->resolvedStory
? data->resolvedStory->replyPreview() ? data->resolvedStory->replyPreview()
: data->fields().externalMedia
? data->fields().externalMedia->replyPreview()
: nullptr; : nullptr;
if (image) { if (image) {
auto to = style::rtlrect( auto to = style::rtlrect(