diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a808dc5d34..6a0c5dc0a9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3779,6 +3779,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_stars_sold_out" = "sold out"; "lng_gift_stars_resale" = "resale"; "lng_gift_stars_on_sale" = "on sale"; +"lng_gift_on_sale_for" = "On sale for {price}"; "lng_gift_stars_premium" = "premium"; "lng_gift_stars_auction" = "auction"; "lng_gift_stars_auction_join" = "Join"; diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 155c54c9a0..cb6b6b67e9 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -3334,7 +3334,14 @@ void AddUpgradeGiftCover( const auto null = nullptr; show->show(Box(StarGiftPreviewBox, title, list, type, null)); }; + auto numberText = state->upgraded.value( + ) | rpl::map([](const std::shared_ptr &v) { + return (v && v->info.unique && v->info.unique->number > 0) + ? u"#"_q + QString::number(v->info.unique->number) + : QString(); + }); AddUniqueGiftCover(container, std::move(gifts), { + .numberText = std::move(numberText), .subtitle = std::move(subtitle), .subtitleClick = hasAll ? showAll : Fn(), .subtitleLinkColored = hasAll, diff --git a/Telegram/SourceFiles/boxes/star_gift_cover_box.cpp b/Telegram/SourceFiles/boxes/star_gift_cover_box.cpp index 3ca068eed8..197ce69fbe 100644 --- a/Telegram/SourceFiles/boxes/star_gift_cover_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_cover_box.cpp @@ -30,7 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/wrap/vertical_layout.h" #include "ui/widgets/labels.h" -#include "ui/wrap/fade_wrap.h" +#include "info/peer_gifts/info_peer_gifts_common.h" +#include "styles/style_chat.h" #include "styles/style_credits.h" #include "styles/style_giveaway.h" #include "styles/style_layers.h" @@ -164,6 +165,14 @@ struct UniqueGiftCoverWidget::State { updateColorsFromBackdrops; Fn setupModel; Fn setupPattern; + + FlatLabel *number = nullptr; + rpl::variable numberTextWidth; + QImage craftedBadge; + Info::PeerGifts::GiftBadge craftedBadgeKey; + rpl::variable resaleAmount; + Fn resaleClick; + }; UniqueGiftCoverWidget::UniqueGiftCoverWidget( @@ -422,7 +431,14 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( : QColor(255, 255, 255)); } _state->released.fg = color; - _state->released.subtitle->setTextColorOverride(color); + const auto onSale = !_state->released.subtitleCustom.current() + && _state->resaleAmount.current() + && _state->resaleAmount.current().value() > 0; + _state->released.subtitle->setTextColorOverride( + onSale ? QColor(255, 255, 255) : color); + if (_state->number) { + _state->number->setTextColorOverride(color); + } }; _state->updateColors = [this, repaintedHook](float64 progress) { if (repaintedHook) { @@ -435,19 +451,11 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( }; if (args.resalePrice) { - auto background = rpl::duplicate( - data - ) | rpl::map([](const UniqueGiftCover &cover) { - auto result = cover.values.backdrop.patternColor; - result.setAlphaF(kGradientButtonBgOpacity * result.alphaF()); - return result; - }); - SetupResalePriceButton( - this, - std::move(background), - std::move(args.resalePrice), - std::move(args.resaleClick)); + std::move(args.resalePrice) | rpl::on_next([this](CreditsAmount value) { + _state->resaleAmount = value; + }, lifetime()); } + _state->resaleClick = std::move(args.resaleClick); _state->pretitle = args.pretitle ? CreateChild( @@ -494,6 +502,24 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( st::uniqueGiftTitle); _state->title->setTextColorOverride(QColor(255, 255, 255)); + auto numberTextDup = args.numberText + ? rpl::duplicate(args.numberText) + : rpl::producer(); + _state->number = args.numberText + ? CreateChild( + this, + std::move(args.numberText), + st::uniqueGiftNumber) + : nullptr; + if (_state->number) { + _state->number->setTextColorOverride(QColor(255, 255, 255)); + std::move( + numberTextDup + ) | rpl::on_next([this](const QString &text) { + _state->numberTextWidth = text.isEmpty() ? 0 : 1; + }, lifetime()); + } + _state->released.by = rpl::duplicate( data ) | rpl::map([](const UniqueGiftCover &cover) { @@ -501,38 +527,53 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( }); _state->released.subtitleText = rpl::combine( std::move(subtitleCustomText), + _state->resaleAmount.value(), rpl::duplicate(data) - ) | rpl::map([](TextWithEntities custom, const UniqueGiftCover &cover) { + ) | rpl::map([]( + TextWithEntities custom, + CreditsAmount resalePrice, + const UniqueGiftCover &cover) { if (!custom.empty()) { return custom; } const auto &gift = cover.values; - return gift.releasedBy - ? tr::lng_gift_unique_number_by( + if (resalePrice && resalePrice.value() > 0) { + auto priceText = resalePrice.ton() + ? Text::IconEmoji(&st::tonIconEmojiInSmall).append( + Lang::FormatCreditsAmountDecimal(resalePrice)) + : Text::IconEmoji(&st::starIconEmojiSmall).append( + Lang::FormatCountDecimal(resalePrice.whole())); + return tr::lng_gift_on_sale_for( + tr::now, + lt_price, + priceText, + tr::marked); + } + if (gift.releasedBy) { + return tr::lng_gift_released_by( tr::now, - lt_index, - tr::marked(QString::number(gift.number)), lt_name, tr::link('@' + gift.releasedBy->username()), - tr::marked) - : tr::lng_gift_unique_number( - tr::now, - lt_index, - tr::marked(QString::number(gift.number)), tr::marked); + } + return tr::marked(gift.model.name); }); const auto subtitleOutlined = args.subtitleOutlined; const auto subtitleClick = args.subtitleClick; rpl::combine( _state->released.by.value(), - _state->released.subtitleCustom.value() - ) | rpl::on_next([=](PeerData *by, bool subtitleCustom) { - _state->released.outlined = by - || (subtitleCustom && subtitleOutlined); - _state->released.st = _state->released.outlined - ? st::uniqueGiftReleasedBy - : st::uniqueGiftSubtitle; + _state->released.subtitleCustom.value(), + _state->resaleAmount.value() + ) | rpl::on_next([=](PeerData *by, bool subtitleCustom, CreditsAmount resale) { + const auto hasResale = resale && resale.value() > 0; + _state->released.outlined = (subtitleCustom && subtitleOutlined) + || (!subtitleCustom && (hasResale || by)); + _state->released.st = !_state->released.outlined + ? st::uniqueGiftSubtitle + : (!subtitleCustom && hasResale) + ? st::uniqueGiftOnSale + : st::uniqueGiftReleasedBy; _state->released.st.palette.linkFg = _state->released.link.color(); const auto session = &_state->now.gift->model.document->session(); @@ -557,10 +598,17 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( _state->released.subtitleHeight.force_assign(subtitle->height()); _state->released.subtitleHeight = subtitle->heightValue(); - const auto handler = subtitleCustom ? subtitleClick : Fn(); - if (!subtitleOutlined && handler) { + const auto handler = subtitleCustom + ? (subtitleClick ? subtitleClick : Fn()) + : hasResale + ? _state->resaleClick + : by + ? Fn([=] { GiftReleasedByHandler(by); }) + : Fn(); + + if (!_state->released.outlined && handler) { subtitle->setClickHandlerFilter([=](const auto &...) { - subtitleClick(); + handler(); return false; }); } else if (_state->released.outlined) { @@ -572,9 +620,11 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( subtitle->raise(); subtitle->setAttribute(Qt::WA_TransparentForMouseEvents); - button->setClickedCallback(handler - ? handler - : [=] { GiftReleasedByHandler(by); }); + if (handler) { + button->setClickedCallback(handler); + } else { + button->setAttribute(Qt::WA_TransparentForMouseEvents); + } subtitle->geometryValue( ) | rpl::on_next([=](QRect geometry) { button->setGeometry( @@ -713,24 +763,40 @@ UniqueGiftCoverWidget::UniqueGiftCoverWidget( rpl::combine( widthValue(), - _state->released.subtitleHeight.value() - ) | rpl::on_next([this](int width, int subtitleHeight) { + _state->released.subtitleHeight.value(), + _state->numberTextWidth.value() + ) | rpl::on_next([this](int width, int subtitleHeight, int) { const auto skip = st::uniqueGiftBottom; if (width <= 3 * skip) { return; } const auto available = width - 2 * skip; - _state->title->resizeToWidth(available); - auto top = st::uniqueGiftTitleTop; if (_state->pretitle) { + _state->title->resizeToWidth(available); _state->pretitle->move((width - _state->pretitle->width()) / 2, top); top += _state->pretitle->height() + (st::uniqueGiftSubtitleTop - st::uniqueGiftTitleTop) - _state->title->height(); } - _state->title->moveToLeft(skip, top); + if (_state->number && _state->number->textMaxWidth() > 0) { + const auto titleWidth = _state->title->textMaxWidth(); + _state->title->resizeToWidth(titleWidth); + const auto numberWidth = _state->number->textMaxWidth(); + _state->number->resizeToWidth(numberWidth); + const auto gap = st::normalFont->spacew; + const auto totalWidth = titleWidth + gap + numberWidth; + const auto groupLeft = (width - totalWidth) / 2; + _state->title->moveToLeft(groupLeft, top); + _state->number->moveToLeft( + groupLeft + titleWidth + gap, + top + (_state->title->height() + - _state->number->height()) / 2); + } else { + _state->title->resizeToWidth(available); + _state->title->moveToLeft(skip, top); + } if (_state->pretitle) { top += _state->title->height() + st::defaultVerticalListSkip; } else { @@ -963,7 +1029,24 @@ bool UniqueGiftCoverWidget::paintGift( if (gift.gift->pattern.document != gift.gift->model.document) { paintPattern(p, gift.pattern, gift.backdrop, context, shown); } - return paintModel(p, gift.model, context); + const auto finished = paintModel(p, gift.model, context); + if (gift.gift->crafted) { + const auto padding = st::chatUniqueGiftBadgePadding; + auto badge = Info::PeerGifts::GiftBadge{ + .text = tr::lng_gift_crafted_tag(tr::now), + .bg1 = gift.gift->backdrop.edgeColor, + .bg2 = gift.gift->backdrop.patternColor, + .fg = gift.gift->backdrop.textColor, + }; + if (_state->craftedBadge.isNull() + || _state->craftedBadgeKey != badge) { + _state->craftedBadgeKey = badge; + _state->craftedBadge = Info::PeerGifts::ValidateRotatedBadge( + badge, padding, true); + } + p.drawImage(0, 0, _state->craftedBadge); + } + return finished; } QColor UniqueGiftCoverWidget::backgroundColor() const { @@ -1221,68 +1304,6 @@ void UniqueGiftCoverWidget::paintNormalAnimation( } } -void SetupResalePriceButton( - not_null parent, - rpl::producer background, - rpl::producer price, - Fn click) { - const auto resale = CreateChild< - FadeWrapScaled - >(parent, object_ptr(parent)); - resale->move(0, 0); - - const auto button = resale->entity(); - const auto text = CreateChild( - button, - QString(), - st::uniqueGiftResalePrice); - text->setAttribute(Qt::WA_TransparentForMouseEvents); - text->sizeValue() | rpl::on_next([=](QSize size) { - const auto padding = st::uniqueGiftResalePadding; - const auto margin = st::uniqueGiftResaleMargin; - button->resize(size.grownBy(padding + margin)); - text->move((margin + padding).left(), (margin + padding).top()); - }, button->lifetime()); - text->setTextColorOverride(QColor(255, 255, 255, 255)); - - std::move(price) | rpl::on_next([=](CreditsAmount value) { - if (value) { - text->setMarkedText(value.ton() - ? Text::IconEmoji(&st::tonIconEmoji).append( - Lang::FormatCreditsAmountDecimal(value)) - : Text::IconEmoji(&st::starIconEmoji).append( - Lang::FormatCountDecimal(value.whole()))); - resale->toggle(true, anim::type::normal); - } else { - resale->toggle(false, anim::type::normal); - } - }, resale->lifetime()); - resale->finishAnimating(); - - const auto bg = button->lifetime().make_state>( - std::move(background)); - button->paintRequest() | rpl::on_next([=] { - auto p = QPainter(button); - auto hq = PainterHighQualityEnabler(p); - - const auto inner = button->rect().marginsRemoved( - st::uniqueGiftResaleMargin); - const auto radius = inner.height() / 2.; - p.setPen(Qt::NoPen); - p.setBrush(bg->current()); - p.drawRoundedRect(inner, radius, radius); - }, button->lifetime()); - bg->changes() | rpl::on_next([=] { - button->update(); - }, button->lifetime()); - - if (click) { - resale->entity()->setClickedCallback(std::move(click)); - } else { - resale->setAttribute(Qt::WA_TransparentForMouseEvents); - } -} - void GiftReleasedByHandler(not_null peer) { const auto session = &peer->session(); const auto window = session->tryResolveWindow(peer); diff --git a/Telegram/SourceFiles/boxes/star_gift_cover_box.h b/Telegram/SourceFiles/boxes/star_gift_cover_box.h index 94a5b09916..4e425e073a 100644 --- a/Telegram/SourceFiles/boxes/star_gift_cover_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_cover_box.h @@ -22,6 +22,7 @@ class VerticalLayout; struct UniqueGiftCoverArgs { rpl::producer pretitle; + rpl::producer numberText; rpl::producer subtitle; Fn subtitleClick; bool subtitleLinkColored = false; @@ -42,12 +43,6 @@ struct UniqueGiftCover { bool force = false; }; -void SetupResalePriceButton( - not_null parent, - rpl::producer background, - rpl::producer price, - Fn click); - void GiftReleasedByHandler(not_null peer); class UniqueGiftCoverWidget final : public RpWidget { diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp index 1a29475a52..550aba6ccf 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp @@ -1296,7 +1296,10 @@ rpl::producer> GiftStickerValue( }); } -QImage ValidateRotatedBadge(const GiftBadge &badge, QMargins padding) { +QImage ValidateRotatedBadge( + const GiftBadge &badge, + QMargins padding, + bool left) { const auto &font = badge.small ? st::giftBoxGiftBadgeFont : st::msgServiceGiftBoxBadgeFont; @@ -1308,7 +1311,10 @@ QImage ValidateRotatedBadge(const GiftBadge &badge, QMargins padding) { const auto multiplier = ratio * 3; const auto size = (twidth + font->height * 2); const auto height = padding.top() + font->height + padding.bottom(); - const auto textpos = QPoint(size - skip, padding.top()); + const auto angle = left ? -45. : 45.; + const auto textpos = left + ? QPoint(padding.top(), size - skip) + : QPoint(size - skip, padding.top()); auto image = QImage( QSize(size, size) * multiplier, QImage::Format_ARGB32_Premultiplied); @@ -1318,7 +1324,7 @@ QImage ValidateRotatedBadge(const GiftBadge &badge, QMargins padding) { auto p = QPainter(&image); auto hq = PainterHighQualityEnabler(p); p.translate(textpos); - p.rotate(45.); + p.rotate(angle); p.setFont(font); p.setPen(badge.fg); p.drawText( @@ -1343,7 +1349,7 @@ QImage ValidateRotatedBadge(const GiftBadge &badge, QMargins padding) { p.save(); p.translate(textpos); - p.rotate(45.); + p.rotate(angle); const auto rect = QRect(-5 * twidth, 0, twidth * 12, height); if (badge.border.alpha() > 0) { p.setPen(badge.border); diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h index 93c563b4ee..465ad7489d 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h @@ -317,7 +317,8 @@ private: [[nodiscard]] QImage ValidateRotatedBadge( const GiftBadge &badge, - QMargins padding); + QMargins padding, + bool left = false); void SelectGiftToUnpin( std::shared_ptr show, diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index f53a602fc0..59a6b3d614 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1356,6 +1356,9 @@ void GenericCreditsEntryCover( } : Fn(); AddUniqueGiftCover(content, rpl::single(cover), { + .numberText = (uniqueGift->number > 0) + ? rpl::single(u"#"_q + QString::number(uniqueGift->number)) + : rpl::producer(), .resalePrice = UniqueGiftResalePrice(e.uniqueGift, forceTon), .resaleClick = resaleClick, }); diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 2c05692c25..1f8ee3e08d 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -295,6 +295,11 @@ uniqueGiftPretitle: FlatLabel(defaultFlatLabel) { uniqueGiftTitle: FlatLabel(boxTitle) { align: align(top); } +uniqueGiftNumber: FlatLabel(uniqueGiftTitle) { + style: TextStyle(defaultTextStyle) { + font: semiboldFont; + } +} uniqueGiftResalePrice: FlatLabel(defaultFlatLabel) { style: semiboldTextStyle; } @@ -310,6 +315,11 @@ uniqueGiftReleasedBy: FlatLabel(uniqueGiftSubtitle) { font: font(10px); } } +uniqueGiftOnSale: FlatLabel(uniqueGiftSubtitle) { + style: TextStyle(defaultTextStyle) { + font: font(10px semibold); + } +} uniqueGiftSubtitleTop: 170px; uniqueGiftUserpicTop: 48px; uniqueGiftUserpicSize: 80px;