Implement upcoming auction preview box.

This commit is contained in:
John Preston
2025-12-02 14:39:43 +04:00
parent b08cf75f0b
commit 65b3a36984
11 changed files with 247 additions and 93 deletions

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer_rpl.h"
#include "base/unixtime.h"
#include "boxes/peers/replace_boost_box.h"
#include "boxes/premium_preview_box.h"
#include "boxes/send_credits_box.h" // CreditsEmojiSmall
#include "boxes/share_box.h"
#include "boxes/star_gift_box.h"
@@ -991,33 +992,55 @@ void AuctionBidBox(not_null<GenericBox*> box, AuctionBidBoxArgs &&args) {
tr::lng_auction_rounds_label(),
rpl::single(tr::marked(
Lang::FormatCountDecimal(now.totalRounds))));
AddTableRow(
raw,
tr::lng_auction_rounds_first(),
((now.roundDurationFirst % 3600)
? tr::lng_minutes(
const auto formatDuration = [&](TimeId value, bool exact) {
return (!(value % 3600))
? (exact ? tr::lng_hours : tr::lng_auction_rounds_hours)(
tr::now,
lt_count,
rpl::single(now.roundDurationFirst / 60.),
tr::marked)
: tr::lng_hours(
value / 3600)
: (!(value % 60))
? (exact ? tr::lng_minutes : tr::lng_auction_rounds_minutes)(
tr::now,
lt_count,
rpl::single(now.roundDurationFirst / 3600.),
tr::marked)));
if (now.totalRounds > 1) {
value / 60)
: (exact ? tr::lng_seconds : tr::lng_auction_rounds_seconds)(
tr::now,
lt_count,
value);
};
for (auto i = 0, n = int(now.roundParameters.size()); i != n; ++i) {
const auto &that = now.roundParameters[i];
const auto next = (i + 1 < n)
? now.roundParameters[i + 1]
: Data::GiftAuctionRound{ now.totalRounds + 1 };
const auto exact = (next.number == that.number + 1);
const auto extended = that.extendTop && that.extendDuration;
const auto duration = formatDuration(that.duration, exact);
const auto value = extended
? tr::lng_auction_rounds_extended(
tr::now,
lt_duration,
duration,
lt_increase,
formatDuration(that.extendDuration, true),
lt_n,
QString::number(that.extendTop))
: duration;
AddTableRow(
raw,
tr::lng_auction_rounds_rest(
lt_last,
rpl::single(QString::number(now.totalRounds))),
((now.roundDurationRest % 3600)
? tr::lng_auction_rounds_rest_minutes(
lt_count,
rpl::single(now.roundDurationRest / 60.),
tr::marked)
: tr::lng_auction_rounds_rest_hours(
lt_count,
rpl::single(now.roundDurationRest / 3600.),
tr::marked)));
(exact
? tr::lng_auction_rounds_exact(
lt_n,
rpl::single(QString::number(that.number)))
: tr::lng_auction_rounds_range(
lt_n,
rpl::single(QString::number(that.number)),
lt_last,
rpl::single(QString::number(next.number - 1)))),
object_ptr<FlatLabel>(
raw,
value,
st::auctionInfoValueMultiline));
}
} else {
auto roundText = state->value.value(
@@ -1180,12 +1203,9 @@ void AuctionGotGiftsBox(
.pattern = Data::UniqueGiftPattern{
.document = info.document,
},
.backdrop = Data::UniqueGiftBackdrop{
.centerColor = QColor(0x3a, 0x76, 0xb4),
.edgeColor = QColor(0x10, 0x2d, 0x4d),
.patternColor = QColor(0, 0, 0, 0),
.textColor = QColor(0xff, 0xff, 0xff),
},
.backdrop = (info.background
? info.background->backdrop()
: Data::UniqueGiftBackdrop()),
};
return rpl::single(initial) | rpl::then(std::move(
attributes
@@ -1302,11 +1322,22 @@ void AuctionInfoBox(
box->setNoContentMargin(true);
if (!started) {
const auto container = box->verticalLayout();
AddUniqueGiftCover(
container,
MakePreviewAuctionStream(*now.gift, state->attributes.value()),
tr::lng_gift_upgrade_about());
auto gift = MakePreviewAuctionStream(
*now.gift,
state->attributes.value());
AddUniqueGiftCover(container, std::move(gift), {
.pretitle = tr::lng_auction_preview_name(),
.subtitle = tr::lng_auction_preview_learn_gifts(
lt_arrow,
rpl::single(Text::IconEmoji(&st::textMoreIconEmoji)),
tr::link),
.subtitleClick = [=] {
ShowPremiumPreviewBox(
window,
PremiumFeature::AnimatedEmoji); AssertIsDebug();
},
.subtitleLinkColored = true,
});
AddSkip(container, st::defaultVerticalListSkip * 2);
AddUniqueCloseButton(box, {}, MakeAuctionFillMenuCallback(show, now));
@@ -1712,9 +1743,11 @@ void SetAuctionButtonCountdownText(
) | rpl::map([=](const Data::GiftAuctionState &state, int leftTill) {
return (state.finished() || (!preview && leftTill <= 0))
? tr::lng_box_ok(tr::marked)
: (type == AuctionButtonCountdownType::Place)
? tr::lng_auction_join_bid(tr::marked)
: tr::lng_auction_join_button(tr::marked);
: (type != AuctionButtonCountdownType::Place)
? tr::lng_auction_join_button(tr::marked)
: preview
? tr::lng_auction_join_early_bid(tr::marked)
: tr::lng_auction_join_bid(tr::marked);
}) | rpl::flatten_latest();
auto buttonSubtitle = rpl::combine(

View File

@@ -137,6 +137,7 @@ constexpr auto kCrossfadeDuration = crl::time(400);
constexpr auto kUpgradeDoneToastDuration = 4 * crl::time(1000);
constexpr auto kGiftsPreloadTimeout = 3 * crl::time(1000);
constexpr auto kResellPriceCacheLifetime = 60 * crl::time(1000);
constexpr auto kGradientButtonBgOpacity = 0.6;
using namespace HistoryView;
using namespace Info::PeerGifts;
@@ -2798,38 +2799,73 @@ void SetupResalePriceButton(
void AddUniqueGiftCover(
not_null<VerticalLayout*> container,
rpl::producer<Data::UniqueGift> data,
rpl::producer<QString> subtitleOverride,
rpl::producer<CreditsAmount> resalePrice,
Fn<void()> resaleClick) {
UniqueGiftCoverArgs &&args) {
const auto cover = container->add(object_ptr<RpWidget>(container));
struct Released {
Released() : white(QColor(255, 255, 255)) {
Released() : link(QColor(255, 255, 255)) {
}
rpl::variable<TextWithEntities> subtitleText;
style::owned_color white;
std::optional<Ui::Premium::ColoredMiniStars> stars;
style::owned_color link;
style::FlatLabel st;
PeerData *by = nullptr;
QColor bg;
};
const auto released = cover->lifetime().make_state<Released>();
released->st = st::uniqueGiftReleasedBy;
released->st.palette.linkFg = released->white.color();
released->st.palette.linkFg = released->link.color();
if (resalePrice) {
if (args.resalePrice) {
auto background = rpl::duplicate(
data
) | rpl::map([=](const Data::UniqueGift &unique) {
return unique.backdrop.patternColor;
auto result = unique.backdrop.patternColor;
result.setAlphaF(kGradientButtonBgOpacity * result.alphaF());
return result;
});
SetupResalePriceButton(
cover,
std::move(background),
std::move(resalePrice),
std::move(resaleClick));
std::move(args.resalePrice),
std::move(args.resaleClick));
}
const auto pretitle = args.pretitle
? CreateChild<FlatLabel>(
cover,
std::move(args.pretitle),
st::uniqueGiftPretitle)
: nullptr;
if (pretitle) {
released->stars.emplace(
cover,
true,
Ui::Premium::MiniStarsType::SlowStars);
const auto white = QColor(255, 255, 255);
released->stars->setColorOverride(QGradientStops{
{ 0., anim::with_alpha(white, .3) },
{ 1., white },
});
pretitle->geometryValue() | rpl::start_with_next([=](QRect rect) {
const auto half = rect.height() / 2;
released->stars->setCenter(rect - QMargins(half, 0, half, 0));
}, pretitle->lifetime());
pretitle->setAttribute(Qt::WA_TransparentForMouseEvents);
pretitle->setTextColorOverride(QColor(255, 255, 255));
pretitle->paintOn([=](QPainter &p) {
auto hq = PainterHighQualityEnabler(p);
const auto radius = pretitle->height() / 2.;
p.setPen(Qt::NoPen);
auto bg = released->bg;
bg.setAlphaF(kGradientButtonBgOpacity * bg.alphaF());
p.setBrush(bg);
p.drawRoundedRect(pretitle->rect(), radius, radius);
p.translate(-pretitle->pos());
released->stars->paint(p);
});
}
const auto title = CreateChild<FlatLabel>(
cover,
rpl::duplicate(
@@ -2837,13 +2873,10 @@ void AddUniqueGiftCover(
) | rpl::map([](const Data::UniqueGift &now) { return now.title; }),
st::uniqueGiftTitle);
title->setTextColorOverride(QColor(255, 255, 255));
released->subtitleText = subtitleOverride
? std::move(
subtitleOverride
) | Ui::Text::ToWithEntities() | rpl::type_erased()
released->subtitleText = args.subtitle
? std::move(args.subtitle)
: rpl::duplicate(data) | rpl::map([=](const Data::UniqueGift &gift) {
released->by = gift.releasedBy;
released->bg = gift.backdrop.patternColor;
return gift.releasedBy
? tr::lng_gift_unique_number_by(
tr::now,
@@ -2860,13 +2893,18 @@ void AddUniqueGiftCover(
});
if (!released->by) {
released->st = st::uniqueGiftSubtitle;
released->st.palette.linkFg = released->white.color();
released->st.palette.linkFg = released->link.color();
}
const auto subtitle = CreateChild<FlatLabel>(
cover,
released->subtitleText.value(),
released->st);
if (released->by) {
if (const auto handler = args.subtitleClick) {
subtitle->setClickHandlerFilter([=](const auto &...) {
handler();
return false;
});
} else if (released->by) {
const auto button = CreateChild<AbstractButton>(cover);
subtitle->raise();
subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
@@ -2912,15 +2950,28 @@ void AddUniqueGiftCover(
};
const auto state = cover->lifetime().make_state<State>();
const auto lottieSize = st::creditsHistoryEntryStarGiftSize;
const auto updateLinkFg = args.subtitleLinkColored;
const auto updateColors = [=](float64 progress) {
subtitle->setTextColorOverride((progress == 0.)
const auto color = (progress == 0.)
? state->now.gift->backdrop.textColor
: (progress == 1.)
? state->next.gift->backdrop.textColor
: anim::color(
state->now.gift->backdrop.textColor,
state->next.gift->backdrop.textColor,
progress));
progress);
if (updateLinkFg) {
released->link.update(color);
}
subtitle->setTextColorOverride(color);
released->bg = (progress == 0.)
? state->now.gift->backdrop.patternColor
: (progress == 1.)
? state->next.gift->backdrop.patternColor
: anim::color(
state->now.gift->backdrop.patternColor,
state->next.gift->backdrop.patternColor,
progress);
};
std::move(
data
@@ -2974,10 +3025,24 @@ void AddUniqueGiftCover(
}
const auto available = width - 2 * skip;
title->resizeToWidth(available);
title->moveToLeft(skip, st::uniqueGiftTitleTop);
subtitle->resizeToWidth(available);
subtitle->moveToLeft(skip, st::uniqueGiftSubtitleTop);
auto top = st::uniqueGiftTitleTop;
if (pretitle) {
pretitle->move((width - pretitle->width()) / 2, top);
top += pretitle->height()
+ (st::uniqueGiftSubtitleTop - st::uniqueGiftTitleTop)
- title->height();
}
title->moveToLeft(skip, top);
if (pretitle) {
top += title->height() + st::defaultVerticalListSkip;
} else {
top += st::uniqueGiftSubtitleTop - st::uniqueGiftTitleTop;
}
subtitle->moveToLeft(skip, top);
cover->resize(width, subtitle->y() + subtitle->height() + skip);
}, cover->lifetime());
@@ -3007,7 +3072,7 @@ void AddUniqueGiftCover(
}
p.drawImage(0, 0, gift.gradient);
if (gift.gift->backdrop.patternColor.alpha() > 0) {
if (gift.gift->pattern.document != gift.gift->model.document) {
Ui::PaintBgPoints(
p,
Ui::PatternBgPoints(),
@@ -3803,16 +3868,16 @@ struct UpgradeArgs : StarGiftUpgradeArgs {
void AddUpgradeGiftCover(
not_null<VerticalLayout*> container,
const UpgradeArgs &args) {
AddUniqueGiftCover(
container,
MakeUpgradeGiftStream(args),
(args.savedId
? tr::lng_gift_upgrade_about()
AddUniqueGiftCover(container, MakeUpgradeGiftStream(args), {
.subtitle = (args.savedId
? tr::lng_gift_upgrade_about(tr::marked)
: (args.peer->isBroadcast()
? tr::lng_gift_upgrade_preview_about_channel
: tr::lng_gift_upgrade_preview_about)(
lt_name,
rpl::single(args.peer->shortName()))));
rpl::single(tr::marked(args.peer->shortName())),
tr::marked)),
});
}
class UpgradePriceValue final {

View File

@@ -65,12 +65,19 @@ void ShowStarGiftBox(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer);
struct UniqueGiftCoverArgs {
rpl::producer<QString> pretitle;
rpl::producer<TextWithEntities> subtitle;
Fn<void()> subtitleClick;
bool subtitleLinkColored = false;
rpl::producer<CreditsAmount> resalePrice;
Fn<void()> resaleClick;
};
void AddUniqueGiftCover(
not_null<VerticalLayout*> container,
rpl::producer<Data::UniqueGift> data,
rpl::producer<QString> subtitleOverride = nullptr,
rpl::producer<CreditsAmount> resalePrice = nullptr,
Fn<void()> resaleClick = nullptr);
UniqueGiftCoverArgs &&args);
void AddWearGiftCover(
not_null<VerticalLayout*> container,
const Data::UniqueGift &data,