Show nice thumbnails for paid albums.

This commit is contained in:
John Preston 2024-06-24 14:48:47 +04:00
parent dfc422b505
commit 90dfae52f5
7 changed files with 187 additions and 88 deletions

View file

@ -34,7 +34,7 @@ constexpr auto kTransactionsLimit = 100;
const auto photo = tl.data().vphoto()
? owner->photoFromWeb(*tl.data().vphoto(), ImageLocation())
: nullptr;
auto extended = std::vector<CreditsHistoryEntry::Media>();
auto extended = std::vector<CreditsHistoryMedia>();
if (const auto list = tl.data().vextended_media()) {
extended.reserve(list->v.size());
for (const auto &media : list->v) {
@ -42,9 +42,9 @@ constexpr auto kTransactionsLimit = 100;
if (const auto inner = photo.vphoto()) {
const auto photo = owner->processPhoto(*inner);
if (!photo->isNull()) {
extended.push_back(CreditsHistoryEntry::Media{
.type = CreditsHistoryEntry::MediaType::Photo,
.mediaId = photo->id,
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Photo,
.id = photo->id,
});
}
}
@ -54,9 +54,9 @@ constexpr auto kTransactionsLimit = 100;
if (document->isAnimation()
|| document->isVideoFile()
|| document->isGifv()) {
extended.push_back(CreditsHistoryEntry::Media{
.type = CreditsHistoryEntry::MediaType::Video,
.mediaId = document->id,
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Video,
.id = document->id,
});
}
}

View file

@ -156,11 +156,18 @@ struct PaidMediaData {
not_null<Payments::CreditsFormData*> form,
int photoSize) {
if (const auto data = LookupPaidMediaData(session, form)) {
const auto first = data.invoice->extendedMedia.front().get();
if (const auto photo = first->photo()) {
if (photo->extendedMediaPreview()) {
return Settings::PaidMediaPhoto(parent, photo, photoSize);
}
const auto first = data.invoice->extendedMedia[0]->photo();
const auto second = (data.photos > 1)
? data.invoice->extendedMedia[1]->photo()
: nullptr;
const auto totalCount = int(data.invoice->extendedMedia.size());
if (first && first->extendedMediaPreview()) {
return Settings::PaidMediaThumbnail(
parent,
first,
second,
totalCount,
photoSize);
}
}
if (form->photo) {

View file

@ -19,6 +19,16 @@ struct CreditTopupOption final {
using CreditTopupOptions = std::vector<CreditTopupOption>;
enum class CreditsHistoryMediaType {
Photo,
Video,
};
struct CreditsHistoryMedia {
CreditsHistoryMediaType type = CreditsHistoryMediaType::Photo;
uint64 id = 0;
};
struct CreditsHistoryEntry final {
using PhotoId = uint64;
enum class PeerType {
@ -30,21 +40,13 @@ struct CreditsHistoryEntry final {
PremiumBot,
Ads,
};
enum class MediaType {
Photo,
Video,
};
struct Media {
MediaType type = MediaType::Photo;
uint64 mediaId = 0;
};
QString id;
QString title;
QString description;
QDateTime date;
PhotoId photoId = 0;
std::vector<Media> extended;
std::vector<CreditsHistoryMedia> extended;
uint64 credits = 0;
uint64 bareMsgId = 0;
uint64 barePeerId = 0;

View file

@ -569,26 +569,20 @@ object_ptr<Ui::RpWidget> HistoryEntryPhoto(
photoSize);
}
object_ptr<Ui::RpWidget> HistoryEntryVideo(
not_null<Ui::RpWidget*> parent,
not_null<DocumentData*> video,
int photoSize) {
return GenericEntryPhoto(
parent,
[=](Fn<void()> update) {
return Ui::GenerateCreditsPaintEntryCallback(video, update);
},
photoSize);
}
object_ptr<Ui::RpWidget> PaidMediaPhoto(
object_ptr<Ui::RpWidget> PaidMediaThumbnail(
not_null<Ui::RpWidget*> parent,
not_null<PhotoData*> photo,
PhotoData *second,
int totalCount,
int photoSize) {
return GenericEntryPhoto(
parent,
[=](Fn<void()> update) {
return Ui::GeneratePaidMediaPaintCallback(photo, update);
return Ui::GeneratePaidMediaPaintCallback(
photo,
second,
totalCount,
update);
},
photoSize);
}

View file

@ -65,14 +65,11 @@ void ReceiptCreditsBox(
not_null<PhotoData*> photo,
int photoSize);
[[nodiscard]] object_ptr<Ui::RpWidget> HistoryEntryVideo(
not_null<Ui::RpWidget*> parent,
not_null<DocumentData*> video,
int photoSize);
[[nodiscard]] object_ptr<Ui::RpWidget> PaidMediaPhoto(
[[nodiscard]] object_ptr<Ui::RpWidget> PaidMediaThumbnail(
not_null<Ui::RpWidget*> parent,
not_null<PhotoData*> photo,
PhotoData *second,
int totalCount,
int photoSize);
void SmallBalanceBox(

View file

@ -37,21 +37,62 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtSvg/QSvgRenderer>
namespace Ui {
namespace {
using PaintRoundImageCallback = Fn<void(
Painter &p,
int x,
int y,
int outerWidth,
int size)>;
PaintRoundImageCallback MultiThumbnail(
PaintRoundImageCallback first,
PaintRoundImageCallback second,
int totalCount) {
const auto cache = std::make_shared<QImage>();
return [=](Painter &p, int x, int y, int outerWidth, int size) {
const auto stroke = st::lineWidth * 2;
const auto shift = stroke * 3;
if (size <= 2 * shift) {
first(p, x, y, outerWidth, size);
return;
}
const auto smaller = size - shift;
const auto ratio = style::DevicePixelRatio();
const auto full = QSize(size, size) * ratio;
if (cache->size() != full) {
*cache = QImage(full, QImage::Format_ARGB32_Premultiplied);
cache->setDevicePixelRatio(ratio);
}
cache->fill(Qt::transparent);
auto q = Painter(cache.get());
second(q, shift, 0, outerWidth, smaller);
q.setCompositionMode(QPainter::CompositionMode_Source);
q.setPen(QPen(Qt::transparent, 2 * stroke));
q.setBrush(Qt::NoBrush);
const auto radius = st::roundRadiusLarge;
auto hq = PainterHighQualityEnabler(q);
q.drawRoundedRect(QRect(0, shift, smaller, smaller), radius, radius);
q.setCompositionMode(QPainter::CompositionMode_SourceOver);
first(q, 0, shift, outerWidth, smaller);
q.setPen(Qt::NoPen);
q.setBrush(st::shadowFg);
q.drawRoundedRect(QRect(0, shift, smaller, smaller), radius, radius);
q.setPen(st::toastFg);
q.setFont(style::font(smaller / 2, style::FontFlag::Semibold, 0));
q.drawText(
QRect(0, shift, smaller, smaller),
QString::number(totalCount),
style::al_center);
q.end();
p.drawImage(x, y, *cache);
};
}
} // namespace
QImage GenerateStars(int height, int count) {
constexpr auto kOutlineWidth = .6;
constexpr auto kStrokeWidth = 3;
constexpr auto kShift = 3;
auto colorized = qs(Ui::Premium::ColorizedSvg(
Ui::Premium::CreditsIconGradientStops()));
auto colorized = qs(Premium::ColorizedSvg(
Premium::CreditsIconGradientStops()));
colorized.replace(
u"stroke=\"none\""_q,
u"stroke=\"%1\""_q.arg(st::creditsStroke->c.name()));
@ -98,10 +139,10 @@ QImage GenerateStars(int height, int count) {
return frame;
}
not_null<Ui::RpWidget*> CreateSingleStarWidget(
not_null<Ui::RpWidget*> parent,
not_null<RpWidget*> CreateSingleStarWidget(
not_null<RpWidget*> parent,
int height) {
const auto widget = Ui::CreateChild<Ui::RpWidget>(parent);
const auto widget = CreateChild<RpWidget>(parent);
const auto image = GenerateStars(height, 1);
widget->resize(image.size() / style::DevicePixelRatio());
widget->paintRequest(
@ -113,15 +154,15 @@ not_null<Ui::RpWidget*> CreateSingleStarWidget(
return widget;
}
not_null<Ui::MaskedInputField*> AddInputFieldForCredits(
not_null<Ui::VerticalLayout*> container,
not_null<MaskedInputField*> AddInputFieldForCredits(
not_null<VerticalLayout*> container,
rpl::producer<uint64> value) {
const auto &st = st::botEarnInputField;
const auto inputContainer = container->add(
CreateSkipWidget(container, st.heightMin));
const auto currentValue = rpl::variable<uint64>(
rpl::duplicate(value));
const auto input = Ui::CreateChild<Ui::NumberInput>(
const auto input = CreateChild<NumberInput>(
inputContainer,
st,
tr::lng_bot_earn_out_ph(),
@ -146,16 +187,16 @@ not_null<Ui::MaskedInputField*> AddInputFieldForCredits(
st::boxRowPadding.left(),
st.textMargins.top());
}, input->lifetime());
Ui::ToggleChildrenVisibility(inputContainer, true);
ToggleChildrenVisibility(inputContainer, true);
return input;
}
PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
const Data::CreditsHistoryEntry &entry) {
const auto bg = [&]() -> Ui::EmptyUserpic::BgColors {
const auto bg = [&]() -> EmptyUserpic::BgColors {
switch (entry.peerType) {
case Data::CreditsHistoryEntry::PeerType::Peer:
return Ui::EmptyUserpic::UserpicColor(0);
return EmptyUserpic::UserpicColor(0);
case Data::CreditsHistoryEntry::PeerType::AppStore:
return { st::historyPeer7UserpicBg, st::historyPeer7UserpicBg2 };
case Data::CreditsHistoryEntry::PeerType::PlayMarket:
@ -172,7 +213,7 @@ PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
}
Unexpected("Unknown peer type.");
}();
const auto userpic = std::make_shared<Ui::EmptyUserpic>(bg, QString());
const auto userpic = std::make_shared<EmptyUserpic>(bg, QString());
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
userpic->paintCircle(p, x, y, outerWidth, size);
using PeerType = Data::CreditsHistoryEntry::PeerType;
@ -190,7 +231,7 @@ PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
};
}
Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<PhotoData*> photo,
Fn<void()> update) {
struct State {
@ -240,7 +281,7 @@ Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
};
}
Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<DocumentData*> video,
Fn<void()> update) {
struct State {
@ -286,7 +327,36 @@ Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
};
}
Fn<void(Painter &, int, int, int, int)> GeneratePaidMediaPaintCallback(
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<Main::Session*> session,
Data::CreditsHistoryMedia media,
Fn<void()> update) {
return (media.type == Data::CreditsHistoryMediaType::Photo)
? GenerateCreditsPaintEntryCallback(
session->data().photo(media.id),
std::move(update))
: GenerateCreditsPaintEntryCallback(
session->data().document(media.id),
std::move(update));
}
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<Main::Session*> session,
const std::vector<Data::CreditsHistoryMedia> &media,
Fn<void()> update) {
if (media.size() == 1) {
return GenerateCreditsPaintEntryCallback(
session,
media.front(),
std::move(update));
}
return MultiThumbnail(
GenerateCreditsPaintEntryCallback(session, media[0], update),
GenerateCreditsPaintEntryCallback(session, media[1], update),
media.size());
}
PaintRoundImageCallback GeneratePaidPhotoPaintCallback(
not_null<PhotoData*> photo,
Fn<void()> update) {
struct State {
@ -294,7 +364,8 @@ Fn<void(Painter &, int, int, int, int)> GeneratePaidMediaPaintCallback(
}
QImage image;
Ui::SpoilerAnimation spoiler;
QImage spoilerCornerCache;
SpoilerAnimation spoiler;
};
const auto state = std::make_shared<State>(update);
@ -322,36 +393,49 @@ Fn<void(Painter &, int, int, int, int)> GeneratePaidMediaPaintCallback(
{ .options = Images::Option::RoundLarge });
}
p.drawImage(x, y, state->image);
Ui::FillSpoilerRect(
FillSpoilerRect(
p,
QRect(x, y, size, size),
Ui::DefaultImageSpoiler().frame(
state->spoiler.index(crl::now(), false)));
Images::CornersMaskRef(
Images::CornersMask(ImageRoundRadius::Large)),
DefaultImageSpoiler().frame(
state->spoiler.index(crl::now(), false)),
state->spoilerCornerCache);
};
}
Fn<Fn<void(Painter&, int, int, int, int)>(Fn<void()>)> PaintPreviewCallback(
PaintRoundImageCallback GeneratePaidMediaPaintCallback(
not_null<PhotoData*> photo,
PhotoData *second,
int totalCount,
Fn<void()> update) {
if (!second) {
return GeneratePaidPhotoPaintCallback(photo, update);
}
return MultiThumbnail(
GeneratePaidPhotoPaintCallback(photo, update),
GeneratePaidPhotoPaintCallback(second, update),
totalCount);
}
Fn<PaintRoundImageCallback(Fn<void()>)> PaintPreviewCallback(
not_null<Main::Session*> session,
const Data::CreditsHistoryEntry &entry) {
using MediaType = Data::CreditsHistoryEntry::MediaType;
using MediaType = Data::CreditsHistoryMediaType;
const auto &extended = entry.extended;
const auto owner = &session->data();
const auto photo = entry.photoId
? owner->photo(entry.photoId).get()
: (!extended.empty() && extended.front().type == MediaType::Photo)
? owner->photo(extended.front().mediaId).get()
: nullptr;
const auto video = (!extended.empty()
&& extended.front().type == MediaType::Video)
? owner->document(extended.front().mediaId).get()
: nullptr;
if (photo) {
if (!extended.empty()) {
return [=](Fn<void()> update) {
return Ui::GenerateCreditsPaintEntryCallback(photo, update);
return GenerateCreditsPaintEntryCallback(
session,
extended,
std::move(update));
};
} else if (video) {
} else if (entry.photoId) {
const auto photo = session->data().photo(entry.photoId);
return [=](Fn<void()> update) {
return Ui::GenerateCreditsPaintEntryCallback(video, update);
return GenerateCreditsPaintEntryCallback(
photo,
std::move(update));
};
}
return nullptr;

View file

@ -12,6 +12,7 @@ class DocumentData;
namespace Data {
struct CreditsHistoryEntry;
struct CreditsHistoryMedia;
} // namespace Data
namespace Main {
@ -26,6 +27,13 @@ class VerticalLayout;
namespace Ui {
using PaintRoundImageCallback = Fn<void(
Painter &p,
int x,
int y,
int outerWidth,
int size)>;
[[nodiscard]] QImage GenerateStars(int height, int count);
[[nodiscard]] not_null<Ui::RpWidget*> CreateSingleStarWidget(
@ -36,22 +44,29 @@ namespace Ui {
not_null<Ui::VerticalLayout*> container,
rpl::producer<uint64> value);
Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintUserpicCallback(
PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
const Data::CreditsHistoryEntry &entry);
Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<PhotoData*> photo,
Fn<void()> update);
Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<DocumentData*> video,
Fn<void()> update);
Fn<void(Painter &, int, int, int, int)> GeneratePaidMediaPaintCallback(
not_null<PhotoData*> photo,
PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
not_null<Main::Session*> session,
Data::CreditsHistoryMedia media,
Fn<void()> update);
Fn<Fn<void(Painter&, int, int, int, int)>(Fn<void()>)> PaintPreviewCallback(
PaintRoundImageCallback GeneratePaidMediaPaintCallback(
not_null<PhotoData*> photo,
PhotoData *second,
int totalCount,
Fn<void()> update);
Fn<PaintRoundImageCallback(Fn<void()>)> PaintPreviewCallback(
not_null<Main::Session*> session,
const Data::CreditsHistoryEntry &entry);