Start multiboosts, support dynamic state.

This commit is contained in:
John Preston 2023-11-07 17:58:20 +04:00
parent 2d67557a91
commit a41bbd27c8
11 changed files with 300 additions and 191 deletions

View file

@ -2030,6 +2030,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_gift_terms_link" = "here";
"lng_boost_channel_button" = "Boost Channel";
"lng_boost_again_button" = "Boost Again";
"lng_boost_level#one" = "Level {count}";
"lng_boost_level#other" = "Level {count}";
"lng_boost_channel_title_first" = "Enable stories for channel";
@ -2060,6 +2061,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_boost_error_flood_text" = "You can change the channel you boost only once a day. Next time you can boost is in {left}.";
"lng_boost_now_instead" = "You currently boost {channel}. Do you want to boost {other} instead?";
"lng_boost_now_replace" = "Replace";
"lng_boost_reassign_title" = "Reassign boost";
"lng_boost_reassign_text" = "To boost {channel}, reassign a previous boost from another channel.";
"lng_boost_remove_title" = "Remove your boost from";
"lng_boost_reassign_button" = "Reassign";
"lng_boost_available_in" = "available in {duration}";
"lng_boost_available_in_toast#one" = "Wait until the boost is available or get **{count}** more boost by gifting a **Telegram Premium** subscription.";
"lng_boost_available_in_toast#other" = "Wait until the boost is available or get **{count}** more boosts by gifting a **Telegram Premium** subscription.";
"lng_boost_channel_title_color" = "Enable colors";
"lng_boost_channel_needs_level_color#one" = "Your channel needs to reach **Level {count}** to change channel color.";

View file

@ -519,7 +519,9 @@ rpl::producer<rpl::no_value, QString> Boosts::request() {
: 0;
_boostStatus.overview = Data::BoostsOverview{
.isBoosted = data.is_my_boost(),
.mine = (data.vmy_boost_slots()
? data.vmy_boost_slots()->v.size()
: 0),
.level = std::max(data.vlevel().v, 0),
.boostCount = std::max(
data.vboosts().v,

View file

@ -514,21 +514,17 @@ void Apply(
close();
return;
}
const auto next = data.vnext_level_boosts().value_or_empty();
const auto openStatistics = [=] {
if (const auto controller = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo)) {
controller->showSection(Info::Boosts::Make(peer));
}
};
auto counters = Window::ParseBoostCounters(result);
counters.mine = 0; // Don't show current level as just-reached.
show->show(Box(Ui::AskBoostBox, Ui::AskBoostBoxData{
.link = qs(data.vboost_url()),
.boost = {
.level = data.vlevel().v,
.boosts = data.vboosts().v,
.thisLevelBoosts = data.vcurrent_level_boosts().v,
.nextLevelBoosts = next,
},
.boost = counters,
.requiredLevel = required,
}, openStatistics, nullptr));
cancel();

View file

@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
struct BoostsOverview final {
bool isBoosted = false;
int mine = 0;
int level = 0;
int boostCount = 0;
int currentLevelBoostCount = 0;

View file

@ -302,17 +302,16 @@ void InnerWidget::fill() {
auto dividerContent = object_ptr<Ui::VerticalLayout>(inner);
Ui::FillBoostLimit(
fakeShowed->events(),
rpl::single(status.overview.isBoosted),
dividerContent.data(),
Ui::BoostCounters{
rpl::single(Ui::BoostCounters{
.level = status.overview.level,
.boosts = status.overview.boostCount,
.thisLevelBoosts
= status.overview.currentLevelBoostCount,
.nextLevelBoosts
= status.overview.nextLevelBoostCount,
.mine = status.overview.isBoosted,
},
.mine = status.overview.mine,
}),
st::statisticsLimitsLinePadding);
inner->add(object_ptr<Ui::DividerLabel>(
inner,

View file

@ -20,6 +20,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QGuiApplication>
namespace Ui {
namespace {
[[nodiscrd]] BoostCounters AdjustByReached(BoostCounters data) {
const auto exact = (data.boosts == data.thisLevelBoosts);
const auto reached = !data.nextLevelBoosts || (exact && data.mine > 0);
if (reached) {
if (data.nextLevelBoosts) {
--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;
}
} // namespace
void StartFireworks(not_null<QWidget*> parent) {
const auto result = Ui::CreateChild<RpWidget>(parent.get());
@ -42,62 +67,65 @@ void StartFireworks(not_null<QWidget*> parent) {
void BoostBox(
not_null<GenericBox*> box,
BoostBoxData data,
Fn<void(Fn<void(bool)>)> boost) {
Fn<void(Fn<void(BoostCounters)>)> boost) {
box->setWidth(st::boxWideWidth);
box->setStyle(st::boostBox);
const auto full = !data.boost.nextLevelBoosts;
//AssertIsDebug();
//data.boost = {
// .level = 2,
// .boosts = 3,
// .thisLevelBoosts = 2,
// .nextLevelBoosts = 5,
// .mine = 2,
//};
struct State {
rpl::variable<bool> you = false;
rpl::variable<BoostCounters> data;
rpl::variable<bool> full;
bool submitted = false;
};
const auto state = box->lifetime().make_state<State>(State{
.you = data.boost.mine,
});
const auto state = box->lifetime().make_state<State>();
state->data = std::move(data.boost);
FillBoostLimit(
BoxShowFinishes(box),
state->you.value(),
box->verticalLayout(),
data.boost,
state->data.value(),
st::boxRowPadding);
{
const auto &d = data.boost;
if (!d.nextLevelBoosts
|| ((d.thisLevelBoosts == d.boosts) && d.mine)) {
--data.boost.level;
}
}
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
const auto name = data.name;
auto title = state->you.value() | rpl::map([=](bool your) {
return your
auto title = state->data.value(
) | rpl::map([=](BoostCounters counters) {
return (counters.mine > 0)
? tr::lng_boost_channel_you_title(
lt_channel,
rpl::single(data.name))
: full
rpl::single(name))
: !counters.nextLevelBoosts
? tr::lng_boost_channel_title_max()
: !data.boost.level
: !counters.level
? tr::lng_boost_channel_title_first()
: tr::lng_boost_channel_title_more();
}) | rpl::flatten_latest();
auto text = state->you.value() | rpl::map([=](bool your) {
const auto bold = Ui::Text::Bold(data.name);
const auto now = data.boost.boosts + (your ? 1 : 0);
const auto left = (data.boost.nextLevelBoosts > now)
? (data.boost.nextLevelBoosts - now)
auto text = state->data.value(
) | rpl::map([=](BoostCounters counters) {
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(data.boost.level + 1)),
rpl::single(float64(counters.level + (left ? 1 : 0))),
Ui::Text::RichLangValue);
return (your || full)
? ((!full && left > 0)
? (!data.boost.level
return (counters.mine || full)
? (left
? (!counters.level
? tr::lng_boost_channel_you_first(
lt_count,
rpl::single(float64(left)),
@ -108,16 +136,16 @@ void BoostBox(
lt_post,
std::move(post),
Ui::Text::RichLangValue))
: (!data.boost.level
: (!counters.level
? tr::lng_boost_channel_reached_first(
Ui::Text::RichLangValue)
: tr::lng_boost_channel_reached_more(
lt_count,
rpl::single(float64(data.boost.level + 1)),
rpl::single(float64(counters.level)),
lt_post,
std::move(post),
Ui::Text::RichLangValue)))
: !data.boost.level
: !counters.level
? tr::lng_boost_channel_needs_first(
lt_count,
rpl::single(float64(left)),
@ -133,12 +161,14 @@ void BoostBox(
std::move(post),
Ui::Text::RichLangValue);
}) | rpl::flatten_latest();
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
std::move(title),
st::boostTitle),
st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0));
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
@ -147,28 +177,83 @@ void BoostBox(
(st::boxRowPadding
+ QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
auto submit = full
? (tr::lng_box_ok() | rpl::type_erased())
: state->you.value(
) | rpl::map([](bool mine) {
return mine ? tr::lng_box_ok() : tr::lng_boost_channel_button();
}) | rpl::flatten_latest();
auto submit = state->data.value(
) | rpl::map([=](BoostCounters counters) {
return !counters.nextLevelBoosts
? tr::lng_box_ok()
: (counters.mine > 0)
? tr::lng_boost_again_button()
: tr::lng_boost_channel_button();
}) | rpl::flatten_latest();
const auto button = box->addButton(rpl::duplicate(submit), [=] {
if (state->submitted) {
return;
} else if (!full && !state->you.current()) {
} else if (state->data.current().nextLevelBoosts > 0) {
state->submitted = true;
boost(crl::guard(box, [=](bool success) {
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 (success) {
StartFireworks(box->parentWidget());
state->you = true;
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()
@ -270,18 +355,14 @@ void AskBoostBox(
box->setStyle(st::boostBox);
struct State {
rpl::variable<bool> you = false;
bool submitted = false;
};
const auto state = box->lifetime().make_state<State>(State{
.you = data.boost.mine,
});
const auto state = box->lifetime().make_state<State>();
FillBoostLimit(
BoxShowFinishes(box),
state->you.value(),
box->verticalLayout(),
data.boost,
rpl::single(data.boost),
st::boxRowPadding);
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
@ -338,56 +419,22 @@ void AskBoostBox(
void FillBoostLimit(
rpl::producer<> showFinished,
rpl::producer<bool> you,
not_null<VerticalLayout*> container,
BoostCounters data,
rpl::producer<BoostCounters> data,
style::margins limitLinePadding) {
const auto full = !data.nextLevelBoosts;
if (data.mine && data.boosts > 0) {
--data.boosts;
}
if (full) {
data.nextLevelBoosts = data.boosts
+ (data.mine ? 1 : 0);
data.thisLevelBoosts = 0;
if (data.level > 0) {
--data.level;
}
} else if (data.mine
&& data.level > 0
&& data.boosts < data.thisLevelBoosts) {
--data.level;
data.nextLevelBoosts = data.thisLevelBoosts;
data.thisLevelBoosts = 0;
}
const auto addSkip = [&](int skip) {
container->add(object_ptr<Ui::FixedHeightWidget>(container, skip));
};
addSkip(st::boostSkipTop);
const auto levelWidth = [&](int add) {
return st::normalFont->width(
tr::lng_boost_level(tr::now, lt_count, data.level + add));
};
const auto paddings = 2 * st::premiumLineTextSkip;
const auto labelLeftWidth = paddings + levelWidth(0);
const auto labelRightWidth = paddings + levelWidth(1);
const auto ratio = [=](int boosts) {
const auto min = std::min(
data.boosts,
data.thisLevelBoosts);
const auto max = std::max({
data.boosts,
data.nextLevelBoosts,
1,
});
Assert(boosts >= min && boosts <= max);
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 = (boosts - min);
const auto index = (counters.boosts - min);
if (!index) {
return 0.;
} else if (index == count) {
@ -399,26 +446,33 @@ void FillBoostLimit(
- 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;
};
const auto min = std::min(data.boosts, data.thisLevelBoosts);
const auto now = data.boosts;
const auto max = (data.nextLevelBoosts > min)
? (data.nextLevelBoosts)
: (data.boosts > 0)
? data.boosts
: 1;
auto bubbleRowState = (
std::move(you)
) | rpl::map([=](bool mine) {
const auto index = mine ? (now + 1) : now;
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 = index,
.ratio = ratio(index),
.counter = counters.boosts,
.ratio = ratio(counters),
.animateFromZero = (counters.level != previous.level),
.dynamic = true,
};
});
@ -427,7 +481,6 @@ void FillBoostLimit(
st::boostBubble,
std::move(showFinished),
rpl::duplicate(bubbleRowState),
max,
true,
nullptr,
&st::premiumIconBoost,
@ -437,20 +490,33 @@ void FillBoostLimit(
const auto level = [](int level) {
return tr::lng_boost_level(tr::now, lt_count, level);
};
auto ratioValue = std::move(
auto limitState = std::move(
bubbleRowState
) | rpl::map([](const Premium::BubbleRowState &state) {
return state.ratio;
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 = level(data.level),
.rightLabel = level(data.level + 1),
.dynamic = true,
.leftLabel = std::move(left),
.rightLabel = std::move(right),
},
std::move(ratioValue),
std::move(limitState),
limitLinePadding);
}

View file

@ -23,7 +23,11 @@ struct BoostCounters {
int boosts = 0;
int thisLevelBoosts = 0;
int nextLevelBoosts = 0; // Zero means no next level is available.
bool mine = false;
int mine = 0;
friend inline constexpr bool operator==(
BoostCounters,
BoostCounters) = default;
};
struct BoostBoxData {
@ -34,7 +38,7 @@ struct BoostBoxData {
void BoostBox(
not_null<GenericBox*> box,
BoostBoxData data,
Fn<void(Fn<void(bool)>)> boost);
Fn<void(Fn<void(BoostCounters)>)> boost);
struct AskBoostBoxData {
QString link;
@ -57,9 +61,8 @@ void AskBoostBox(
void FillBoostLimit(
rpl::producer<> showFinished,
rpl::producer<bool> you,
not_null<VerticalLayout*> container,
BoostCounters data,
rpl::producer<BoostCounters> data,
style::margins limitLinePadding);
} // namespace Ui

View file

@ -195,7 +195,7 @@ public:
[[nodiscard]] int height() const;
[[nodiscard]] int width() const;
[[nodiscard]] int bubbleRadius() const;
[[nodiscard]] int countMaxWidth(int maxCounter) const;
[[nodiscard]] int countMaxWidth(int maxPossibleCounter) const;
void setCounter(int value);
void setTailEdge(EdgeProgress edge);
@ -271,12 +271,12 @@ int Bubble::width() const {
return filledWidth() + _numberAnimation.countWidth();
}
int Bubble::countMaxWidth(int maxCounter) const {
int Bubble::countMaxWidth(int maxPossibleCounter) const {
auto numbers = Ui::NumbersAnimation(_st.font, [] {});
numbers.setDisabledMonospace(true);
numbers.setDuration(0);
numbers.setText(_textFactory(0), 0);
numbers.setText(_textFactory(maxCounter), maxCounter);
numbers.setText(_textFactory(maxPossibleCounter), maxPossibleCounter);
numbers.finishAnimating();
return filledWidth() + numbers.maxWidth();
}
@ -389,7 +389,6 @@ public:
const style::PremiumBubble &st,
TextFactory textFactory,
rpl::producer<BubbleRowState> state,
int maxCounter,
bool premiumPossible,
rpl::producer<> showFinishes,
const style::icon *icon,
@ -414,9 +413,8 @@ private:
BubbleRowState _animatingFrom;
float64 _animatingFromResultRatio = 0.;
rpl::variable<BubbleRowState> _state;
const int _maxCounter;
Bubble _bubble;
const int _maxBubbleWidth;
int _maxBubbleWidth = 0;
const bool _premiumPossible;
const style::margins _outerPadding;
@ -439,7 +437,6 @@ BubbleWidget::BubbleWidget(
const style::PremiumBubble &st,
TextFactory textFactory,
rpl::producer<BubbleRowState> state,
int maxCounter,
bool premiumPossible,
rpl::producer<> showFinishes,
const style::icon *icon,
@ -447,14 +444,12 @@ BubbleWidget::BubbleWidget(
: RpWidget(parent)
, _st(st)
, _state(std::move(state))
, _maxCounter(maxCounter)
, _bubble(
_st,
[=] { update(); },
std::move(textFactory),
icon,
premiumPossible)
, _maxBubbleWidth(_bubble.countMaxWidth(_maxCounter))
, _premiumPossible(premiumPossible)
, _outerPadding(outerPadding)
, _deflection(kDeflection)
@ -485,6 +480,7 @@ BubbleWidget::BubbleWidget(
}
void BubbleWidget::animateTo(BubbleRowState state) {
_maxBubbleWidth = _bubble.countMaxWidth(state.counter);
const auto parent = parentWidget();
const auto computeLeft = [=](float64 pointRatio, float64 animProgress) {
const auto halfWidth = (_maxBubbleWidth / 2);
@ -541,6 +537,11 @@ void BubbleWidget::animateTo(BubbleRowState state) {
const auto duration = kSlideDuration
* (_ignoreDeflection ? kStepBeforeDeflection : 1.)
* ((_state.current().ratio < 0.001) ? 0.5 : 1.);
if (state.animateFromZero) {
_animatingFrom.ratio = 0.;
_animatingFrom.counter = 0;
_animatingFromResultRatio = 0.;
}
_appearanceAnimation.start([=](float64 value) {
if (!_appearanceAnimation.animating()) {
_animatingFrom = state;
@ -658,7 +659,7 @@ public:
not_null<Ui::RpWidget*> parent,
const style::PremiumLimits &st,
LimitRowLabels labels,
rpl::producer<float64> ratio);
rpl::producer<LimitRowState> state);
void setColorOverride(QBrush brush);
@ -675,6 +676,7 @@ private:
float64 _ratio = 0.;
Ui::Animations::Simple _animation;
rpl::event_stream<> _recaches;
Ui::Text::String _leftLabel;
Ui::Text::String _leftText;
Ui::Text::String _rightLabel;
@ -707,44 +709,56 @@ Line::Line(
QString min,
float64 ratio)
: Line(parent, st, LimitRowLabels{
.leftLabel = tr::lng_premium_free(tr::now),
.leftCount = min,
.rightLabel = tr::lng_premium(tr::now),
.rightCount = max,
}, rpl::single(ratio)) {
.leftLabel = tr::lng_premium_free(),
.leftCount = rpl::single(min),
.rightLabel = tr::lng_premium(),
.rightCount = rpl::single(max),
}, rpl::single(LimitRowState{ ratio })) {
}
Line::Line(
not_null<Ui::RpWidget*> parent,
const style::PremiumLimits &st,
LimitRowLabels labels,
rpl::producer<float64> ratio)
rpl::producer<LimitRowState> state)
: Ui::RpWidget(parent)
, _st(st)
, _leftLabel(st::semiboldTextStyle, labels.leftLabel)
, _leftText(st::semiboldTextStyle, labels.leftCount)
, _rightLabel(st::semiboldTextStyle, labels.rightLabel)
, _rightText(st::semiboldTextStyle, labels.rightCount)
, _dynamic(labels.dynamic) {
, _st(st) {
resize(width(), st::requestsAcceptButton.height);
std::move(ratio) | rpl::start_with_next([=](float64 ratio) {
const auto set = [&](
Ui::Text::String &label,
rpl::producer<QString> &text) {
std::move(text) | rpl::start_with_next([=, &label](QString text) {
label = { st::semiboldTextStyle, text };
_recaches.fire({});
}, lifetime());
};
set(_leftLabel, labels.leftLabel);
set(_leftText, labels.leftCount);
set(_rightLabel, labels.rightLabel);
set(_rightText, labels.rightCount);
std::move(state) | rpl::start_with_next([=](LimitRowState state) {
_dynamic = state.dynamic;
if (width() > 0) {
const auto from = _animation.value(_ratio);
const auto from = state.animateFromZero
? 0.
: _animation.value(_ratio);
const auto duration = kSlideDuration * kStepBeforeDeflection;
_animation.start([=] {
update();
}, from, ratio, duration, anim::easeOutCirc);
}, from, state.ratio, duration, anim::easeOutCirc);
}
_ratio = ratio;
_ratio = state.ratio;
}, lifetime());
rpl::combine(
sizeValue(),
parent->widthValue()
) | rpl::filter([](const QSize &size, int parentWidth) {
parent->widthValue(),
_recaches.events_starting_with({})
) | rpl::filter([](const QSize &size, int parentWidth, auto) {
return !size.isEmpty() && parentWidth;
}) | rpl::start_with_next([=](const QSize &size, int) {
}) | rpl::start_with_next([=](const QSize &size, auto, auto) {
recache(size);
update();
}, lifetime());
@ -906,7 +920,6 @@ void AddBubbleRow(
.counter = current,
.ratio = (current - min) / float64(max - min),
}),
max,
premiumPossible,
ProcessTextFactory(phrase),
icon,
@ -918,7 +931,6 @@ void AddBubbleRow(
const style::PremiumBubble &st,
rpl::producer<> showFinishes,
rpl::producer<BubbleRowState> state,
int max,
bool premiumPossible,
Fn<QString(int)> text,
const style::icon *icon,
@ -930,7 +942,6 @@ void AddBubbleRow(
st,
text ? std::move(text) : ProcessTextFactory(std::nullopt),
std::move(state),
max,
premiumPossible,
std::move(showFinishes),
icon,
@ -975,10 +986,10 @@ void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
const style::PremiumLimits &st,
LimitRowLabels labels,
rpl::producer<float64> ratio,
rpl::producer<LimitRowState> state,
const style::margins &padding) {
parent->add(
object_ptr<Line>(parent, st, std::move(labels), std::move(ratio)),
object_ptr<Line>(parent, st, std::move(labels), std::move(state)),
padding);
}

View file

@ -55,6 +55,7 @@ void AddBubbleRow(
struct BubbleRowState {
int counter = 0;
float64 ratio = 0.;
bool animateFromZero = false;
bool dynamic = false;
};
void AddBubbleRow(
@ -62,7 +63,6 @@ void AddBubbleRow(
const style::PremiumBubble &st,
rpl::producer<> showFinishes,
rpl::producer<BubbleRowState> state,
int max,
bool premiumPossible,
Fn<QString(int)> text,
const style::icon *icon,
@ -84,17 +84,23 @@ void AddLimitRow(
float64 ratio = kLimitRowRatio);
struct LimitRowLabels {
QString leftLabel;
QString leftCount;
QString rightLabel;
QString rightCount;
rpl::producer<QString> leftLabel;
rpl::producer<QString> leftCount;
rpl::producer<QString> rightLabel;
rpl::producer<QString> rightCount;
};
struct LimitRowState {
float64 ratio = 0.;
bool animateFromZero = false;
bool dynamic = false;
};
void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
const style::PremiumLimits &st,
LimitRowLabels labels,
rpl::producer<float64> ratio,
rpl::producer<LimitRowState> state,
const style::margins &padding);
struct AccountsRowArgs final {

View file

@ -267,6 +267,19 @@ Fn<bool()> PausedIn(
return [=] { return IsPaused(controller, level); };
}
Ui::BoostCounters ParseBoostCounters(
const MTPpremium_BoostsStatus &status) {
const auto &data = status.data();
const auto slots = data.vmy_boost_slots();
return {
.level = data.vlevel().v,
.boosts = data.vboosts().v,
.thisLevelBoosts = data.vcurrent_level_boosts().v,
.nextLevelBoosts = data.vnext_level_boosts().value_or_empty(),
.mine = slots ? slots->v.size() : 0,
};
}
bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b) {
return (a.peer == b.peer) && (a.theme == b.theme);
}
@ -629,20 +642,12 @@ void SessionNavigation::resolveBoostState(not_null<ChannelData*> channel) {
channel->input
)).done([=](const MTPpremium_BoostsStatus &result) {
_boostStateResolving = nullptr;
const auto &data = result.data();
const auto submit = [=](Fn<void(bool)> done) {
const auto submit = [=](Fn<void(Ui::BoostCounters)> done) {
applyBoost(channel, done);
};
const auto next = data.vnext_level_boosts().value_or_empty();
uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{
.name = channel->name(),
.boost = {
.level = data.vlevel().v,
.boosts = data.vboosts().v,
.thisLevelBoosts = data.vcurrent_level_boosts().v,
.nextLevelBoosts = next,
.mine = data.is_my_boost(),
},
.boost = ParseBoostCounters(result),
}, submit));
}).fail([=](const MTP::Error &error) {
_boostStateResolving = nullptr;
@ -652,7 +657,7 @@ void SessionNavigation::resolveBoostState(not_null<ChannelData*> channel) {
void SessionNavigation::applyBoost(
not_null<ChannelData*> channel,
Fn<void(bool)> done) {
Fn<void(Ui::BoostCounters)> done) {
_api.request(MTPpremium_GetMyBoosts(
)).done([=](const MTPpremium_MyBoosts &result) {
const auto &data = result.data();
@ -682,7 +687,7 @@ void SessionNavigation::applyBoost(
.inform = true,
}));
}
done(false);
done({});
return;
}
auto slot = int();
@ -725,7 +730,7 @@ void SessionNavigation::applyBoost(
.title = tr::lng_boost_error_flood_title(),
.inform = true,
}));
done(false);
done({});
} else {
const auto peer = _session->data().peer(different);
replaceBoostConfirm(peer, channel, slot, done);
@ -737,12 +742,12 @@ void SessionNavigation::applyBoost(
.title = tr::lng_boost_error_already_title(),
.inform = true,
}));
done(false);
done({});
}
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
showToast(u"Error: "_q + type);
done(false);
done({});
}).handleFloodErrors().send();
}
@ -750,7 +755,7 @@ void SessionNavigation::replaceBoostConfirm(
not_null<PeerData*> from,
not_null<ChannelData*> channel,
int slot,
Fn<void(bool)> done) {
Fn<void(Ui::BoostCounters)> done) {
const auto forwarded = std::make_shared<bool>(false);
const auto confirmed = [=](Fn<void()> close) {
*forwarded = true;
@ -777,23 +782,30 @@ void SessionNavigation::replaceBoostConfirm(
box->boxClosing() | rpl::filter([=] {
return !*forwarded;
}) | rpl::start_with_next([=] {
done(false);
done({});
}, box->lifetime());
}
void SessionNavigation::applyBoostChecked(
not_null<ChannelData*> channel,
int slot,
Fn<void(bool)> done) {
Fn<void(Ui::BoostCounters)> done) {
_api.request(MTPpremium_ApplyBoost(
MTP_flags(MTPpremium_ApplyBoost::Flag::f_slots),
MTP_vector<MTPint>({ MTP_int(slot) }),
channel->input
)).done([=](const MTPpremium_MyBoosts &result) {
done(true);
_api.request(MTPpremium_GetBoostsStatus(
channel->input
)).done([=](const MTPpremium_BoostsStatus &result) {
done(ParseBoostCounters(result));
}).fail([=](const MTP::Error &error) {
showToast(u"Error: "_q + error.type());
done({});
}).send();
}).fail([=](const MTP::Error &error) {
showToast(u"Error: "_q + error.type());
done(false);
done({});
}).send();
}

View file

@ -68,6 +68,7 @@ struct ChatPaintContext;
struct ChatThemeBackground;
struct ChatThemeBackgroundData;
class MessageSendingAnimationController;
struct BoostCounters;
} // namespace Ui
namespace Data {
@ -314,16 +315,18 @@ private:
const PeerByLinkInfo &info);
void resolveBoostState(not_null<ChannelData*> channel);
void applyBoost(not_null<ChannelData*> channel, Fn<void(bool)> done);
void applyBoost(
not_null<ChannelData*> channel,
Fn<void(Ui::BoostCounters)> done);
void replaceBoostConfirm(
not_null<PeerData*> from,
not_null<ChannelData*> channel,
int slot,
Fn<void(bool)> done);
Fn<void(Ui::BoostCounters)> done);
void applyBoostChecked(
not_null<ChannelData*> channel,
int slot,
Fn<void(bool)> done);
Fn<void(Ui::BoostCounters)> done);
const not_null<Main::Session*> _session;
@ -752,4 +755,7 @@ void ActivateWindow(not_null<SessionController*> controller);
not_null<SessionController*> controller,
GifPauseReason level);
[[nodiscard]] Ui::BoostCounters ParseBoostCounters(
const MTPpremium_BoostsStatus &status);
} // namespace Window