tdesktop/Telegram/SourceFiles/ui/boxes/boost_box.cpp

279 lines
7.4 KiB
C++

/*
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 "lang/lang_keys.h"
#include "ui/effects/fireworks_animation.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 "styles/style_layers.h"
#include "styles/style_premium.h"
namespace Ui {
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);
}
} // namespace
void BoostBox(
not_null<GenericBox*> box,
BoostBoxData data,
Fn<void(Fn<void(bool)>)> boost) {
box->setWidth(st::boxWideWidth);
box->setStyle(st::boostBox);
const auto full = !data.boost.nextLevelBoosts;
if (data.boost.mine && data.boost.boosts > 0) {
--data.boost.boosts;
}
if (full) {
data.boost.nextLevelBoosts = data.boost.boosts
+ (data.boost.mine ? 1 : 0);
data.boost.thisLevelBoosts = 0;
if (data.boost.level > 0) {
--data.boost.level;
}
} else if (data.boost.mine
&& data.boost.level > 0
&& data.boost.boosts < data.boost.thisLevelBoosts) {
--data.boost.level;
data.boost.nextLevelBoosts = data.boost.thisLevelBoosts;
data.boost.thisLevelBoosts = 0;
}
struct State {
rpl::variable<bool> you = false;
bool submitted = false;
};
const auto state = box->lifetime().make_state<State>(State{
.you = data.boost.mine,
});
box->addTopButton(st::boxTitleClose, [=] {
box->closeBox();
});
const auto addSkip = [&](int skip) {
box->addRow(object_ptr<Ui::FixedHeightWidget>(box, skip));
};
addSkip(st::boostSkipTop);
const auto levelWidth = [&](int add) {
return st::normalFont->width(
tr::lng_boost_level(tr::now, lt_count, data.boost.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.boost.boosts,
data.boost.thisLevelBoosts);
const auto max = std::max({
data.boost.boosts,
data.boost.nextLevelBoosts,
1,
});
Assert(boosts >= min && boosts <= max);
const auto count = (max - min);
const auto index = (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 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.boost.boosts,
data.boost.thisLevelBoosts);
const auto now = data.boost.boosts;
const auto max = (data.boost.nextLevelBoosts > min)
? (data.boost.nextLevelBoosts)
: (data.boost.boosts > 0)
? data.boost.boosts
: 1;
auto bubbleRowState = state->you.value(
) | rpl::map([=](bool mine) {
const auto index = mine ? (now + 1) : now;
return Premium::BubbleRowState{
.counter = index,
.ratio = ratio(index),
.dynamic = true,
};
});
Premium::AddBubbleRow(
box->verticalLayout(),
st::boostBubble,
BoxShowFinishes(box),
rpl::duplicate(bubbleRowState),
max,
true,
nullptr,
&st::premiumIconBoost);
addSkip(st::premiumLineTextSkip);
const auto level = [](int level) {
return tr::lng_boost_level(tr::now, lt_count, level);
};
auto ratioValue = std::move(
bubbleRowState
) | rpl::map([](const Premium::BubbleRowState &state) {
return state.ratio;
});
Premium::AddLimitRow(
box->verticalLayout(),
st::boostLimits,
Premium::LimitRowLabels{
.leftLabel = level(data.boost.level),
.rightLabel = level(data.boost.level + 1),
.dynamic = true,
},
std::move(ratioValue));
const auto name = data.name;
auto title = state->you.value() | rpl::map([=](bool your) {
return your
? tr::lng_boost_channel_you_title(
lt_channel,
rpl::single(data.name))
: full
? tr::lng_boost_channel_title_max()
: !data.boost.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)
: 0;
auto post = tr::lng_boost_channel_post_stories(
lt_count,
rpl::single(float64(data.boost.level + 1)),
Ui::Text::RichLangValue);
return (your || full)
? ((!full && left > 0)
? (!data.boost.level
? tr::lng_boost_channel_you_first(
lt_count,
rpl::single(float64(left)),
Ui::Text::RichLangValue)
: tr::lng_boost_channel_you_more(
lt_count,
rpl::single(float64(left)),
lt_post,
std::move(post),
Ui::Text::RichLangValue))
: (!data.boost.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)),
lt_post,
std::move(post),
Ui::Text::RichLangValue)))
: !data.boost.level
? tr::lng_boost_channel_needs_first(
lt_count,
rpl::single(float64(left)),
lt_channel,
rpl::single(bold),
Ui::Text::RichLangValue)
: tr::lng_boost_channel_needs_more(
lt_count,
rpl::single(float64(left)),
lt_channel,
rpl::single(bold),
lt_post,
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,
std::move(text),
st::boostText),
(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();
const auto button = box->addButton(rpl::duplicate(submit), [=] {
if (state->submitted) {
return;
} else if (!full && !state->you.current()) {
state->submitted = true;
boost(crl::guard(box, [=](bool success) {
state->submitted = false;
if (success) {
StartFireworks(box->parentWidget());
state->you = true;
}
}));
} 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());
}
} // namespace Ui