Pause premium settings animations.

This commit is contained in:
John Preston 2022-06-14 13:42:43 +04:00
parent a34e6ca7db
commit 6d8012f13a
8 changed files with 218 additions and 97 deletions

View File

@ -1672,6 +1672,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium" = "Premium";
"lng_premium_free" = "Free";
"lng_premium_more_about" = "More About Telegram Premium";
"lng_premium_unlock_reactions" = "Unlock Premium Reactions";
"lng_premium_unlock_stickers" = "Unlock Premium Stickers";
"lng_premium_summary_title" = "Telegram Premium";
"lng_premium_summary_top_about" = "Go **beyond the limits**, get **exclusive features** and support us by subscribing to **Telegram Premium**.";
@ -1739,7 +1741,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_double_limits_about_folder_chats#other" = "Add up to {count} chats into each of your folders";
"lng_premium_double_limits_subtitle_accounts" = "Connected Accounts";
"lng_premium_double_limits_about_accounts#one" = "Connect {count} accounts with different mobile numbers";
"lng_premium_double_limits_about_accounts#one" = "Connect {count} account with different mobile numbers";
"lng_premium_double_limits_about_accounts#other" = "Connect {count} accounts with different mobile numbers";
//

View File

@ -61,12 +61,15 @@ struct Descriptor {
PremiumPreview section = PremiumPreview::Stickers;
DocumentData *requestedSticker = nullptr;
base::flat_map<QString, ReactionDisableType> disabled;
bool fromSettings = false;
Fn<void()> hiddenCallback;
};
bool operator==(const Descriptor &a, const Descriptor &b) {
return (a.section == b.section)
&& (a.requestedSticker == b.requestedSticker)
&& (a.disabled == b.disabled);
&& (a.disabled == b.disabled)
&& (a.fromSettings == b.fromSettings);
}
bool operator!=(const Descriptor &a, const Descriptor &b) {
@ -1146,27 +1149,27 @@ void ReactionPreview::paintEffect(QPainter &p) {
}
}
[[nodiscard]] object_ptr<Ui::AbstractButton> CreateGradientButton(
[[nodiscard]] object_ptr<Ui::GradientButton> CreateGradientButton(
QWidget *parent,
QGradientStops stops) {
return object_ptr<Ui::GradientButton>(parent, std::move(stops));
}
[[nodiscard]] object_ptr<Ui::AbstractButton> CreatePremiumButton(
[[nodiscard]] object_ptr<Ui::GradientButton> CreatePremiumButton(
QWidget *parent) {
return CreateGradientButton(parent, Ui::Premium::ButtonGradientStops());
}
[[nodiscard]] object_ptr<Ui::AbstractButton> CreateUnlockButton(
[[nodiscard]] object_ptr<Ui::GradientButton> CreateUnlockButton(
QWidget *parent,
int width) {
rpl::producer<QString> text) {
auto result = CreatePremiumButton(parent);
const auto &st = st::premiumPreviewBox.button;
result->resize(width, st.height);
result->resize(result->width(), st.height);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
result.data(),
tr::lng_premium_more_about(),
std::move(text),
st::premiumPreviewButtonLabel);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(
@ -1250,6 +1253,8 @@ void PreviewBox(
bool stickersPreloadReady = false;
Ui::RpWidget *reactionsPreload = nullptr;
bool reactionsPreloadReady = false;
bool preloadScheduled = false;
bool showFinished = false;
Ui::Animations::Simple animation;
Fn<void()> preload;
std::vector<Hiding> hiding;
@ -1259,6 +1264,10 @@ void PreviewBox(
state->selected = descriptor.section;
state->preload = [=] {
if (!state->showFinished) {
state->preloadScheduled = true;
return;
}
const auto now = state->selected.current();
if (now != PremiumPreview::Stickers && !state->stickersPreload) {
const auto ready = [=] {
@ -1294,12 +1303,17 @@ void PreviewBox(
switch (descriptor.section) {
case PremiumPreview::Stickers:
Assert(media != nullptr);
state->content = StickerPreview(
outer,
controller,
media,
state->preload);
state->content = media
? StickerPreview(
outer,
controller,
media,
state->preload)
: GenericPreview(
outer,
controller,
descriptor.section,
state->preload);
break;
case PremiumPreview::Reactions:
state->content = ReactionsPreview(
@ -1428,11 +1442,39 @@ void PreviewBox(
const auto width = size.width()
- buttonPadding.left()
- buttonPadding.right();
auto button = CreateUnlockButton(box, width);
const auto computeRef = [=] {
return Settings::LookupPremiumRef(state->selected.current());
};
auto unlock = state->selected.value(
) | rpl::map([=](PremiumPreview section) {
return (section == PremiumPreview::Reactions)
? tr::lng_premium_unlock_reactions()
: (section == PremiumPreview::Stickers)
? tr::lng_premium_unlock_stickers()
: tr::lng_premium_more_about();
}) | rpl::flatten_latest();
auto button = descriptor.fromSettings
? object_ptr<Ui::GradientButton>::fromRaw(
Settings::CreateSubscribeButton(controller, box, computeRef))
: CreateUnlockButton(box, std::move(unlock));
button->resizeToWidth(width);
button->setClickedCallback([=] {
Settings::ShowPremium(controller, "premium_stickers");
Settings::ShowPremium(
controller,
Settings::LookupPremiumRef(state->selected.current()));
});
box->setShowFinishedCallback([=, raw = button.data()] {
state->showFinished = true;
if (base::take(state->preloadScheduled)) {
state->preload();
}
raw->startGlareAnimation();
});
box->addButton(std::move(button));
if (const auto &hidden = descriptor.hiddenCallback) {
box->boxClosing() | rpl::start_with_next(hidden, box->lifetime());
}
}
void Show(
@ -1573,6 +1615,17 @@ void ShowPremiumPreviewBox(
});
}
void ShowPremiumPreviewToBuy(
not_null<Window::SessionController*> controller,
PremiumPreview section,
Fn<void()> hiddenCallback) {
Show(controller, Descriptor{
.section = section,
.fromSettings = true,
.hiddenCallback = std::move(hiddenCallback),
});
}
void PremiumUnavailableBox(not_null<Ui::GenericBox*> box) {
Ui::ConfirmBox(box, {
.text = tr::lng_premium_unavailable(
@ -1744,15 +1797,19 @@ void DoubledLimitsPreviewBox(
premium,
});
}
const auto now = session->domain().accounts().size();
const auto till = (now + 1 >= Main::Domain::kPremiumMaxAccounts)
? QString::number(Main::Domain::kPremiumMaxAccounts)
: (QString::number(now + 1) + QChar('+'));
entries.push_back(Ui::Premium::ListEntry{
tr::lng_premium_double_limits_subtitle_accounts(),
tr::lng_premium_double_limits_about_accounts(
lt_count,
rpl::single(float64(Main::Domain::kMaxAccounts)),
rpl::single(float64(Main::Domain::kPremiumMaxAccounts)),
Ui::Text::RichLangValue),
Main::Domain::kMaxAccounts,
Main::Domain::kPremiumMaxAccounts,
QString::number(Main::Domain::kMaxAccounts + 1) + QChar('+'),
till,
});
Ui::Premium::ShowListBox(box, std::move(entries));
}

View File

@ -53,4 +53,9 @@ void ShowPremiumPreviewBox(
PremiumPreview section,
const base::flat_map<QString, ReactionDisableType> &disabled = {});
void ShowPremiumPreviewToBuy(
not_null<Window::SessionController*> controller,
PremiumPreview section,
Fn<void()> hiddenCallback);
void PremiumUnavailableBox(not_null<Ui::GenericBox*> box);

View File

@ -178,49 +178,6 @@ using Order = std::vector<QString>;
};
}
[[nodiscard]] not_null<Ui::GradientButton*> CreateSubscribeButton(
not_null<Window::SessionController*> controller,
not_null<Ui::RpWidget*> parent,
Fn<void()> callback) {
const auto result = Ui::CreateChild<Ui::GradientButton>(
parent.get(),
Ui::Premium::ButtonGradientStops());
result->setClickedCallback(std::move(callback));
const auto &st = st::premiumPreviewBox.button;
result->resize(parent->width(), st.height);
const auto premium = &controller->session().api().premium();
const auto computeCost = [=] {
const auto amount = premium->monthlyAmount();
const auto currency = premium->monthlyCurrency();
const auto valid = (amount > 0) && !currency.isEmpty();
return Ui::FillAmountAndCurrency(
valid ? amount : 500,
valid ? currency : "USD");
};
const auto label = Ui::CreateChild<Ui::FlatLabel>(
result,
tr::lng_premium_summary_button(
lt_cost,
premium->statusTextValue() | rpl::map(computeCost)),
st::premiumPreviewButtonLabel);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(
result->widthValue(),
label->widthValue()
) | rpl::start_with_next([=](int outer, int width) {
label->moveToLeft(
(outer - width) / 2,
st::premiumPreviewBox.button.textTop,
outer);
}, label->lifetime());
return result;
}
void SendAppLog(
not_null<Main::Session*> session,
const QString &type,
@ -278,6 +235,7 @@ public:
MiniStars(Fn<void(const QRect &r)> updateCallback);
void paint(Painter &p, const QRectF &rect);
void setPaused(bool paused);
private:
struct MiniStar {
@ -317,6 +275,7 @@ private:
std::vector<MiniStar> _ministars;
crl::time _nextBirthTime = 0;
bool _paused = false;
QRect _rectToUpdate;
@ -339,7 +298,7 @@ MiniStars::MiniStars(Fn<void(const QRect &r)> updateCallback)
, _distanceProgressStart(0.5)
, _sprite(u":/gui/icons/settings/starmini.svg"_q)
, _animation([=](crl::time now) {
if (now > _nextBirthTime) {
if (now > _nextBirthTime && !_paused) {
createStar(now);
_nextBirthTime = now + randomInterval(_lifeLength);
}
@ -423,6 +382,10 @@ void MiniStars::paint(Painter &p, const QRectF &rect) {
p.setOpacity(opacity);
}
void MiniStars::setPaused(bool paused) {
_paused = paused;
}
int MiniStars::angle() const {
const auto &interval = _availableAngles[
base::RandomIndex(_availableAngles.size())];
@ -456,6 +419,7 @@ public:
rpl::producer<QString> title,
rpl::producer<TextWithEntities> about);
void setPaused(bool paused);
void setRoundEdges(bool value);
void setTextPosition(int x, int y);
@ -523,6 +487,10 @@ TopBar::TopBar(
});
}
void TopBar::setPaused(bool paused) {
_ministars.setPaused(paused);
}
void TopBar::setRoundEdges(bool value) {
_roundEdges = value;
update();
@ -578,17 +546,6 @@ void TopBar::paintEvent(QPaintEvent *e) {
p.fillRect(e->rect(), Qt::transparent);
const auto r = rect();
auto pathTop = QPainterPath();
if (_roundEdges) {
pathTop.addRoundedRect(r, st::boxRadius, st::boxRadius);
} else {
pathTop.addRect(r);
}
auto pathBottom = QPainterPath();
pathBottom.addRect(
QRect(
QPoint(r.x(), r.y() + r.height() - st::boxRadius),
QSize(r.width(), st::boxRadius)));
const auto gradientPointTop = r.height() / 3. * 2.;
auto gradient = QLinearGradient(
@ -599,7 +556,16 @@ void TopBar::paintEvent(QPaintEvent *e) {
gradient.setColorAt(1., st::premiumButtonBg3->c);
PainterHighQualityEnabler hq(p);
p.fillPath(pathTop + pathBottom, gradient);
if (_roundEdges) {
const auto radius = st::boxRadius;
p.setBrush(gradient);
p.drawRoundedRect(
r + QMargins{ 0, 0, 0, radius + 1 },
radius,
radius);
} else {
p.fillRect(r, gradient);
}
p.setOpacity(_progress.body);
p.translate(_starRect.center());
@ -663,10 +629,12 @@ private:
const not_null<Window::SessionController*> _controller;
const QString _ref;
QPointer<Ui::GradientButton> _subscribe;
base::unique_qptr<Ui::FadeWrap<Ui::IconButton>> _back;
base::unique_qptr<Ui::IconButton> _close;
rpl::variable<bool> _backToggles;
rpl::variable<Info::Wrap> _wrap;
Fn<void(bool)> _setPaused;
rpl::event_stream<> _showBack;
rpl::event_stream<> _showFinished;
@ -791,21 +759,30 @@ void Premium::setupContent() {
const auto section = entry.section;
button->setClickedCallback([=, controller = _controller] {
_setPaused(true);
const auto hidden = crl::guard(this, [=] {
_setPaused(false);
});
if (section) {
ShowPremiumPreviewBox(controller, *section);
ShowPremiumPreviewToBuy(controller, *section, hidden);
return;
}
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
DoubledLimitsPreviewBox(box, &controller->session());
auto callback = [=] {
SendScreenAccept(controller);
StartPremiumPayment(controller, _ref);
};
const auto button = CreateSubscribeButton(
controller,
box,
std::move(callback));
[] { return u"double_limits"_q; });
box->boxClosing(
) | rpl::start_with_next(hidden, box->lifetime());
box->setShowFinishedCallback([=] {
button->startGlareAnimation();
});
box->setStyle(st::premiumPreviewDoubledLimitsBox);
box->widthValue(
) | rpl::start_with_next([=](int width) {
@ -925,6 +902,10 @@ QPointer<Ui::RpWidget> Premium::createPinnedToTop(
_controller,
std::move(title),
std::move(about));
_setPaused = [=](bool paused) {
content->setPaused(paused);
_subscribe->setGlarePaused(paused);
};
_wrap.value(
) | rpl::start_with_next([=](Info::Wrap wrap) {
@ -988,25 +969,24 @@ QPointer<Ui::RpWidget> Premium::createPinnedToBottom(
not_null<Ui::RpWidget*> parent) {
const auto content = Ui::CreateChild<Ui::RpWidget>(parent.get());
const auto button = CreateSubscribeButton(_controller, content, [=] {
SendScreenAccept(_controller);
StartPremiumPayment(_controller, _ref);
_subscribe = CreateSubscribeButton(_controller, content, [=] {
return _ref;
});
_showFinished.events(
) | rpl::take(1) | rpl::start_with_next([=] {
button->startGlareAnimation();
}, button->lifetime());
_subscribe->startGlareAnimation();
}, _subscribe->lifetime());
content->widthValue(
) | rpl::start_with_next([=](int width) {
const auto padding = st::settingsPremiumButtonPadding;
button->resizeToWidth(width - padding.left() - padding.right());
}, button->lifetime());
_subscribe->resizeToWidth(width - padding.left() - padding.right());
}, _subscribe->lifetime());
const auto session = &_controller->session();
rpl::combine(
button->heightValue(),
_subscribe->heightValue(),
Data::AmPremiumValue(session),
session->premiumPossibleValue()
) | rpl::start_with_next([=](
@ -1020,9 +1000,9 @@ QPointer<Ui::RpWidget> Premium::createPinnedToBottom(
? (padding.top() + buttonHeight + padding.bottom())
: 0;
content->resize(content->width(), finalHeight);
button->moveToLeft(padding.left(), padding.top());
button->setVisible(!premium && premiumPossible);
}, button->lifetime());
_subscribe->moveToLeft(padding.left(), padding.top());
_subscribe->setVisible(!premium && premiumPossible);
}, _subscribe->lifetime());
return Ui::MakeWeak(not_null<Ui::RpWidget*>{ content });
}
@ -1082,4 +1062,60 @@ void StartPremiumPayment(
}
}
QString LookupPremiumRef(PremiumPreview section) {
for (const auto &[ref, entry] : EntryMap()) {
if (entry.section == section) {
return ref;
}
}
return QString();
}
not_null<Ui::GradientButton*> CreateSubscribeButton(
not_null<Window::SessionController*> controller,
not_null<Ui::RpWidget*> parent,
Fn<QString()> computeRef) {
const auto result = Ui::CreateChild<Ui::GradientButton>(
parent.get(),
Ui::Premium::ButtonGradientStops());
result->setClickedCallback([=] {
SendScreenAccept(controller);
StartPremiumPayment(controller, computeRef());
});
const auto &st = st::premiumPreviewBox.button;
result->resize(parent->width(), st.height);
const auto premium = &controller->session().api().premium();
premium->reload();
const auto computeCost = [=] {
const auto amount = premium->monthlyAmount();
const auto currency = premium->monthlyCurrency();
const auto valid = (amount > 0) && !currency.isEmpty();
return Ui::FillAmountAndCurrency(
valid ? amount : 500,
valid ? currency : "USD");
};
const auto label = Ui::CreateChild<Ui::FlatLabel>(
result,
tr::lng_premium_summary_button(
lt_cost,
premium->statusTextValue() | rpl::map(computeCost)),
st::premiumPreviewButtonLabel);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
rpl::combine(
result->widthValue(),
label->widthValue()
) | rpl::start_with_next([=](int outer, int width) {
label->moveToLeft(
(outer - width) / 2,
st::premiumPreviewBox.button.textTop,
outer);
}, label->lifetime());
return result;
}
} // namespace Settings

View File

@ -9,6 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_type.h"
enum class PremiumPreview;
namespace Ui {
class RpWidget;
class GradientButton;
} // namespace Ui
namespace Main {
class Session;
} // namespace Main
@ -30,5 +37,13 @@ void StartPremiumPayment(
not_null<Window::SessionController*> controller,
const QString &ref);
[[nodiscard]] QString LookupPremiumRef(PremiumPreview section);
[[nodiscard]] not_null<Ui::GradientButton*> CreateSubscribeButton(
not_null<Window::SessionController*> controller,
not_null<Ui::RpWidget*> parent,
Fn<QString()> computeRef);
} // namespace Settings

View File

@ -484,10 +484,10 @@ Line::Line(
TextFactory textFactory,
int min)
: Ui::RpWidget(parent)
, _leftText(st::defaultTextStyle, tr::lng_premium_free(tr::now))
, _rightText(st::defaultTextStyle, tr::lng_premium(tr::now))
, _rightLabel(st::defaultTextStyle, max ? textFactory(max) : QString())
, _leftLabel(st::defaultTextStyle, min ? textFactory(min) : QString()) {
, _leftText(st::semiboldTextStyle, tr::lng_premium_free(tr::now))
, _rightText(st::semiboldTextStyle, tr::lng_premium(tr::now))
, _rightLabel(st::semiboldTextStyle, max ? textFactory(max) : QString())
, _leftLabel(st::semiboldTextStyle, min ? textFactory(min) : QString()) {
resize(width(), st::requestsAcceptButton.height);
sizeValue(

View File

@ -81,6 +81,10 @@ void GradientButton::validateBg() {
_bg = Images::Round(std::move(_bg), ImageRoundRadius::Large);
}
void GradientButton::setGlarePaused(bool paused) {
_glare.paused = paused;
}
void GradientButton::validateGlare() {
if (anim::Disabled()) {
return;
@ -88,7 +92,7 @@ void GradientButton::validateGlare() {
_glare.width = st::gradientButtonGlareWidth;
_glare.animation.init([=](crl::time now) {
if (const auto diff = (now - _glare.glare.deathTime); diff > 0) {
if (diff > st::gradientButtonGlareTimeout) {
if (diff > st::gradientButtonGlareTimeout && !_glare.paused) {
_glare.glare = Glare{
.birthTime = now,
.deathTime = now + st::gradientButtonGlareDuration,

View File

@ -16,6 +16,7 @@ public:
GradientButton(QWidget *widget, QGradientStops stops);
void startGlareAnimation();
void setGlarePaused(bool paused);
private:
void paintEvent(QPaintEvent *e);
@ -35,7 +36,8 @@ private:
Ui::Animations::Basic animation;
Glare glare;
QPixmap pixmap;
int width;
int width = 0;
bool paused = false;
} _glare;
};