mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Customize pay-by-stars box for paid media.
This commit is contained in:
parent
950a946a16
commit
479b63c33a
9 changed files with 229 additions and 27 deletions
|
@ -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.";
|
||||
|
|
|
@ -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<Main::Session*> session,
|
||||
not_null<Payments::CreditsFormData*> form) {
|
||||
using namespace Payments;
|
||||
const auto message = std::get_if<InvoiceMessage>(&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<TextWithEntities> SendCreditsConfirmText(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Payments::CreditsFormData*> 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<Ui::RpWidget> SendCreditsThumbnail(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Main::Session*> session,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (form->photo) {
|
||||
return Settings::HistoryEntryPhoto(parent, form->photo, photoSize);
|
||||
}
|
||||
const auto bot = session->data().user(form->botId);
|
||||
return object_ptr<Ui::UserpicButton>(
|
||||
parent,
|
||||
bot,
|
||||
st::defaultUserpicButton);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SendCreditsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
|
@ -89,22 +220,10 @@ void SendCreditsBox(
|
|||
}, ministarsContainer->lifetime());
|
||||
}
|
||||
|
||||
const auto bot = session->data().user(form->botId);
|
||||
|
||||
if (form->photo) {
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
Settings::HistoryEntryPhoto(content, form->photo, photoSize)));
|
||||
} else {
|
||||
const auto widget = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::UserpicButton>(
|
||||
content,
|
||||
bot,
|
||||
st::defaultUserpicButton)));
|
||||
widget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
const auto thumb = box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
||||
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
|
@ -118,14 +237,7 @@ void SendCreditsBox(
|
|||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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<Ui::FlatLabel>(
|
||||
button,
|
||||
rpl::single(QString()),
|
||||
st::defaultFlatLabel);
|
||||
st::creditsBoxButtonLabel);
|
||||
std::move(
|
||||
buttonText
|
||||
) | rpl::start_with_next([=](const TextWithEntities &text) {
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -178,6 +178,7 @@ struct InvoiceId {
|
|||
};
|
||||
|
||||
struct CreditsFormData {
|
||||
InvoiceId id;
|
||||
uint64 formId = 0;
|
||||
uint64 botId = 0;
|
||||
QString title;
|
||||
|
|
|
@ -544,6 +544,27 @@ object_ptr<Ui::RpWidget> HistoryEntryPhoto(
|
|||
return owned;
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> PaidMediaPhoto(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PhotoData*> photo,
|
||||
int photoSize) {
|
||||
auto owned = object_ptr<Ui::RpWidget>(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<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
|
|
|
@ -50,6 +50,11 @@ void ReceiptCreditsBox(
|
|||
not_null<PhotoData*> photo,
|
||||
int photoSize);
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> PaidMediaPhoto(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PhotoData*> photo,
|
||||
int photoSize);
|
||||
|
||||
void SmallBalanceBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
|
|||
};
|
||||
}
|
||||
|
||||
Fn<void(Painter &, int, int, int, int)> GeneratePaidMediaPaintCallback(
|
||||
not_null<PhotoData*> photo,
|
||||
Fn<void()> update) {
|
||||
struct State {
|
||||
explicit State(Fn<void()> update) : spoiler(std::move(update)) {
|
||||
}
|
||||
|
||||
QImage image;
|
||||
Ui::SpoilerAnimation spoiler;
|
||||
};
|
||||
const auto state = std::make_shared<State>(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
|
||||
|
|
|
@ -24,6 +24,10 @@ Fn<void(Painter &, int, int, int, int)> GenerateCreditsPaintEntryCallback(
|
|||
not_null<PhotoData*> photo,
|
||||
Fn<void()> update);
|
||||
|
||||
Fn<void(Painter &, int, int, int, int)> GeneratePaidMediaPaintCallback(
|
||||
not_null<PhotoData*> photo,
|
||||
Fn<void()> update);
|
||||
|
||||
[[nodiscard]] TextWithEntities GenerateEntryName(
|
||||
const Data::CreditsHistoryEntry &entry);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue