| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931 |
- /*
- 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/boxes/boost_box.h"
- #include "info/profile/info_profile_icon.h"
- #include "lang/lang_keys.h"
- #include "ui/boxes/confirm_box.h"
- #include "ui/effects/fireworks_animation.h"
- #include "ui/effects/premium_bubble.h"
- #include "ui/effects/premium_graphics.h"
- #include "ui/layers/generic_box.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/buttons.h"
- #include "ui/wrap/fade_wrap.h"
- #include "ui/painter.h"
- #include "styles/style_giveaway.h"
- #include "styles/style_layers.h"
- #include "styles/style_premium.h"
- #include <QtGui/QGuiApplication>
- namespace Ui {
- namespace {
- [[nodiscard]] BoostCounters AdjustByReached(BoostCounters data) {
- const auto exact = (data.boosts == data.thisLevelBoosts);
- const auto reached = !data.nextLevelBoosts || (exact && data.mine > 0);
- if (reached) {
- --data.level;
- data.boosts = data.nextLevelBoosts = std::max({
- data.boosts,
- data.thisLevelBoosts,
- 1
- });
- data.thisLevelBoosts = 0;
- } else {
- data.boosts = std::max(data.thisLevelBoosts, data.boosts);
- data.nextLevelBoosts = std::max(
- data.nextLevelBoosts,
- data.boosts + 1);
- }
- return data;
- }
- [[nodiscard]] object_ptr<Ui::RpWidget> MakeTitle(
- not_null<Ui::RpWidget*> parent,
- rpl::producer<QString> title,
- rpl::producer<QString> repeated,
- bool centered = true) {
- auto result = object_ptr<Ui::RpWidget>(parent);
- struct State {
- not_null<Ui::FlatLabel*> title;
- not_null<Ui::FlatLabel*> repeated;
- };
- const auto notEmpty = [](const QString &text) {
- return !text.isEmpty();
- };
- const auto state = parent->lifetime().make_state<State>(State{
- .title = Ui::CreateChild<Ui::FlatLabel>(
- result.data(),
- rpl::duplicate(title),
- st::boostTitle),
- .repeated = Ui::CreateChild<Ui::FlatLabel>(
- result.data(),
- rpl::duplicate(repeated) | rpl::filter(notEmpty),
- st::boostTitleBadge),
- });
- state->title->show();
- state->repeated->showOn(std::move(repeated) | rpl::map(notEmpty));
- result->resize(result->width(), st::boostTitle.style.font->height);
- rpl::combine(
- result->widthValue(),
- rpl::duplicate(title),
- state->repeated->shownValue(),
- state->repeated->widthValue()
- ) | rpl::start_with_next([=](int outer, auto&&, bool shown, int badge) {
- const auto repeated = shown ? badge : 0;
- const auto skip = st::boostTitleBadgeSkip;
- const auto available = outer - repeated - skip;
- const auto use = std::min(state->title->textMaxWidth(), available);
- state->title->resizeToWidth(use);
- const auto left = centered
- ? (outer - use - skip - repeated) / 2
- : 0;
- state->title->moveToLeft(left, 0);
- const auto mleft = st::boostTitleBadge.margin.left();
- const auto mtop = st::boostTitleBadge.margin.top();
- state->repeated->moveToLeft(left + use + skip + mleft, mtop);
- }, result->lifetime());
- const auto badge = state->repeated;
- badge->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(badge);
- auto hq = PainterHighQualityEnabler(p);
- const auto radius = std::min(badge->width(), badge->height()) / 2;
- p.setPen(Qt::NoPen);
- p.setBrush(st::premiumButtonBg2);
- p.drawRoundedRect(badge->rect(), radius, radius);
- }, badge->lifetime());
- return result;
- }
- [[nodiscard]] object_ptr<Ui::FlatLabel> MakeFeaturesBadge(
- not_null<QWidget*> parent,
- rpl::producer<QString> text) {
- return MakeBoostFeaturesBadge(parent, std::move(text), [](QRect rect) {
- auto gradient = QLinearGradient(
- rect.topLeft(),
- rect.topRight());
- gradient.setStops(Ui::Premium::GiftGradientStops());
- return QBrush(gradient);
- });
- }
- void AddFeaturesList(
- not_null<Ui::VerticalLayout*> container,
- const Ui::BoostFeatures &features,
- int startFromLevel,
- bool group) {
- const auto add = [&](
- rpl::producer<TextWithEntities> text,
- const style::icon &st) {
- const auto label = container->add(
- object_ptr<Ui::FlatLabel>(
- container,
- std::move(text),
- st::boostFeatureLabel),
- st::boostFeaturePadding);
- object_ptr<Info::Profile::FloatingIcon>(
- label,
- st,
- st::boostFeatureIconPosition);
- };
- const auto proj = &Ui::Text::RichLangValue;
- const auto lowMax = std::max({
- features.linkLogoLevel,
- features.transcribeLevel,
- features.emojiPackLevel,
- features.emojiStatusLevel,
- features.wallpaperLevel,
- features.customWallpaperLevel,
- (features.nameColorsByLevel.empty()
- ? 0
- : features.nameColorsByLevel.back().first),
- (features.linkStylesByLevel.empty()
- ? 0
- : features.linkStylesByLevel.back().first),
- });
- const auto highMax = std::max(lowMax, features.sponsoredLevel);
- auto nameColors = 0;
- auto linkStyles = 0;
- for (auto i = std::max(startFromLevel, 1); i <= highMax; ++i) {
- if ((i > lowMax) && (i < highMax)) {
- continue;
- }
- const auto unlocks = (i == startFromLevel);
- container->add(
- MakeFeaturesBadge(
- container,
- (unlocks
- ? tr::lng_boost_level_unlocks
- : tr::lng_boost_level)(
- lt_count,
- rpl::single(float64(i)))),
- st::boostLevelBadgePadding);
- if (i >= features.sponsoredLevel) {
- add(tr::lng_channel_earn_off(proj), st::boostFeatureOffSponsored);
- }
- if (i >= features.customWallpaperLevel) {
- add(
- (group
- ? tr::lng_feature_custom_background_group
- : tr::lng_feature_custom_background_channel)(proj),
- st::boostFeatureCustomBackground);
- }
- if (i >= features.wallpaperLevel) {
- add(
- (group
- ? tr::lng_feature_backgrounds_group
- : tr::lng_feature_backgrounds_channel)(
- lt_count,
- rpl::single(float64(features.wallpapersCount)),
- proj),
- st::boostFeatureBackground);
- }
- if (i >= features.emojiStatusLevel) {
- add(
- tr::lng_feature_emoji_status(proj),
- st::boostFeatureEmojiStatus);
- }
- if (group && i >= features.transcribeLevel) {
- add(
- tr::lng_feature_transcribe(proj),
- st::boostFeatureTranscribe);
- }
- if (group && i >= features.emojiPackLevel) {
- add(
- tr::lng_feature_custom_emoji_pack(proj),
- st::boostFeatureCustomEmoji);
- }
- if (!group) {
- if (const auto j = features.linkStylesByLevel.find(i)
- ; j != end(features.linkStylesByLevel)) {
- linkStyles += j->second;
- }
- if (i >= features.linkLogoLevel) {
- add(
- tr::lng_feature_link_emoji(proj),
- st::boostFeatureCustomLink);
- }
- if (linkStyles > 0) {
- add(tr::lng_feature_link_style_channel(
- lt_count,
- rpl::single(float64(linkStyles)),
- proj
- ), st::boostFeatureLink);
- }
- if (const auto j = features.nameColorsByLevel.find(i)
- ; j != end(features.nameColorsByLevel)) {
- nameColors += j->second;
- }
- if (nameColors > 0) {
- add(tr::lng_feature_name_color_channel(
- lt_count,
- rpl::single(float64(nameColors)),
- proj
- ), st::boostFeatureName);
- }
- add(tr::lng_feature_reactions(
- lt_count,
- rpl::single(float64(i)),
- proj
- ), st::boostFeatureCustomReactions);
- }
- add(
- tr::lng_feature_stories(lt_count, rpl::single(float64(i)), proj),
- st::boostFeatureStories);
- }
- }
- } // namespace
- void StartFireworks(not_null<QWidget*> parent) {
- const auto result = Ui::CreateChild<RpWidget>(parent.get());
- result->setAttribute(Qt::WA_TransparentForMouseEvents);
- result->setGeometry(parent->rect());
- result->show();
- auto &lifetime = result->lifetime();
- const auto animation = lifetime.make_state<FireworksAnimation>([=] {
- result->update();
- });
- result->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(result);
- if (!animation->paint(p, result->rect())) {
- crl::on_main(result, [=] { delete result; });
- }
- }, lifetime);
- }
- void BoostBox(
- not_null<GenericBox*> box,
- BoostBoxData data,
- Fn<void(Fn<void(BoostCounters)>)> boost) {
- box->setWidth(st::boxWideWidth);
- box->setStyle(st::boostBox);
- //AssertIsDebug();
- //data.boost = {
- // .level = 2,
- // .boosts = 3,
- // .thisLevelBoosts = 2,
- // .nextLevelBoosts = 5,
- // .mine = 2,
- //};
- struct State {
- rpl::variable<BoostCounters> data;
- rpl::variable<bool> full;
- bool submitted = false;
- };
- const auto state = box->lifetime().make_state<State>();
- state->data = std::move(data.boost);
- FillBoostLimit(
- BoxShowFinishes(box),
- box->verticalLayout(),
- state->data.value(),
- st::boxRowPadding);
- box->setMaxHeight(st::boostBoxMaxHeight);
- const auto close = box->addTopButton(
- st::boxTitleClose,
- [=] { box->closeBox(); });
- const auto name = data.name;
- auto title = state->data.value(
- ) | rpl::map([=](BoostCounters counters) {
- return (counters.mine > 0)
- ? tr::lng_boost_channel_you_title(
- lt_channel,
- rpl::single(name))
- : !counters.nextLevelBoosts
- ? tr::lng_boost_channel_title_max()
- : counters.level
- ? (data.group
- ? tr::lng_boost_channel_title_more_group()
- : tr::lng_boost_channel_title_more())
- : (data.group
- ? tr::lng_boost_channel_title_first_group()
- : tr::lng_boost_channel_title_first());
- }) | rpl::flatten_latest();
- auto repeated = state->data.value(
- ) | rpl::map([=](BoostCounters counters) {
- return (counters.mine > 1) ? u"x%1"_q.arg(counters.mine) : u""_q;
- });
- const auto wasMine = state->data.current().mine;
- const auto wasLifting = data.lifting;
- auto text = state->data.value(
- ) | rpl::map([=](BoostCounters counters) {
- const auto lifting = wasLifting
- ? (wasLifting
- - std::clamp(counters.mine - wasMine, 0, wasLifting - 1))
- : 0;
- const auto bold = Ui::Text::Bold(name);
- const auto now = counters.boosts;
- const auto full = !counters.nextLevelBoosts;
- const auto left = (counters.nextLevelBoosts > now)
- ? (counters.nextLevelBoosts - now)
- : 0;
- auto post = tr::lng_boost_channel_post_stories(
- lt_count,
- rpl::single(float64(counters.level + (left ? 1 : 0))),
- Ui::Text::RichLangValue);
- return (lifting > 1)
- ? tr::lng_boost_group_lift_restrictions_many(
- lt_count,
- rpl::single(float64(lifting)),
- Ui::Text::RichLangValue)
- : lifting
- ? tr::lng_boost_group_lift_restrictions(Ui::Text::RichLangValue)
- : (counters.mine || full)
- ? (left
- ? tr::lng_boost_channel_needs_unlock(
- lt_count,
- rpl::single(float64(left)),
- lt_channel,
- rpl::single(bold),
- Ui::Text::RichLangValue)
- : (!counters.level
- ? (data.group
- ? tr::lng_boost_channel_reached_first_group
- : tr::lng_boost_channel_reached_first)(
- Ui::Text::RichLangValue)
- : (data.group
- ? tr::lng_boost_channel_reached_more_group
- : tr::lng_boost_channel_reached_more)(
- lt_count,
- rpl::single(float64(counters.level)),
- lt_post,
- std::move(post),
- Ui::Text::RichLangValue)))
- : tr::lng_boost_channel_needs_unlock(
- lt_count,
- rpl::single(float64(left)),
- lt_channel,
- rpl::single(bold),
- Ui::Text::RichLangValue);
- }) | rpl::flatten_latest();
- if (wasLifting) {
- state->data.value(
- ) | rpl::start_with_next([=](BoostCounters counters) {
- if (counters.mine - wasMine >= wasLifting) {
- box->closeBox();
- }
- }, box->lifetime());
- }
- auto faded = object_ptr<Ui::FadeWrap<>>(
- close->parentWidget(),
- MakeTitle(
- box,
- (data.group
- ? tr::lng_boost_group_button
- : tr::lng_boost_channel_button)(),
- rpl::duplicate(repeated),
- false));
- const auto titleInner = faded.data();
- titleInner->move(st::boxTitlePosition);
- titleInner->resizeToWidth(st::boxWideWidth
- - st::boxTitleClose.width
- - st::boxTitlePosition.x());
- titleInner->hide(anim::type::instant);
- crl::on_main(titleInner, [=] {
- titleInner->raise();
- titleInner->toggleOn(rpl::single(
- rpl::empty
- ) | rpl::then(
- box->scrolls()
- ) | rpl::map([=] {
- return box->scrollTop() > 0;
- }));
- });
- box->addRow(
- MakeTitle(box, std::move(title), std::move(repeated)),
- st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0));
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box,
- std::move(text),
- st::boostText),
- (st::boxRowPadding
- + QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
- const auto current = state->data.current();
- box->setTitle(rpl::single(QString()));
- AddFeaturesList(
- box->verticalLayout(),
- data.features,
- current.level + (current.nextLevelBoosts ? 1 : 0),
- data.group);
- const auto allowMulti = data.allowMulti;
- auto submit = state->data.value(
- ) | rpl::map([=](BoostCounters counters) {
- return (!counters.nextLevelBoosts || (counters.mine && !allowMulti))
- ? tr::lng_box_ok()
- : (counters.mine > 0)
- ? tr::lng_boost_again_button()
- : data.group
- ? tr::lng_boost_group_button()
- : tr::lng_boost_channel_button();
- }) | rpl::flatten_latest();
- const auto button = box->addButton(rpl::duplicate(submit), [=] {
- if (state->submitted) {
- return;
- } else if (state->data.current().nextLevelBoosts > 0
- && (allowMulti || !state->data.current().mine)) {
- state->submitted = true;
- const auto was = state->data.current().mine;
- //AssertIsDebug();
- //state->submitted = false;
- //if (state->data.current().level == 5
- // && state->data.current().boosts == 11) {
- // state->data = BoostCounters{
- // .level = 5,
- // .boosts = 14,
- // .thisLevelBoosts = 9,
- // .nextLevelBoosts = 15,
- // .mine = 14,
- // };
- //} else if (state->data.current().level == 5) {
- // state->data = BoostCounters{
- // .level = 7,
- // .boosts = 16,
- // .thisLevelBoosts = 15,
- // .nextLevelBoosts = 19,
- // .mine = 16,
- // };
- //} else if (state->data.current().level == 4) {
- // state->data = BoostCounters{
- // .level = 5,
- // .boosts = 11,
- // .thisLevelBoosts = 9,
- // .nextLevelBoosts = 15,
- // .mine = 9,
- // };
- //} else if (state->data.current().level == 3) {
- // state->data = BoostCounters{
- // .level = 4,
- // .boosts = 7,
- // .thisLevelBoosts = 7,
- // .nextLevelBoosts = 9,
- // .mine = 5,
- // };
- //} else {
- // state->data = BoostCounters{
- // .level = 3,
- // .boosts = 5,
- // .thisLevelBoosts = 5,
- // .nextLevelBoosts = 7,
- // .mine = 3,
- // };
- //}
- //return;
- boost(crl::guard(box, [=](BoostCounters result) {
- state->submitted = false;
- if (result.thisLevelBoosts || result.nextLevelBoosts) {
- if (result.mine > was) {
- StartFireworks(box->parentWidget());
- }
- state->data = result;
- }
- }));
- } else {
- box->closeBox();
- }
- });
- rpl::combine(
- std::move(submit),
- box->widthValue()
- ) | rpl::start_with_next([=](const QString &, int width) {
- const auto &padding = st::boostBox.buttonPadding;
- button->resizeToWidth(width
- - padding.left()
- - padding.right());
- button->moveToLeft(padding.left(), button->y());
- }, button->lifetime());
- }
- object_ptr<Ui::RpWidget> MakeLinkLabel(
- not_null<QWidget*> parent,
- rpl::producer<QString> text,
- rpl::producer<QString> link,
- std::shared_ptr<Ui::Show> show,
- object_ptr<Ui::RpWidget> right) {
- auto result = object_ptr<Ui::AbstractButton>(parent);
- const auto raw = result.data();
- const auto rawRight = right.release();
- if (rawRight) {
- rawRight->setParent(raw);
- rawRight->show();
- }
- struct State {
- State(
- not_null<QWidget*> parent,
- rpl::producer<QString> value,
- rpl::producer<QString> link)
- : text(std::move(value))
- , link(std::move(link))
- , label(parent, text.value(), st::giveawayGiftCodeLink)
- , bg(st::roundRadiusLarge, st::windowBgOver) {
- }
- rpl::variable<QString> text;
- rpl::variable<QString> link;
- Ui::FlatLabel label;
- Ui::RoundRect bg;
- };
- const auto state = raw->lifetime().make_state<State>(
- raw,
- rpl::duplicate(text),
- std::move(link));
- state->label.setSelectable(true);
- rpl::combine(
- raw->widthValue(),
- std::move(text)
- ) | rpl::start_with_next([=](int outer, const auto&) {
- const auto textWidth = state->label.textMaxWidth();
- const auto skipLeft = st::giveawayGiftCodeLink.margin.left();
- const auto skipRight = rawRight
- ? rawRight->width()
- : st::giveawayGiftCodeLink.margin.right();
- const auto available = outer - skipRight - skipLeft;
- const auto use = std::min(textWidth, available);
- state->label.resizeToWidth(use);
- const auto forCenter = (outer - use) / 2;
- const auto x = (forCenter < skipLeft)
- ? skipLeft
- : (forCenter > outer - skipRight - use)
- ? (outer - skipRight - use)
- : forCenter;
- state->label.moveToLeft(x, st::giveawayGiftCodeLink.margin.top());
- }, raw->lifetime());
- raw->paintRequest() | rpl::start_with_next([=] {
- auto p = QPainter(raw);
- state->bg.paint(p, raw->rect());
- }, raw->lifetime());
- state->label.setAttribute(Qt::WA_TransparentForMouseEvents);
- raw->resize(raw->width(), st::giveawayGiftCodeLinkHeight);
- if (rawRight) {
- raw->widthValue() | rpl::start_with_next([=](int width) {
- rawRight->move(width - rawRight->width(), 0);
- }, raw->lifetime());
- }
- raw->setClickedCallback([=] {
- QGuiApplication::clipboard()->setText(state->link.current());
- show->showToast(tr::lng_username_copied(tr::now));
- });
- return result;
- }
- void BoostBoxAlready(not_null<GenericBox*> box, bool group) {
- ConfirmBox(box, {
- .text = (group
- ? tr::lng_boost_error_already_text_group
- : tr::lng_boost_error_already_text)(Text::RichLangValue),
- .title = tr::lng_boost_error_already_title(),
- .inform = true,
- });
- }
- void GiftForBoostsBox(
- not_null<GenericBox*> box,
- QString channel,
- int receive,
- bool again) {
- ConfirmBox(box, {
- .text = (again
- ? tr::lng_boost_need_more_again
- : tr::lng_boost_need_more_text)(
- lt_count,
- rpl::single(receive) | tr::to_count(),
- lt_channel,
- rpl::single(TextWithEntities{ channel }),
- Text::RichLangValue),
- .title = tr::lng_boost_need_more(),
- .inform = true,
- });
- }
- void GiftedNoBoostsBox(not_null<GenericBox*> box, bool group) {
- InformBox(box, {
- .text = (group
- ? tr::lng_boost_error_gifted_text_group
- : tr::lng_boost_error_gifted_text)(Text::RichLangValue),
- .title = tr::lng_boost_error_gifted_title(),
- });
- }
- void PremiumForBoostsBox(
- not_null<GenericBox*> box,
- bool group,
- Fn<void()> buyPremium) {
- ConfirmBox(box, {
- .text = (group
- ? tr::lng_boost_error_premium_text_group
- : tr::lng_boost_error_premium_text)(Text::RichLangValue),
- .confirmed = buyPremium,
- .confirmText = tr::lng_boost_error_premium_yes(),
- .title = tr::lng_boost_error_premium_title(),
- });
- }
- void AskBoostBox(
- not_null<GenericBox*> box,
- AskBoostBoxData data,
- Fn<void()> openStatistics,
- Fn<void()> startGiveaway) {
- box->setWidth(st::boxWideWidth);
- box->setStyle(st::boostBox);
- FillBoostLimit(
- BoxShowFinishes(box),
- box->verticalLayout(),
- rpl::single(data.boost),
- st::boxRowPadding);
- box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
- auto title = v::match(data.reason.data, [](AskBoostChannelColor) {
- return tr::lng_boost_channel_title_color();
- }, [](AskBoostWallpaper) {
- return tr::lng_boost_channel_title_wallpaper();
- }, [](AskBoostEmojiStatus) {
- return tr::lng_boost_channel_title_status();
- }, [](AskBoostEmojiPack) {
- return tr::lng_boost_group_title_emoji();
- }, [](AskBoostCustomReactions) {
- return tr::lng_boost_channel_title_reactions();
- }, [](AskBoostCpm) {
- return tr::lng_boost_channel_title_cpm();
- }, [](AskBoostWearCollectible) {
- return tr::lng_boost_channel_title_wear();
- });
- auto isGroup = false;
- auto reasonText = v::match(data.reason.data, [&](
- AskBoostChannelColor data) {
- return tr::lng_boost_channel_needs_level_color(
- lt_count,
- rpl::single(float64(data.requiredLevel)),
- Ui::Text::RichLangValue);
- }, [&](AskBoostWallpaper data) {
- isGroup = data.group;
- return (data.group
- ? tr::lng_boost_group_needs_level_wallpaper
- : tr::lng_boost_channel_needs_level_wallpaper)(
- lt_count,
- rpl::single(float64(data.requiredLevel)),
- Ui::Text::RichLangValue);
- }, [&](AskBoostEmojiStatus data) {
- isGroup = data.group;
- return (data.group
- ? tr::lng_boost_group_needs_level_status
- : tr::lng_boost_channel_needs_level_status)(
- lt_count,
- rpl::single(float64(data.requiredLevel)),
- Ui::Text::RichLangValue);
- }, [&](AskBoostEmojiPack data) {
- isGroup = true;
- return tr::lng_boost_group_needs_level_emoji(
- lt_count,
- rpl::single(float64(data.requiredLevel)),
- Ui::Text::RichLangValue);
- }, [&](AskBoostCustomReactions data) {
- return tr::lng_boost_channel_needs_level_reactions(
- lt_count,
- rpl::single(float64(data.count)),
- lt_same_count,
- rpl::single(TextWithEntities{ QString::number(data.count) }),
- Ui::Text::RichLangValue);
- }, [&](AskBoostCpm data) {
- return tr::lng_boost_channel_needs_level_cpm(
- lt_count,
- rpl::single(float64(data.requiredLevel)),
- Ui::Text::RichLangValue);
- }, [&](AskBoostWearCollectible data) {
- return tr::lng_boost_channel_needs_level_wear(
- lt_count,
- rpl::single(float64(data.requiredLevel)),
- Ui::Text::RichLangValue);
- });
- auto text = rpl::combine(
- std::move(reasonText),
- (isGroup ? tr::lng_boost_group_ask : tr::lng_boost_channel_ask)(
- Ui::Text::RichLangValue)
- ) | rpl::map([](TextWithEntities &&text, TextWithEntities &&ask) {
- return text.append(u"\n\n"_q).append(std::move(ask));
- });
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box,
- std::move(title),
- st::boostCenteredTitle),
- st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0));
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box,
- std::move(text),
- st::boostText),
- (st::boxRowPadding
- + QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
- auto stats = object_ptr<Ui::IconButton>(box, st::boostLinkStatsButton);
- stats->setClickedCallback(openStatistics);
- box->addRow(MakeLinkLabel(
- box,
- rpl::single(data.link),
- rpl::single(data.link),
- box->uiShow(),
- std::move(stats)));
- auto submit = tr::lng_boost_channel_ask_button();
- const auto button = box->addButton(rpl::duplicate(submit), [=] {
- QGuiApplication::clipboard()->setText(data.link);
- box->uiShow()->showToast(tr::lng_username_copied(tr::now));
- });
- rpl::combine(
- std::move(submit),
- box->widthValue()
- ) | rpl::start_with_next([=](const QString &, int width) {
- const auto &padding = st::boostBox.buttonPadding;
- button->resizeToWidth(width
- - padding.left()
- - padding.right());
- button->moveToLeft(padding.left(), button->y());
- }, button->lifetime());
- }
- void FillBoostLimit(
- rpl::producer<> showFinished,
- not_null<VerticalLayout*> container,
- rpl::producer<BoostCounters> data,
- style::margins limitLinePadding) {
- const auto addSkip = [&](int skip) {
- container->add(object_ptr<Ui::FixedHeightWidget>(container, skip));
- };
- const auto ratio = [=](BoostCounters counters) {
- const auto min = counters.thisLevelBoosts;
- const auto max = counters.nextLevelBoosts;
- Assert(counters.boosts >= min && counters.boosts <= max);
- const auto count = (max - min);
- const auto index = (counters.boosts - min);
- if (!index) {
- return 0.;
- } else if (index == count) {
- return 1.;
- } else if (count == 2) {
- return 0.5;
- }
- const auto available = st::boxWideWidth
- - st::boxPadding.left()
- - st::boxPadding.right();
- const auto average = available / float64(count);
- const auto levelWidth = [&](int add) {
- return st::normalFont->width(
- tr::lng_boost_level(
- tr::now,
- lt_count,
- counters.level + add));
- };
- const auto paddings = 2 * st::premiumLineTextSkip;
- const auto labelLeftWidth = paddings + levelWidth(0);
- const auto labelRightWidth = paddings + levelWidth(1);
- const auto first = std::max(average, labelLeftWidth * 1.);
- const auto last = std::max(average, labelRightWidth * 1.);
- const auto other = (available - first - last) / (count - 2);
- return (first + (index - 1) * other) / available;
- };
- auto adjustedData = rpl::duplicate(data) | rpl::map(AdjustByReached);
- auto bubbleRowState = rpl::duplicate(
- adjustedData
- ) | rpl::combine_previous(
- BoostCounters()
- ) | rpl::map([=](BoostCounters previous, BoostCounters counters) {
- return Premium::BubbleRowState{
- .counter = counters.boosts,
- .ratio = ratio(counters),
- .animateFromZero = (counters.level != previous.level),
- .dynamic = true,
- };
- });
- Premium::AddBubbleRow(
- container,
- st::boostBubble,
- std::move(showFinished),
- rpl::duplicate(bubbleRowState),
- Premium::BubbleType::Premium,
- nullptr,
- &st::premiumIconBoost,
- limitLinePadding);
- addSkip(st::premiumLineTextSkip);
- const auto level = [](int level) {
- return tr::lng_boost_level(tr::now, lt_count, level);
- };
- auto limitState = std::move(
- bubbleRowState
- ) | rpl::map([](const Premium::BubbleRowState &state) {
- return Premium::LimitRowState{
- .ratio = state.ratio,
- .animateFromZero = state.animateFromZero,
- .dynamic = state.dynamic
- };
- });
- auto left = rpl::duplicate(
- adjustedData
- ) | rpl::map([=](BoostCounters counters) {
- return level(counters.level);
- });
- auto right = rpl::duplicate(
- adjustedData
- ) | rpl::map([=](BoostCounters counters) {
- return level(counters.level + 1);
- });
- Premium::AddLimitRow(
- container,
- st::boostLimits,
- Premium::LimitRowLabels{
- .leftLabel = std::move(left),
- .rightLabel = std::move(right),
- },
- std::move(limitState),
- limitLinePadding);
- }
- object_ptr<Ui::FlatLabel> MakeBoostFeaturesBadge(
- not_null<QWidget*> parent,
- rpl::producer<QString> text,
- Fn<QBrush(QRect)> bg) {
- auto result = object_ptr<Ui::FlatLabel>(
- parent,
- std::move(text),
- st::boostLevelBadge);
- const auto label = result.data();
- label->show();
- label->paintRequest() | rpl::start_with_next([=] {
- const auto size = label->textMaxWidth();
- const auto rect = QRect(
- (label->width() - size) / 2,
- st::boostLevelBadge.margin.top(),
- size,
- st::boostLevelBadge.style.font->height
- ).marginsAdded(st::boostLevelBadge.margin);
- auto p = QPainter(label);
- auto hq = PainterHighQualityEnabler(p);
- p.setBrush(bg(rect));
- p.setPen(Qt::NoPen);
- p.drawRoundedRect(rect, rect.height() / 2., rect.height() / 2.);
- const auto &lineFg = st::windowBgRipple;
- const auto line = st::boostLevelBadgeLine;
- const auto top = st::boostLevelBadge.margin.top()
- + ((st::boostLevelBadge.style.font->height - line) / 2);
- const auto left = 0;
- const auto skip = st::boostLevelBadgeSkip;
- if (const auto right = rect.x() - skip; right > left) {
- p.fillRect(left, top, right - left, line, lineFg);
- }
- const auto right = label->width();
- if (const auto left = rect.x() + rect.width() + skip
- ; left < right) {
- p.fillRect(left, top, right - left, line, lineFg);
- }
- }, label->lifetime());
- return result;
- }
- } // namespace Ui
|