mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Show correct chat preview for paid media.
This commit is contained in:
parent
5f8da27c86
commit
950a946a16
9 changed files with 184 additions and 54 deletions
|
@ -4214,11 +4214,18 @@ void ApiWrap::sendMediaWithRandomId(
|
|||
|
||||
void ApiWrap::sendMultiPaidMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const QVector<MTPInputMedia> &medias,
|
||||
Api::SendOptions options,
|
||||
uint64 randomId,
|
||||
not_null<SendingAlbum*> album,
|
||||
Fn<void(bool)> done) {
|
||||
Expects(options.price > 0);
|
||||
Expects(album->options.price > 0);
|
||||
|
||||
const auto groupId = album->groupId;
|
||||
const auto &options = album->options;
|
||||
const auto randomId = album->items.front().randomId;
|
||||
auto medias = album->items | ranges::view::transform([](
|
||||
const SendingAlbum::Item &part) {
|
||||
Assert(part.media.has_value());
|
||||
return MTPInputMedia(part.media->data().vmedia());
|
||||
}) | ranges::to<QVector<MTPInputMedia>>();
|
||||
|
||||
const auto history = item->history();
|
||||
const auto replyTo = item->replyTo();
|
||||
|
@ -4256,7 +4263,7 @@ void ApiWrap::sendMultiPaidMedia(
|
|||
Data::Histories::ReplyToPlaceholder(),
|
||||
MTP_inputMediaPaidMedia(
|
||||
MTP_long(options.price),
|
||||
MTP_vector<MTPInputMedia>(medias)),
|
||||
MTP_vector<MTPInputMedia>(std::move(medias))),
|
||||
MTP_string(caption.text),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
|
@ -4266,6 +4273,14 @@ void ApiWrap::sendMultiPaidMedia(
|
|||
Data::ShortcutIdToMTP(_session, options.shortcutId),
|
||||
MTP_long(options.effectId)
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
if (const auto album = _sendingAlbums.take(groupId)) {
|
||||
const auto copy = (*album)->items;
|
||||
for (const auto &part : copy) {
|
||||
if (const auto item = history->owner().message(part.msgId)) {
|
||||
item->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done) done(true);
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (done) done(false);
|
||||
|
@ -4326,6 +4341,9 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
|||
if (!sample) {
|
||||
_sendingAlbums.remove(groupId);
|
||||
return;
|
||||
} else if (album->options.price > 0) {
|
||||
sendMultiPaidMedia(sample, album);
|
||||
return;
|
||||
} else if (medias.size() < 2) {
|
||||
const auto &single = medias.front().data();
|
||||
sendMediaWithRandomId(
|
||||
|
@ -4335,19 +4353,6 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
|||
single.vrandom_id().v);
|
||||
_sendingAlbums.remove(groupId);
|
||||
return;
|
||||
} else if (album->options.price > 0) {
|
||||
const auto &single = medias.front().data();
|
||||
auto list = medias | ranges::view::transform([](
|
||||
const MTPInputSingleMedia &media) {
|
||||
return MTPInputMedia(media.data().vmedia());
|
||||
}) | ranges::to<QVector<MTPInputMedia>>();
|
||||
sendMultiPaidMedia(
|
||||
sample,
|
||||
std::move(list),
|
||||
album->options,
|
||||
single.vrandom_id().v);
|
||||
_sendingAlbums.remove(groupId);
|
||||
return;
|
||||
}
|
||||
const auto history = sample->history();
|
||||
const auto replyTo = sample->replyTo();
|
||||
|
|
|
@ -547,9 +547,7 @@ private:
|
|||
Fn<void(bool)> done = nullptr);
|
||||
void sendMultiPaidMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const QVector<MTPInputMedia> &medias,
|
||||
Api::SendOptions options,
|
||||
uint64 randomId,
|
||||
not_null<SendingAlbum*> album,
|
||||
Fn<void(bool)> done = nullptr);
|
||||
|
||||
void getTopPromotionDelayed(TimeId now, TimeId next);
|
||||
|
|
|
@ -81,6 +81,7 @@ void Groups::refreshMessage(
|
|||
bool justRefreshViews) {
|
||||
if (!isGrouped(item)) {
|
||||
unregisterMessage(item);
|
||||
_data->requestItemViewRefresh(item);
|
||||
return;
|
||||
}
|
||||
if (!item->isRegular() && !item->isScheduled()) {
|
||||
|
|
|
@ -85,6 +85,13 @@ constexpr auto kLoadingStoryPhotoId = PhotoId(0x7FFF'DEAD'FFFF'FFFFULL);
|
|||
using ItemPreview = HistoryView::ItemPreview;
|
||||
using ItemPreviewImage = HistoryView::ItemPreviewImage;
|
||||
|
||||
struct AlbumCounts {
|
||||
int photos = 0;
|
||||
int videos = 0;
|
||||
int audios = 0;
|
||||
int files = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] TextWithEntities WithCaptionNotificationText(
|
||||
const QString &attachType,
|
||||
const TextWithEntities &caption,
|
||||
|
@ -166,7 +173,7 @@ template <typename MediaType>
|
|||
return (reinterpret_cast<uint64>(data.get()) & ~1) | (spoiler ? 1 : 0);
|
||||
}
|
||||
|
||||
[[nodiscard]] ItemPreviewImage PreparePhotoPreview(
|
||||
[[nodiscard]] ItemPreviewImage PreparePhotoPreviewImage(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::shared_ptr<PhotoMedia> &media,
|
||||
ImageRoundRadius radius,
|
||||
|
@ -182,14 +189,15 @@ template <typename MediaType>
|
|||
}
|
||||
const auto allowedToDownload = media->autoLoadThumbnailAllowed(
|
||||
item->history()->peer);
|
||||
const auto cacheKey = allowedToDownload ? 0 : counted;
|
||||
const auto spoilered = uint64(spoiler ? 1 : 0);
|
||||
const auto cacheKey = allowedToDownload ? spoilered : counted;
|
||||
if (allowedToDownload) {
|
||||
media->owner()->load(PhotoSize::Small, item->fullId());
|
||||
}
|
||||
if (const auto blurred = media->thumbnailInline()) {
|
||||
return { PreparePreviewImage(blurred, radius, spoiler), cacheKey };
|
||||
}
|
||||
return { QImage(), allowedToDownload ? 0 : cacheKey };
|
||||
return { QImage(), allowedToDownload ? spoilered : cacheKey };
|
||||
}
|
||||
|
||||
[[nodiscard]] ItemPreviewImage PrepareFilePreviewImage(
|
||||
|
@ -208,10 +216,11 @@ template <typename MediaType>
|
|||
};
|
||||
}
|
||||
document->loadThumbnail(item->fullId());
|
||||
const auto spoilered = uint64(spoiler ? 1 : 0);
|
||||
if (const auto blurred = media->thumbnailInline()) {
|
||||
return { PreparePreviewImage(blurred, radius, spoiler), 0 };
|
||||
return { PreparePreviewImage(blurred, radius, spoiler), spoilered };
|
||||
}
|
||||
return { QImage(), 0 };
|
||||
return { QImage(), spoilered };
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage PutPlayIcon(QImage preview) {
|
||||
|
@ -226,6 +235,18 @@ template <typename MediaType>
|
|||
return preview;
|
||||
}
|
||||
|
||||
[[nodiscard]] ItemPreviewImage PreparePhotoPreview(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::shared_ptr<PhotoMedia> &media,
|
||||
ImageRoundRadius radius,
|
||||
bool spoiler) {
|
||||
auto result = PreparePhotoPreviewImage(item, media, radius, spoiler);
|
||||
if (media->owner()->extendedMediaVideoDuration().has_value()) {
|
||||
result.data = PutPlayIcon(std::move(result.data));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] ItemPreviewImage PrepareFilePreview(
|
||||
not_null<const HistoryItem*> item,
|
||||
const std::shared_ptr<DocumentMedia> &media,
|
||||
|
@ -280,7 +301,7 @@ bool UpdateExtendedMedia(
|
|||
auto changed = false;
|
||||
auto size = QSize();
|
||||
auto thumbnail = QByteArray();
|
||||
auto videoDuration = TimeId();
|
||||
auto videoDuration = std::optional<TimeId>();
|
||||
if (const auto &w = data.vw()) {
|
||||
const auto &h = data.vh();
|
||||
Assert(h.has_value());
|
||||
|
@ -302,6 +323,8 @@ bool UpdateExtendedMedia(
|
|||
if (photo->extendedMediaVideoDuration() != videoDuration) {
|
||||
changed = true;
|
||||
}
|
||||
} else if (photo->extendedMediaVideoDuration().has_value()) {
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
photo->setExtendedMediaPreview(size, thumbnail, videoDuration);
|
||||
|
@ -355,6 +378,29 @@ TextForMimeData WithCaptionClipboardText(
|
|||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString ComputeAlbumCountsString(AlbumCounts counts) {
|
||||
const auto medias = counts.photos + counts.videos;
|
||||
return (counts.photos && counts.videos)
|
||||
? tr::lng_in_dlg_media_count(tr::now, lt_count, medias)
|
||||
: (counts.photos > 1)
|
||||
? tr::lng_in_dlg_photo_count(tr::now, lt_count, counts.photos)
|
||||
: counts.photos
|
||||
? tr::lng_in_dlg_photo(tr::now)
|
||||
: (counts.videos > 1)
|
||||
? tr::lng_in_dlg_video_count(tr::now, lt_count, counts.videos)
|
||||
: counts.videos
|
||||
? tr::lng_in_dlg_video(tr::now)
|
||||
: (counts.audios > 1)
|
||||
? tr::lng_in_dlg_audio_count(tr::now, lt_count, counts.audios)
|
||||
: counts.audios
|
||||
? tr::lng_in_dlg_audio(tr::now)
|
||||
: (counts.files > 1)
|
||||
? tr::lng_in_dlg_file_count(tr::now, lt_count, counts.files)
|
||||
: counts.files
|
||||
? tr::lng_in_dlg_file(tr::now)
|
||||
: tr::lng_in_dlg_album(tr::now);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Invoice ComputeInvoiceData(
|
||||
|
@ -481,6 +527,15 @@ bool HasUnpaidMedia(const Invoice &invoice) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool IsFirstVideo(const Invoice &invoice) {
|
||||
if (invoice.extendedMedia.empty()) {
|
||||
return false;
|
||||
} else if (const auto photo = invoice.extendedMedia.front()->photo()) {
|
||||
return photo->extendedMediaVideoDuration().has_value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Media::Media(not_null<HistoryItem*> parent) : _parent(parent) {
|
||||
}
|
||||
|
||||
|
@ -643,21 +698,18 @@ ItemPreview Media::toGroupPreview(
|
|||
ToPreviewOptions options) const {
|
||||
auto result = ItemPreview();
|
||||
auto loadingContext = std::vector<std::any>();
|
||||
auto photoCount = 0;
|
||||
auto videoCount = 0;
|
||||
auto audioCount = 0;
|
||||
auto fileCount = 0;
|
||||
auto counts = AlbumCounts();
|
||||
auto manyCaptions = false;
|
||||
for (const auto &item : items) {
|
||||
if (const auto media = item->media()) {
|
||||
if (media->photo()) {
|
||||
photoCount++;
|
||||
counts.photos++;
|
||||
} else if (const auto document = media->document()) {
|
||||
(document->isVideoFile()
|
||||
? videoCount
|
||||
? counts.videos
|
||||
: document->isAudioFile()
|
||||
? audioCount
|
||||
: fileCount)++;
|
||||
? counts.audios
|
||||
: counts.files)++;
|
||||
}
|
||||
auto copy = options;
|
||||
copy.ignoreGroup = true;
|
||||
|
@ -687,19 +739,7 @@ ItemPreview Media::toGroupPreview(
|
|||
}
|
||||
}
|
||||
if (manyCaptions || result.text.text.isEmpty()) {
|
||||
const auto mediaCount = photoCount + videoCount;
|
||||
auto genericText = (photoCount && videoCount)
|
||||
? tr::lng_in_dlg_media_count(tr::now, lt_count, mediaCount)
|
||||
: photoCount
|
||||
? tr::lng_in_dlg_photo_count(tr::now, lt_count, photoCount)
|
||||
: videoCount
|
||||
? tr::lng_in_dlg_video_count(tr::now, lt_count, videoCount)
|
||||
: audioCount
|
||||
? tr::lng_in_dlg_audio_count(tr::now, lt_count, audioCount)
|
||||
: fileCount
|
||||
? tr::lng_in_dlg_file_count(tr::now, lt_count, fileCount)
|
||||
: tr::lng_in_dlg_album(tr::now);
|
||||
result.text = Ui::Text::Colorized(genericText);
|
||||
result.text = Ui::Text::Colorized(ComputeAlbumCountsString(counts));
|
||||
}
|
||||
if (!loadingContext.empty()) {
|
||||
result.loadingContext = std::move(loadingContext);
|
||||
|
@ -1952,9 +1992,87 @@ bool MediaInvoice::replyPreviewLoaded() const {
|
|||
}
|
||||
|
||||
TextWithEntities MediaInvoice::notificationText() const {
|
||||
if (_invoice.isPaidMedia && !_invoice.extendedMedia.empty()) {
|
||||
return WithCaptionNotificationText(
|
||||
(IsFirstVideo(_invoice)
|
||||
? tr::lng_in_dlg_video
|
||||
: tr::lng_in_dlg_photo)(tr::now),
|
||||
parent()->originalText());
|
||||
}
|
||||
return { .text = _invoice.title };
|
||||
}
|
||||
|
||||
ItemPreview MediaInvoice::toPreview(ToPreviewOptions options) const {
|
||||
if (!_invoice.isPaidMedia || _invoice.extendedMedia.empty()) {
|
||||
return Media::toPreview(options);
|
||||
}
|
||||
auto counts = AlbumCounts();
|
||||
const auto item = parent();
|
||||
auto images = std::vector<ItemPreviewImage>();
|
||||
auto context = std::vector<std::any>();
|
||||
const auto existing = options.existing;
|
||||
const auto spoiler = HasUnpaidMedia(_invoice);
|
||||
for (const auto &media : _invoice.extendedMedia) {
|
||||
const auto raw = media.get();
|
||||
const auto photo = raw->photo();
|
||||
const auto document = raw->document();
|
||||
if (!photo && !document) {
|
||||
continue;
|
||||
} else if (images.size() < kMaxPreviewImages) {
|
||||
auto found = photo
|
||||
? FindCachedPreview(existing, not_null(photo), spoiler)
|
||||
: FindCachedPreview(existing, not_null(document), spoiler);
|
||||
const auto radius = ImageRoundRadius::Small;
|
||||
if (found) {
|
||||
images.push_back(std::move(found));
|
||||
} else if (photo) {
|
||||
const auto media = photo->createMediaView();
|
||||
if (auto prepared = PreparePhotoPreview(
|
||||
parent(),
|
||||
media,
|
||||
radius,
|
||||
spoiler)
|
||||
; prepared || !prepared.cacheKey) {
|
||||
images.push_back(std::move(prepared));
|
||||
if (!prepared.cacheKey) {
|
||||
context.push_back(media);
|
||||
}
|
||||
}
|
||||
} else if (TryFilePreview(document)) {
|
||||
const auto media = document->createMediaView();
|
||||
if (auto prepared = PrepareFilePreview(
|
||||
parent(),
|
||||
media,
|
||||
radius,
|
||||
spoiler)
|
||||
; prepared || !prepared.cacheKey) {
|
||||
images.push_back(std::move(prepared));
|
||||
if (!prepared.cacheKey) {
|
||||
context.push_back(media);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (photo && !photo->extendedMediaVideoDuration().has_value()) {
|
||||
++counts.photos;
|
||||
} else {
|
||||
++counts.videos;
|
||||
}
|
||||
}
|
||||
const auto type = ComputeAlbumCountsString(counts);
|
||||
const auto caption = (options.hideCaption || options.ignoreMessageText)
|
||||
? TextWithEntities()
|
||||
: options.translated
|
||||
? parent()->translatedText()
|
||||
: parent()->originalText();
|
||||
const auto hasMiniImages = !images.empty();
|
||||
return {
|
||||
.text = WithCaptionNotificationText(type, caption, hasMiniImages),
|
||||
.images = std::move(images),
|
||||
.loadingContext = std::move(context),
|
||||
};
|
||||
}
|
||||
|
||||
QString MediaInvoice::pinnedTextSubstring() const {
|
||||
return QString::fromUtf8("\xC2\xAB")
|
||||
+ _invoice.title
|
||||
|
|
|
@ -99,6 +99,7 @@ struct Invoice {
|
|||
};
|
||||
[[nodiscard]] bool HasExtendedMedia(const Invoice &invoice);
|
||||
[[nodiscard]] bool HasUnpaidMedia(const Invoice &invoice);
|
||||
[[nodiscard]] bool IsFirstVideo(const Invoice &invoice);
|
||||
|
||||
struct GiveawayStart {
|
||||
std::vector<not_null<ChannelData*>> channels;
|
||||
|
@ -506,6 +507,7 @@ public:
|
|||
Image *replyPreview() const override;
|
||||
bool replyPreviewLoaded() const override;
|
||||
TextWithEntities notificationText() const override;
|
||||
ItemPreview toPreview(ToPreviewOptions way) const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ void PhotoData::setFields(TimeId date, bool hasAttachedStickers) {
|
|||
void PhotoData::setExtendedMediaPreview(
|
||||
QSize dimensions,
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
TimeId videoDuration) {
|
||||
std::optional<TimeId> videoDuration) {
|
||||
_extendedMediaPreview = true;
|
||||
updateImages(
|
||||
inlineThumbnailBytes,
|
||||
|
@ -69,7 +69,7 @@ void PhotoData::setExtendedMediaPreview(
|
|||
{},
|
||||
{},
|
||||
{});
|
||||
_dateOrExtendedVideoDuration = videoDuration + 1;
|
||||
_dateOrExtendedVideoDuration = videoDuration ? (*videoDuration + 1) : 0;
|
||||
}
|
||||
|
||||
bool PhotoData::extendedMediaPreview() const {
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
void setExtendedMediaPreview(
|
||||
QSize dimensions,
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
TimeId videoDuration);
|
||||
std::optional<TimeId> videoDuration);
|
||||
[[nodiscard]] bool extendedMediaPreview() const;
|
||||
[[nodiscard]] std::optional<TimeId> extendedMediaVideoDuration() const;
|
||||
|
||||
|
|
|
@ -104,7 +104,11 @@ HistoryItem *GroupedMedia::itemForText() const {
|
|||
auto result = (HistoryItem*)nullptr;
|
||||
for (const auto &part : _parts) {
|
||||
if (!part.item->emptyText()) {
|
||||
if (result) {
|
||||
if (result == part.item) {
|
||||
// All parts are from the same message, that means
|
||||
// this is an album with a single item, single text.
|
||||
return result;
|
||||
} else if (result) {
|
||||
return nullptr;
|
||||
} else {
|
||||
result = part.item;
|
||||
|
|
|
@ -618,7 +618,8 @@ FillMenuResult FillSendMenu(
|
|||
const auto sending = (type != Type::Disabled);
|
||||
const auto empty = !sending
|
||||
&& (details.spoiler == SpoilerState::None)
|
||||
&& (details.caption == CaptionState::None);
|
||||
&& (details.caption == CaptionState::None)
|
||||
&& !details.price.has_value();
|
||||
if (empty || !action) {
|
||||
return FillMenuResult::Skipped;
|
||||
}
|
||||
|
@ -651,7 +652,8 @@ FillMenuResult FillSendMenu(
|
|||
|
||||
if ((type != Type::Disabled)
|
||||
&& ((details.spoiler != SpoilerState::None)
|
||||
|| (details.caption != CaptionState::None))) {
|
||||
|| (details.caption != CaptionState::None)
|
||||
|| details.price.has_value())) {
|
||||
menu->addSeparator(&st::expandedMenuSeparator);
|
||||
}
|
||||
if (details.spoiler != SpoilerState::None) {
|
||||
|
|
Loading…
Add table
Reference in a new issue