From cab93600dd263ae52bb22c83805ad3bb3c28034b Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 13 Nov 2025 14:12:07 +0400 Subject: [PATCH] Add about auction box layout (no icons yet). --- Telegram/Resources/langs/lang.strings | 4 +- .../boxes/star_gift_auction_box.cpp | 273 ++++++++++++++---- Telegram/SourceFiles/calls/calls.style | 1 + .../calls/group/calls_group_common.cpp | 20 +- .../calls/group/calls_group_common.h | 4 + .../media/stories/media_stories_stealth.cpp | 62 +--- .../SourceFiles/media/view/media_view.style | 2 - Telegram/SourceFiles/mtproto/scheme/api.tl | 3 +- .../settings/settings_credits_graphics.cpp | 2 +- .../SourceFiles/storage/storage_account.cpp | 27 +- .../SourceFiles/ui/controls/feature_list.cpp | 61 ++++ .../SourceFiles/ui/controls/feature_list.h | 40 +++ .../SourceFiles/ui/controls/stars_rating.cpp | 102 ++----- Telegram/SourceFiles/ui/effects/credits.style | 4 +- Telegram/cmake/td_ui.cmake | 2 + 15 files changed, 406 insertions(+), 201 deletions(-) create mode 100644 Telegram/SourceFiles/ui/controls/feature_list.cpp create mode 100644 Telegram/SourceFiles/ui/controls/feature_list.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3382ae733c..aa3c2a9cdd 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4003,8 +4003,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_auction_about_missed_title" = "Missed Bidders"; "lng_auction_about_missed_about" = "If your bid doesn't win after the final round, your Stars will be fully refunded."; "lng_auction_about_understood" = "Understood"; -"lng_auction_text#one" = "Top **{count}** bidder will get 1 {name} this round. {link}"; -"lng_auction_text#other" = "Top **{count}** bidders will get 1 {name} this round. {link}"; +"lng_auction_text#one" = "Top **{count}** bidder will get {name} gifts this round. {link}"; +"lng_auction_text#other" = "Top **{count}** bidders will get {name} gifts this round. {link}"; "lng_auction_text_link" = "Learn more {arrow}"; "lng_auction_text_ended" = "Auction ended."; "lng_auction_start_label" = "Started"; diff --git a/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp b/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp index 7b761f4e21..53cb3eb447 100644 --- a/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_auction_box.cpp @@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "boxes/star_gift_box.h" +#include "calls/group/calls_group_common.h" #include "core/credits_amount.h" +#include "core/ui_integration.h" #include "data/components/credits.h" #include "data/components/gift_auctions.h" #include "data/data_message_reactions.h" @@ -21,7 +23,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "payments/ui/payments_reaction_box.h" #include "payments/payments_checkout_process.h" +#include "storage/storage_account.h" #include "ui/controls/button_labels.h" +#include "ui/controls/feature_list.h" #include "ui/controls/table_rows.h" #include "ui/layers/generic_box.h" #include "ui/text/format_values.h" @@ -31,13 +35,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/table_layout.h" #include "ui/dynamic_thumbnails.h" #include "window/window_session_controller.h" +#include "styles/style_calls.h" #include "styles/style_chat.h" -#include "styles/style_layers.h" #include "styles/style_credits.h" +#include "styles/style_info.h" +#include "styles/style_layers.h" +#include "styles/style_menu_icons.h" namespace Ui { namespace { +constexpr auto kAuctionAboutShownPref = "gift_auction_about_shown"_cs; +constexpr auto kBidPlacedToastDuration = 5 * crl::time(1000); + [[nodiscard]] rpl::producer MinutesLeftTillValue(TimeId endDate) { return [=](auto consumer) { auto lifetime = rpl::lifetime(); @@ -71,9 +81,9 @@ namespace { } struct AuctionBidBoxArgs { + not_null peer; std::shared_ptr show; rpl::producer state; - Fn save; }; void PlaceAuctionBid( @@ -87,16 +97,14 @@ void PlaceAuctionBid( const MTPUpdates *updates) { done(result); }; - const auto invoice = state.my.bid - ? MTP_inputInvoiceStarGiftAuctionUpdateBid( - MTP_long(state.gift->id), - MTP_long(amount)) - : MTP_inputInvoiceStarGiftAuctionBid( - MTP_flags(0), - to->input, - MTP_long(state.gift->id), - MTP_long(amount), - MTPTextWithEntities()); + using Flag = MTPDinputInvoiceStarGiftAuctionBid::Flag; + const auto invoice = MTP_inputInvoiceStarGiftAuctionBid( + MTP_flags((state.my.bid ? Flag::f_update_bid : Flag()) + | (state.my.bid ? Flag() : Flag::f_peer)), + state.my.bid ? MTP_inputPeerEmpty() : to->input, + MTP_long(state.gift->id), + MTP_long(amount), + MTPTextWithEntities()); RequestOurForm(show, invoice, [=]( uint64 formId, CreditsAmount price, @@ -135,6 +143,28 @@ void AuctionBidBox(not_null box, AuctionBidBoxArgs &&args) { .my = true, } }; + const auto save = [=, peer = args.peer](int amount) { + const auto &now = state->current(); + const auto was = (now.my.bid > 0); + const auto perRound = now.gift->auctionGiftsPerRound; + const auto done = [=](Payments::CheckoutResult result) { + if (result == Payments::CheckoutResult::Paid) { + show->showToast({ + .title = (was + ? tr::lng_auction_bid_increased_title + : tr::lng_auction_bid_placed_title)( + tr::now), + .text = tr::lng_auction_bid_done_text( + tr::now, + lt_count, + perRound, + tr::rich), + .duration = kBidPlacedToastDuration, + }); + } + }; + PlaceAuctionBid(show, peer, amount, now, done); + }; PaidReactionsBox(box, { .min = int(state->current().my.bid ? state->current().my.minBidAmount @@ -147,7 +177,7 @@ void AuctionBidBox(not_null box, AuctionBidBoxArgs &&args) { .submit = submit, .colorings = session->appConfig().groupCallColorings(), .balanceValue = session->credits().balanceValue(), - .send = [weak, save = args.save](int count, uint64 barePeerId) { + .send = [=](int count, uint64 barePeerId) { save(count); if (const auto strong = weak.get()) { strong->closeBox(); @@ -290,8 +320,95 @@ void AuctionBidBox(not_null box, AuctionBidBoxArgs &&args) { void AuctionAboutBox( not_null box, int rounds, - int giftsPerRound) { + int giftsPerRound, + Fn close)> understood) { + box->setStyle(st::confcallJoinBox); + box->setWidth(st::boxWideWidth); + box->setNoContentMargin(true); + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); + box->addRow( + Calls::Group::MakeRoundActiveLogo( + box, + st::auctionAboutLogo, + st::auctionAboutLogoPadding), + st::boxRowPadding + st::confcallLinkHeaderIconPadding); + box->addRow( + object_ptr( + box, + tr::lng_auction_about_title(), + st::boxTitle), + st::boxRowPadding + st::confcallLinkTitlePadding, + style::al_top); + box->addRow( + object_ptr( + box, + tr::lng_auction_about_subtitle(tr::rich), + st::confcallLinkCenteredText), + st::boxRowPadding, + style::al_top + )->setTryMakeSimilarLines(true); + + const auto features = std::vector{ + { + st::menuIconRatingGifts, + tr::lng_auction_about_top_title( + tr::now, + lt_count, + giftsPerRound), + tr::lng_auction_about_top_about( + tr::now, + lt_count, + giftsPerRound, + lt_rounds, + tr::lng_auction_about_top_rounds( + tr::now, + lt_count, + rounds, + tr::rich), + lt_bidders, + tr::lng_auction_about_top_bidders( + tr::now, + lt_count, + giftsPerRound, + tr::rich), + tr::rich), + }, + { + st::menuIconRatingRefund, + tr::lng_auction_about_bid_title(tr::now), + tr::lng_auction_about_bid_about( + tr::now, + lt_count, + giftsPerRound, + tr::rich), + }, + { + st::menuIconRatingUsers, + tr::lng_auction_about_missed_title(tr::now), + tr::lng_auction_about_missed_about(tr::now, tr::rich), + }, + }; + for (const auto &feature : features) { + box->addRow(Ui::MakeFeatureListEntry(box, feature)); + } + + const auto close = Fn([weak = base::make_weak(box)] { + if (const auto strong = weak.get()) { + strong->closeBox(); + } + }); + box->addButton( + rpl::single(QString()), + understood ? [=] { understood(close); } : close + )->setText(rpl::single(Ui::Text::IconEmoji( + &st::infoStarsUnderstood + ).append(' ').append(tr::lng_auction_about_understood(tr::now)))); +} + +void AuctionGotGiftsBox(not_null box, int count) { } void AuctionInfoBox( @@ -375,7 +492,7 @@ void AuctionInfoBox( box->resizeToWidth(box->widthNoMargins()); about->setClickHandlerFilter([=](const auto &...) { - show->show(Box(AuctionAboutBox, rounds, perRound)); + show->show(Box(AuctionAboutBox, rounds, perRound, nullptr)); return false; }); @@ -383,42 +500,46 @@ void AuctionInfoBox( AuctionInfoTable(box, box->verticalLayout(), state->value.value()), st::boxRowPadding + st::auctionInfoTableMargin); + state->value.value( + ) | rpl::map([=](const Data::GiftAuctionState &state) { + return state.my.gotCount; + }) | rpl::filter( + rpl::mappers::_1 > 0 + ) | rpl::take(1) | rpl::start_with_next([=](int count) { + box->addRow( + object_ptr( + box, + tr::lng_auction_bought( + lt_count_decimal, + rpl::single(count * 1.), + lt_emoji, + rpl::single(Data::SingleCustomEmoji( + state->value.current().gift->document)), + lt_arrow, + rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)), + tr::link), + st::uniqueGiftValueAvailableLink, + st::defaultPopupMenu, + Core::TextContext({ .session = &show->session() })), + st::boxRowPadding + st::uniqueGiftValueAvailableMargin, + style::al_top + )->setClickHandlerFilter([=](const auto &...) { + const auto now = state->value.current().my.gotCount; + show->show(Box(AuctionGotGiftsBox, now)); + return false; + }); + }, box->lifetime()); + const auto button = box->addButton(rpl::single(QString()), [=] { - if (state->value.current().finished() || !state->minutesLeft.current()) { + if (state->value.current().finished() + || !state->minutesLeft.current()) { box->closeBox(); return; } - - const auto save = [=](int amount) { - const auto &now = state->value.current(); - const auto was = (now.my.bid > 0); - const auto perRound = now.gift->auctionGiftsPerRound; - const auto done = [=](Payments::CheckoutResult result) { - if (result == Payments::CheckoutResult::Paid) { - show->showToast({ - .title = (was - ? tr::lng_auction_bid_increased_title - : tr::lng_auction_bid_placed_title)( - tr::now), - .text = tr::lng_auction_bid_done_text( - tr::now, - lt_count, - perRound, - tr::rich) - }); - } - }; - PlaceAuctionBid( - show, - peer, - amount, - now, - done); - }; const auto bidBox = show->show(MakeAuctionBidBox({ + .peer = peer, .show = show, .state = state->value.value(), - .save = save, })); bidBox->boxClosing( ) | rpl::start_with_next([=] { @@ -481,6 +602,59 @@ void AuctionInfoBox( ? st::attentionButtonFg->c : std::optional()); }, box->lifetime()); + + box->setNoContentMargin(true); + const auto close = CreateChild( + box->verticalLayout(), + st::boxTitleClose); + close->setClickedCallback([=] { box->closeBox(); }); + box->verticalLayout()->widthValue() | rpl::start_with_next([=](int) { + close->moveToRight(0, 0); + }, close->lifetime()); +} + +void ChooseAndShowAuctionBox( + std::shared_ptr show, + not_null peer, + std::shared_ptr> state, + Fn boxClosed) { + const auto local = &peer->session().local(); + const auto &now = state->current(); + const auto finished = now.finished() + || (now.endDate <= base::unixtime::now()); + const auto showBidBox = now.my.bid && !finished; + const auto showInfoBox = !showBidBox + && (local->readPref(kAuctionAboutShownPref) || finished); + auto box = base::weak_qptr(); + if (showBidBox) { + box = show->show(MakeAuctionBidBox({ + .peer = peer, + .show = show, + .state = state->value(), + })); + } else if (showInfoBox) { + box = show->show(Box( + AuctionInfoBox, + show, + peer, + state->value())); + } else { + local->writePref(kAuctionAboutShownPref, true); + const auto understood = [=](Fn close) { + ChooseAndShowAuctionBox(show, peer, state, close); + }; + box = show->show(Box( + AuctionAboutBox, + now.totalRounds, + now.gift->auctionGiftsPerRound, + understood)); + } + if (const auto strong = box.get()) { + strong->boxClosing( + ) | rpl::start_with_next(boxClosed, strong->lifetime()); + } else { + boxClosed(); + } } } // namespace @@ -506,13 +680,10 @@ rpl::lifetime ShowStarGiftAuction( (*value) = std::move(state); if (initial) { if (const auto strong = weak.get()) { - const auto box = strong->show(Box( - AuctionInfoBox, - strong->uiShow(), - peer, - value->value())); - box->boxClosing( - ) | rpl::start_with_next(boxClosed, box->lifetime()); + const auto show = strong->uiShow(); + ChooseAndShowAuctionBox(show, peer, value, boxClosed); + } else { + boxClosed(); } } }); diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index ec6f5e39df..01fc76bbc2 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1593,6 +1593,7 @@ confcallLinkButton: RoundButton(defaultActiveButton) { confcallLinkBoxInitial: Box(defaultBox) { buttonPadding: margins(12px, 11px, 24px, 96px); buttonHeight: 42px; + buttonWide: true; button: confcallLinkButton; shadowIgnoreTopSkip: true; } diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.cpp b/Telegram/SourceFiles/calls/group/calls_group_common.cpp index dfd48d3203..c21168430f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_common.cpp @@ -77,13 +77,16 @@ object_ptr ScreenSharingPrivacyRequestBox() { #endif // Q_OS_MAC } -object_ptr MakeJoinCallLogo(not_null parent) { - const auto logoSize = st::confcallJoinLogo.size(); - const auto logoOuter = logoSize.grownBy(st::confcallJoinLogoPadding); +object_ptr MakeRoundActiveLogo( + not_null parent, + const style::icon &icon, + const style::margins &padding) { + const auto logoSize = icon.size(); + const auto logoOuter = logoSize.grownBy(padding); auto result = object_ptr(parent); const auto logo = result.data(); logo->resize(logo->width(), logoOuter.height()); - logo->paintRequest() | rpl::start_with_next([=] { + logo->paintRequest() | rpl::start_with_next([=, &icon] { if (logo->width() < logoOuter.width()) { return; } @@ -94,11 +97,18 @@ object_ptr MakeJoinCallLogo(not_null parent) { p.setBrush(st::windowBgActive); p.setPen(Qt::NoPen); p.drawEllipse(outer); - st::confcallJoinLogo.paintInCenter(p, outer); + icon.paintInCenter(p, outer); }, logo->lifetime()); return result; } +object_ptr MakeJoinCallLogo(not_null parent) { + return MakeRoundActiveLogo( + parent, + st::confcallJoinLogo, + st::confcallJoinLogoPadding); +} + void ConferenceCallJoinConfirm( not_null box, std::shared_ptr call, diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.h b/Telegram/SourceFiles/calls/group/calls_group_common.h index 4ee9e08288..c0b255c498 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.h +++ b/Telegram/SourceFiles/calls/group/calls_group_common.h @@ -162,6 +162,10 @@ using StickedTooltips = base::flags; [[nodiscard]] object_ptr ScreenSharingPrivacyRequestBox(); +[[nodiscard]] object_ptr MakeRoundActiveLogo( + not_null parent, + const style::icon &icon, + const style::margins &padding); [[nodiscard]] object_ptr MakeJoinCallLogo( not_null parent); diff --git a/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp b/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp index 0f6dc3569c..08dd10827e 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_session.h" #include "settings/settings_premium.h" +#include "ui/controls/feature_list.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" @@ -40,12 +41,6 @@ struct State { bool premium = false; }; -struct Feature { - const style::icon &icon; - QString title; - TextWithEntities about; -}; - [[nodiscard]] Ui::Toast::Config ToastAlready(TimeId left) { return { .title = tr::lng_stealth_mode_already_title(tr::now), @@ -134,7 +129,7 @@ struct Feature { }) | rpl::flatten_latest(); } -[[nodiscard]] Feature FeaturePast() { +[[nodiscard]] Ui::FeatureListEntry FeaturePast() { return { .icon = st::storiesStealthFeaturePastIcon, .title = tr::lng_stealth_mode_past_title(tr::now), @@ -142,7 +137,7 @@ struct Feature { }; } -[[nodiscard]] Feature FeatureNext() { +[[nodiscard]] Ui::FeatureListEntry FeatureNext() { return { .icon = st::storiesStealthFeatureNextIcon, .title = tr::lng_stealth_mode_next_title(tr::now), @@ -202,45 +197,6 @@ struct Feature { st::storiesStealthAboutMargin); } -[[nodiscard]] object_ptr MakeFeature( - QWidget *parent, - Feature feature) { - auto result = object_ptr>( - parent, - object_ptr(parent), - st::storiesStealthFeatureMargin); - const auto widget = result->entity(); - const auto icon = Ui::CreateChild( - widget, - feature.icon, - st::storiesStealthFeatureIconPosition); - const auto title = Ui::CreateChild( - widget, - feature.title, - st::storiesStealthFeatureTitle); - const auto about = Ui::CreateChild( - widget, - rpl::single(feature.about), - st::storiesStealthFeatureAbout); - icon->show(); - title->show(); - about->show(); - widget->widthValue( - ) | rpl::start_with_next([=](int width) { - const auto left = st::storiesStealthFeatureLabelLeft; - const auto available = width - left; - title->resizeToWidth(available); - about->resizeToWidth(available); - auto top = 0; - title->move(left, top); - top += title->height() + st::storiesStealthFeatureSkip; - about->move(left, top); - top += about->height(); - widget->resize(width, top); - }, widget->lifetime()); - return result; -} - [[nodiscard]] object_ptr MakeButton( QWidget *parent, rpl::producer state) { @@ -329,9 +285,17 @@ struct Feature { box->addRow(MakeLogo(box)); box->addRow(MakeTitle(box), style::al_top); box->addRow(MakeAbout(box, data->state.value()), style::al_top); - box->addRow(MakeFeature(box, FeaturePast())); + const auto make = [&](const Ui::FeatureListEntry &entry) { + return Ui::MakeFeatureListEntry( + box, + entry, + {}, + st::storiesStealthFeatureTitle, + st::storiesStealthFeatureAbout); + }; + box->addRow(make(FeaturePast())); box->addRow( - MakeFeature(box, FeatureNext()), + make(FeatureNext()), (st::boxRowPadding + QMargins(0, 0, 0, st::storiesStealthBoxBottom))); box->setNoContentMargin(true); diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index ac8f432904..ce46902e02 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -1093,8 +1093,6 @@ storiesStealthFeatureAbout: FlatLabel(defaultFlatLabel) { } storiesStealthFeaturePastIcon: icon{{ "stories/stealth_5m", storiesComposeBlue }}; storiesStealthFeatureNextIcon: icon{{ "stories/stealth_25m", storiesComposeBlue }}; -storiesStealthFeatureIconPosition: point(3px, 7px); -storiesStealthFeatureMargin: margins(0px, 8px, 0px, 6px); storiesStealthFeatureLabelLeft: 46px; storiesStealthFeatureSkip: 2px; storiesStealthBoxBottom: 11px; diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl index 798b9d8cab..10f82fa822 100644 --- a/Telegram/SourceFiles/mtproto/scheme/api.tl +++ b/Telegram/SourceFiles/mtproto/scheme/api.tl @@ -1524,8 +1524,7 @@ inputInvoiceStarGiftResale#c39f5324 flags:# ton:flags.0?true slug:string to_id:I inputInvoiceStarGiftPrepaidUpgrade#9a0b48b8 peer:InputPeer hash:string = InputInvoice; inputInvoicePremiumAuthCode#3e77f614 purpose:InputStorePaymentPurpose = InputInvoice; inputInvoiceStarGiftDropOriginalDetails#923d8d1 stargift:InputSavedStarGift = InputInvoice; -inputInvoiceStarGiftAuctionBid#77d28da6 flags:# hide_name:flags.0?true peer:InputPeer gift_id:long bid_amount:long message:flags.1?TextWithEntities = InputInvoice; -inputInvoiceStarGiftAuctionUpdateBid#85275c98 gift_id:long bid_amount:long = InputInvoice; +inputInvoiceStarGiftAuctionBid#1ecafa10 flags:# hide_name:flags.0?true update_bid:flags.2?true peer:flags.3?InputPeer gift_id:long bid_amount:long message:flags.1?TextWithEntities = InputInvoice; payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 1ef7924416..a1a698d8a3 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -2438,7 +2438,7 @@ void UniqueGiftValueBox( platform(Ui::Text::WithEntities), lt_arrow, rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)), - [](const QString &text) { return Ui::Text::Link(text); }), + tr::link), st::uniqueGiftValueAvailableLink, st::defaultPopupMenu, Core::TextContext({ .session = &show->session() })), diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index 13c46b8bc0..ab62a58beb 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -3676,19 +3676,6 @@ Webview::StorageId TonSiteStorageId() { return result; } -template <> -std::optional Account::readPrefImpl(std::string_view key) { - if (const auto data = readPrefGeneric(key)) { - return !data->isEmpty(); - } - return {}; -} - -template <> -void Account::writePrefImpl(std::string_view key, bool value) { - writePrefGeneric(key, value ? "\x1"_q : QByteArray()); -} - void Account::clearPref(std::string_view key) { const auto i = _prefs.find(QByteArray(key.data(), key.size())); if (i == end(_prefs)) { @@ -3790,4 +3777,18 @@ void Account::readPrefs() { _prefs = std::move(map); } +// Define your own pref types in the similar way. +template <> +std::optional Account::readPrefImpl(std::string_view key) { + if (const auto data = readPrefGeneric(key)) { + return !data->isEmpty(); + } + return {}; +} + +template <> +void Account::writePrefImpl(std::string_view key, bool value) { + writePrefGeneric(key, value ? "\x1"_q : QByteArray()); +} + } // namespace Storage diff --git a/Telegram/SourceFiles/ui/controls/feature_list.cpp b/Telegram/SourceFiles/ui/controls/feature_list.cpp new file mode 100644 index 0000000000..b591609543 --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/feature_list.cpp @@ -0,0 +1,61 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/controls/feature_list.h" + +#include "base/object_ptr.h" +#include "info/profile/info_profile_icon.h" +#include "ui/widgets/labels.h" +#include "styles/style_info.h" + +namespace Ui { + +object_ptr MakeFeatureListEntry( + QWidget *parent, + FeatureListEntry feature, + const Text::MarkedContext &context, + const style::FlatLabel &stTitle, + const style::FlatLabel &stAbout) { + auto result = object_ptr>( + parent, + object_ptr(parent), + st::infoStarsFeatureMargin); + const auto widget = result->entity(); + const auto icon = CreateChild( + widget, + feature.icon, + st::infoStarsFeatureIconPosition); + const auto title = CreateChild( + widget, + feature.title, + stTitle); + const auto about = CreateChild( + widget, + rpl::single(feature.about), + stAbout, + st::defaultPopupMenu, + context); + icon->show(); + title->show(); + about->show(); + widget->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto left = st::infoStarsFeatureLabelLeft; + const auto available = width - left; + title->resizeToWidth(available); + about->resizeToWidth(available); + auto top = 0; + title->move(left, top); + top += title->height() + st::infoStarsFeatureSkip; + about->move(left, top); + top += about->height(); + widget->resize(width, top); + }, widget->lifetime()); + return result; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/feature_list.h b/Telegram/SourceFiles/ui/controls/feature_list.h new file mode 100644 index 0000000000..573d8e53eb --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/feature_list.h @@ -0,0 +1,40 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace style { +struct FlatLabel; +} // namespace style + +namespace st { +extern const style::FlatLabel &infoStarsFeatureTitle; +extern const style::FlatLabel &infoStarsFeatureAbout; +} // namespace st + +namespace Ui::Text { +struct MarkedContext; +} // namespace Ui::Text + +namespace Ui { + +class RpWidget; + +struct FeatureListEntry { + const style::icon &icon; + QString title; + TextWithEntities about; +}; + +[[nodiscard]] object_ptr MakeFeatureListEntry( + QWidget *parent, + FeatureListEntry feature, + const Text::MarkedContext &context = {}, + const style::FlatLabel &stTitle = st::infoStarsFeatureTitle, + const style::FlatLabel &stAbout = st::infoStarsFeatureAbout); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/stars_rating.cpp b/Telegram/SourceFiles/ui/controls/stars_rating.cpp index 3840c559a8..10d8d02085 100644 --- a/Telegram/SourceFiles/ui/controls/stars_rating.cpp +++ b/Telegram/SourceFiles/ui/controls/stars_rating.cpp @@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/controls/stars_rating.h" #include "base/unixtime.h" -#include "info/profile/info_profile_icon.h" #include "lang/lang_keys.h" +#include "ui/controls/feature_list.h" #include "ui/effects/premium_bubble.h" #include "ui/effects/premium_graphics.h" #include "ui/layers/generic_box.h" @@ -41,54 +41,6 @@ constexpr auto kAutoCollapseTimeout = 4 * crl::time(1000); using Counters = Data::StarsRating; -struct Feature { - const style::icon &icon; - QString title; - TextWithEntities about; -}; - -[[nodiscard]] object_ptr MakeFeature( - QWidget *parent, - Feature feature, - const Text::MarkedContext &context) { - auto result = object_ptr>( - parent, - object_ptr(parent), - st::infoStarsFeatureMargin); - const auto widget = result->entity(); - const auto icon = Ui::CreateChild( - widget, - feature.icon, - st::infoStarsFeatureIconPosition); - const auto title = Ui::CreateChild( - widget, - feature.title, - st::infoStarsFeatureTitle); - const auto about = Ui::CreateChild( - widget, - rpl::single(feature.about), - st::infoStarsFeatureAbout, - st::defaultPopupMenu, - context); - icon->show(); - title->show(); - about->show(); - widget->widthValue( - ) | rpl::start_with_next([=](int width) { - const auto left = st::infoStarsFeatureLabelLeft; - const auto available = width - left; - title->resizeToWidth(available); - about->resizeToWidth(available); - auto top = 0; - title->move(left, top); - top += title->height() + st::infoStarsFeatureSkip; - about->move(left, top); - top += about->height(); - widget->resize(width, top); - }, widget->lifetime()); - return result; -} - [[nodiscard]] Counters AdjustByReached(Counters data) { if (data.stars < 0) { return data; @@ -111,7 +63,7 @@ struct Feature { return data; } -[[nodiscard]] Fn BubbleTextFactory( +[[nodiscard]] Fn BubbleTextFactory( int countForScale, int nextLevelCounter) { return [=](int count) { @@ -122,7 +74,7 @@ struct Feature { ? (Lang::FormatCountDecimal((count / 100) / 10.) + 'K') : (Lang::FormatCountDecimal((count / 100'000) / 10.) + 'M'); }; - return Ui::Premium::BubbleText{ + return Premium::BubbleText{ .counter = counter(count), .additional = (nextLevelCounter ? (u"/"_q + counter(nextLevelCounter)) @@ -141,7 +93,7 @@ void FillRatingLimit( int nextLevelStars, bool hideCount) { const auto addSkip = [&](int skip) { - container->add(object_ptr(container, skip)); + container->add(object_ptr(container, skip)); }; const auto negative = (type == Premium::BubbleType::NegativeRating); @@ -203,7 +155,7 @@ void FillRatingLimit( rpl::duplicate(bubbleRowState), type, (hideCount - ? [](int) { return Ui::Premium::BubbleText(); } + ? [](int) { return Premium::BubbleText(); } : BubbleTextFactory(starsForScale, nextLevelStars)), negative ? &st::levelNegativeBubble : &st::infoStarsCrown, limitLinePadding); @@ -287,22 +239,22 @@ void AboutRatingBox( ? tr::lng_stars_rating_about( lt_name, rpl::single(TextWithEntities{ name }), - Ui::Text::RichLangValue) | rpl::type_erased() + Text::RichLangValue) | rpl::type_erased() : tr::lng_stars_rating_about_your( - Ui::Text::RichLangValue) | rpl::type_erased(); + Text::RichLangValue) | rpl::type_erased(); if (data.level < 0) { auto text = (data.stars < 0) ? tr::lng_stars_rating_negative_your( lt_count_decimal, rpl::single(-data.stars * 1.), - Ui::Text::RichLangValue) + Text::RichLangValue) : tr::lng_stars_rating_negative( lt_name, rpl::single(TextWithEntities{ name }), - Ui::Text::RichLangValue); + Text::RichLangValue); box->addRow( - object_ptr( + object_ptr( box, std::move(text), st::boostTextNegative), @@ -313,7 +265,7 @@ void AboutRatingBox( } box->addRow( - object_ptr(box, std::move(title), st::infoStarsTitle), + object_ptr(box, std::move(title), st::infoStarsTitle), st::boxRowPadding + QMargins(0, st::boostTitleSkip / 2, 0, 0), style::al_top); @@ -331,17 +283,17 @@ void AboutRatingBox( tr::lng_stars_rating_updates(tr::now, lt_count, days), }, lt_link, - Ui::Text::Link((value + Text::Link((value ? tr::lng_stars_rating_pending_back : tr::lng_stars_rating_pending_preview)( tr::now, lt_arrow, - Ui::Text::IconEmoji(&st::textMoreIconEmoji), - Ui::Text::WithEntities)), - Ui::Text::RichLangValue); + Text::IconEmoji(&st::textMoreIconEmoji), + Text::WithEntities)), + Text::RichLangValue); }); const auto aboutPending = box->addRow( - object_ptr( + object_ptr( box, std::move(text), st::boostTextPending), @@ -360,7 +312,7 @@ void AboutRatingBox( } box->addRow( - object_ptr( + object_ptr( box, std::move(text), st::boostText), @@ -369,12 +321,12 @@ void AboutRatingBox( style::al_top )->setTryMakeSimilarLines(true); - auto helper = Ui::Text::CustomEmojiHelper(); + auto helper = Text::CustomEmojiHelper(); const auto makeBadge = [&]( const QString &text, const style::RoundButton &st) { return helper.paletteDependent( - Ui::Text::CustomEmojiTextBadge(text, st)); + Text::CustomEmojiTextBadge(text, st)); }; const auto makeActive = [&](const QString &text) { return makeBadge(text, st::customEmojiTextBadge); @@ -382,7 +334,7 @@ void AboutRatingBox( const auto makeInactive = [&](const QString &text) { return makeBadge(text, st::infoRatingDeductedBadge); }; - const auto features = std::vector{ + const auto features = std::vector{ { st::menuIconRatingGifts, tr::lng_stars_title_gifts_telegram(tr::now), @@ -390,7 +342,7 @@ void AboutRatingBox( tr::now, lt_emoji, makeActive(tr::lng_stars_rating_added(tr::now)), - Ui::Text::RichLangValue), + Text::RichLangValue), }, { st::menuIconRatingUsers, @@ -399,7 +351,7 @@ void AboutRatingBox( tr::now, lt_emoji, makeActive(tr::lng_stars_rating_added(tr::now)), - Ui::Text::RichLangValue), + Text::RichLangValue), }, { st::menuIconRatingRefund, @@ -408,16 +360,16 @@ void AboutRatingBox( tr::now, lt_emoji, makeInactive(tr::lng_stars_rating_deducted(tr::now)), - Ui::Text::RichLangValue), + Text::RichLangValue), }, }; const auto context = helper.context(); for (const auto &feature : features) { - box->addRow(MakeFeature(box, feature, context)); + box->addRow(MakeFeatureListEntry(box, feature, context)); } box->addButton(rpl::single(QString()), [=] { box->closeBox(); - })->setText(rpl::single(Ui::Text::IconEmoji( + })->setText(rpl::single(Text::IconEmoji( &st::infoStarsUnderstood ).append(' ').append(tr::lng_stars_rating_understood(tr::now)))); } @@ -462,11 +414,11 @@ void AboutRatingBox( StarsRating::StarsRating( QWidget *parent, - std::shared_ptr show, + std::shared_ptr show, const QString &name, rpl::producer value, Fn pending) -: _widget(std::make_unique(parent)) +: _widget(std::make_unique(parent)) , _show(std::move(show)) , _name(name) , _value(std::move(value)) diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 21e51b60f5..a91f95fcba 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -281,7 +281,7 @@ uniqueGiftResalePadding: margins(4px, 4px, 8px, 4px); uniqueGiftResaleMargin: margins(10px, 10px, 10px, 10px); uniqueGiftTitleTop: 140px; uniqueGiftSubtitle: FlatLabel(defaultFlatLabel) { - minWidth: 256px; + minWidth: 64px; align: align(top); } uniqueGiftReleasedBy: FlatLabel(uniqueGiftSubtitle) { @@ -444,3 +444,5 @@ videoStreamStarsCover: PremiumCover(creditsLowBalancePremiumCover) { auctionInfoPreviewMargin: margins(0px, 24px, 0px, 8px); auctionInfoSubtitleSkip: 8px; auctionInfoTableMargin: margins(0px, 12px, 0px, 12px); +auctionAboutLogo: icon {{ "calls/group_call_logo", windowFgActive }}; +auctionAboutLogoPadding: margins(8px, 8px, 8px, 8px); diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 902f1f9353..6f2673598a 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -391,6 +391,8 @@ PRIVATE ui/controls/download_bar.h ui/controls/emoji_button.cpp ui/controls/emoji_button.h + ui/controls/feature_list.cpp + ui/controls/feature_list.h ui/controls/filter_link_header.cpp ui/controls/filter_link_header.h ui/controls/jump_down_button.cpp