diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f4d2c145d..b8a0d6af7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2327,9 +2327,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_box_out_title" = "Confirm Your Purchase"; "lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?"; "lng_credits_box_out_sure#other" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Stars**?"; +"lng_credits_box_out_media#one" = "Do you want to unlock {media} in {chat} for **{count} Star**?"; +"lng_credits_box_out_media#other" = "Do you want to unlock {media} in {chat} for **{count} Stars**?"; +"lng_credits_box_out_photo" = "a photo"; +"lng_credits_box_out_photos#one" = "{count} photo"; +"lng_credits_box_out_photos#other" = "{count} photos"; +"lng_credits_box_out_video" = "a video"; +"lng_credits_box_out_videos#one" = "{count} video"; +"lng_credits_box_out_videos#other" = "{count} videos"; +"lng_credits_box_out_both" = "{photo} and {video}"; "lng_credits_box_out_confirm#one" = "Confirm and Pay {emoji} {count} Star"; "lng_credits_box_out_confirm#other" = "Confirm and Pay {emoji} {count} Stars"; "lng_credits_box_out_about" = "Review the {link} for Stars."; +"lng_credits_media_done_title" = "Media Unlocked"; +"lng_credits_media_done_text#one" = "**{count} Star** transferred to {chat}."; +"lng_credits_media_done_text#other" = "**{count} Stars** transferred to {chat}."; "lng_credits_summary_in_toast_title" = "Stars Acquired"; "lng_credits_summary_in_toast_about#one" = "**{count}** Star added to your balance."; "lng_credits_summary_in_toast_about#other" = "**{count}** Stars added to your balance."; diff --git a/Telegram/SourceFiles/boxes/send_credits_box.cpp b/Telegram/SourceFiles/boxes/send_credits_box.cpp index 8820c0c65..6e75f5c5c 100644 --- a/Telegram/SourceFiles/boxes/send_credits_box.cpp +++ b/Telegram/SourceFiles/boxes/send_credits_box.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "core/ui_integration.h" // Core::MarkedTextContext. #include "data/data_credits.h" +#include "data/data_photo.h" #include "data/data_session.h" #include "data/data_user.h" #include "data/stickers/data_custom_emoji.h" @@ -39,6 +40,136 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_settings.h" namespace Ui { +namespace { + +struct PaidMediaData { + const Data::Invoice *invoice = nullptr; + HistoryItem *item = nullptr; + PeerData *peer = nullptr; + int photos = 0; + int videos = 0; + + explicit operator bool() const { + return invoice && item && peer && (photos || videos); + } +}; + +[[nodiscard]] PaidMediaData LookupPaidMediaData( + not_null session, + not_null form) { + using namespace Payments; + const auto message = std::get_if(&form->id.value); + const auto item = message + ? session->data().message(message->peer, message->itemId) + : nullptr; + const auto media = item ? item->media() : nullptr; + const auto invoice = media ? media->invoice() : nullptr; + if (!invoice || !invoice->isPaidMedia) { + return {}; + } + + auto photos = 0; + auto videos = 0; + for (const auto &media : invoice->extendedMedia) { + const auto photo = media->photo(); + if (photo && !photo->extendedMediaVideoDuration().has_value()) { + ++photos; + } else { + ++videos; + } + } + + return { + .invoice = invoice, + .item = item, + .peer = message->peer, + .photos = photos, + .videos = videos, + }; +} + +[[nodiscard]] rpl::producer SendCreditsConfirmText( + not_null session, + not_null form) { + if (const auto data = LookupPaidMediaData(session, form)) { + auto photos = 0; + auto videos = 0; + for (const auto &media : data.invoice->extendedMedia) { + const auto photo = media->photo(); + if (photo && !photo->extendedMediaVideoDuration().has_value()) { + ++photos; + } else { + ++videos; + } + } + + auto photosBold = tr::lng_credits_box_out_photos( + lt_count, + rpl::single(photos) | tr::to_count(), + Ui::Text::Bold); + auto videosBold = tr::lng_credits_box_out_videos( + lt_count, + rpl::single(videos) | tr::to_count(), + Ui::Text::Bold); + auto media = (!videos) + ? ((photos > 1) + ? std::move(photosBold) + : tr::lng_credits_box_out_photo(Ui::Text::WithEntities)) + : (!photos) + ? ((videos > 1) + ? std::move(videosBold) + : tr::lng_credits_box_out_video(Ui::Text::WithEntities)) + : tr::lng_credits_box_out_both( + lt_photo, + std::move(photosBold), + lt_video, + std::move(videosBold), + Ui::Text::WithEntities); + return tr::lng_credits_box_out_media( + lt_count, + rpl::single(form->invoice.amount) | tr::to_count(), + lt_media, + std::move(media), + lt_chat, + rpl::single(Ui::Text::Bold(data.peer->name())), + Ui::Text::RichLangValue); + } + + const auto bot = session->data().user(form->botId); + return tr::lng_credits_box_out_sure( + lt_count, + rpl::single(form->invoice.amount) | tr::to_count(), + lt_text, + rpl::single(TextWithEntities{ form->title }), + lt_bot, + rpl::single(TextWithEntities{ bot->name() }), + Ui::Text::RichLangValue); +} + +[[nodiscard]] object_ptr SendCreditsThumbnail( + not_null parent, + not_null session, + not_null 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); + } + } + } + if (form->photo) { + return Settings::HistoryEntryPhoto(parent, form->photo, photoSize); + } + const auto bot = session->data().user(form->botId); + return object_ptr( + parent, + bot, + st::defaultUserpicButton); +} + +} // namespace void SendCreditsBox( not_null box, @@ -89,22 +220,10 @@ void SendCreditsBox( }, ministarsContainer->lifetime()); } - const auto bot = session->data().user(form->botId); - - if (form->photo) { - box->addRow(object_ptr>( - content, - Settings::HistoryEntryPhoto(content, form->photo, photoSize))); - } else { - const auto widget = box->addRow( - object_ptr>( - content, - object_ptr( - content, - bot, - st::defaultUserpicButton))); - widget->setAttribute(Qt::WA_TransparentForMouseEvents); - } + const auto thumb = box->addRow(object_ptr>( + content, + SendCreditsThumbnail(content, session, form.get(), photoSize))); + thumb->setAttribute(Qt::WA_TransparentForMouseEvents); Ui::AddSkip(content); box->addRow(object_ptr>( @@ -118,14 +237,7 @@ void SendCreditsBox( box, object_ptr( box, - tr::lng_credits_box_out_sure( - lt_count, - rpl::single(form->invoice.amount) | tr::to_count(), - lt_text, - rpl::single(TextWithEntities{ form->title }), - lt_bot, - rpl::single(TextWithEntities{ bot->name() }), - Ui::Text::RichLangValue), + SendCreditsConfirmText(session, form.get()), st::creditsBoxAbout))); Ui::AddSkip(content); Ui::AddSkip(content); @@ -177,7 +289,7 @@ void SendCreditsBox( const auto buttonLabel = Ui::CreateChild( button, rpl::single(QString()), - st::defaultFlatLabel); + st::creditsBoxButtonLabel); std::move( buttonText ) | rpl::start_with_next([=](const TextWithEntities &text) { diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index 8dac6d0c5..c09b41c36 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -399,6 +399,7 @@ void Form::requestForm() { .amount = amount, }; const auto formData = CreditsFormData{ + .id = _id, .formId = data.vform_id().v, .botId = data.vbot_id().v, .title = qs(data.vtitle()), diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index cf4cef890..c85d946ed 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -178,6 +178,7 @@ struct InvoiceId { }; struct CreditsFormData { + InvoiceId id; uint64 formId = 0; uint64 botId = 0; QString title; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 4eec43924..63521ec31 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -544,6 +544,27 @@ object_ptr HistoryEntryPhoto( return owned; } +object_ptr PaidMediaPhoto( + not_null parent, + not_null photo, + int photoSize) { + auto owned = object_ptr(parent); + const auto widget = owned.data(); + widget->resize(Size(photoSize)); + + const auto draw = Ui::GeneratePaidMediaPaintCallback( + photo, + [=] { widget->update(); }); + + widget->paintRequest( + ) | rpl::start_with_next([=] { + auto p = Painter(widget); + draw(p, 0, 0, photoSize, photoSize); + }, widget->lifetime()); + + return owned; +} + void SmallBalanceBox( not_null box, not_null controller, diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.h b/Telegram/SourceFiles/settings/settings_credits_graphics.h index d25b74688..d191187b3 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.h +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.h @@ -50,6 +50,11 @@ void ReceiptCreditsBox( not_null photo, int photoSize); +[[nodiscard]] object_ptr PaidMediaPhoto( + not_null parent, + not_null photo, + int photoSize); + void SmallBalanceBox( not_null box, not_null controller, diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 2da64404d..1518b69b1 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -35,11 +35,12 @@ creditsBoxAbout: FlatLabel(defaultFlatLabel) { minWidth: 256px; align: align(top); } - creditsBoxAboutTitle: FlatLabel(settingsPremiumUserTitle) { minWidth: 256px; } - creditsBoxAboutDivider: FlatLabel(boxDividerLabel) { align: align(top); } +creditsBoxButtonLabel: FlatLabel(defaultFlatLabel) { + style: semiboldTextStyle; +} diff --git a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp index ffc5a6503..ce257b936 100644 --- a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/effects/premium_graphics.h" +#include "ui/effects/spoiler_mess.h" #include "ui/empty_userpic.h" #include "ui/painter.h" #include "ui/rect.h" @@ -179,6 +180,50 @@ Fn GenerateCreditsPaintEntryCallback( }; } +Fn GeneratePaidMediaPaintCallback( + not_null photo, + Fn update) { + struct State { + explicit State(Fn update) : spoiler(std::move(update)) { + } + + QImage image; + Ui::SpoilerAnimation spoiler; + }; + const auto state = std::make_shared(update); + + return [=](Painter &p, int x, int y, int outerWidth, int size) { + if (state->image.isNull()) { + const auto media = photo->createMediaView(); + const auto thumbnail = media->thumbnailInline(); + const auto ratio = style::DevicePixelRatio(); + const auto scaled = QSize(size, size) * ratio; + auto image = thumbnail + ? Images::Blur(thumbnail->original(), true) + : QImage(scaled, QImage::Format_ARGB32_Premultiplied); + if (!thumbnail) { + image.fill(Qt::black); + image.setDevicePixelRatio(ratio); + } + const auto minSize = std::min(image.width(), image.height()); + state->image = Images::Prepare( + image.copy( + (image.width() - minSize) / 2, + (image.height() - minSize) / 2, + minSize, + minSize), + size * ratio, + { .options = Images::Option::RoundLarge }); + } + p.drawImage(x, y, state->image); + Ui::FillSpoilerRect( + p, + QRect(x, y, size, size), + Ui::DefaultImageSpoiler().frame( + state->spoiler.index(crl::now(), false))); + }; +} + TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) { return ((entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment) ? tr::lng_bot_username_description1_link diff --git a/Telegram/SourceFiles/ui/effects/credits_graphics.h b/Telegram/SourceFiles/ui/effects/credits_graphics.h index 1d0751605..a730cffec 100644 --- a/Telegram/SourceFiles/ui/effects/credits_graphics.h +++ b/Telegram/SourceFiles/ui/effects/credits_graphics.h @@ -24,6 +24,10 @@ Fn GenerateCreditsPaintEntryCallback( not_null photo, Fn update); +Fn GeneratePaidMediaPaintCallback( + not_null photo, + Fn update); + [[nodiscard]] TextWithEntities GenerateEntryName( const Data::CreditsHistoryEntry &entry);