Added box for premium accounts limits.

This commit is contained in:
23rd 2022-05-31 13:32:07 +03:00 committed by John Preston
parent e09b624b84
commit 78246aada7
6 changed files with 308 additions and 9 deletions

View file

@ -1698,6 +1698,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone.";
"lng_premium_summary_button" = "Subscribe for {cost} per month";
"lng_accounts_limit_title" = "Limit Reached";
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected accounts. You can free one space by subscribing to **Telegram Premium** with on of these connected accounts:";
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts. You can free one space by subscribing to **Telegram Premium** with on of these connected accounts:";
"lng_group_about_header" = "You have created a group.";
"lng_group_about_text" = "Groups can have:";
"lng_group_about1" = "Up to 100,000 members";

View file

@ -1096,3 +1096,17 @@ premiumIconFolders: icon {{ "limits/folders", settingsIconFg }};
premiumIconGroups: icon {{ "limits/groups", settingsIconFg }};
premiumIconLinks: icon {{ "limits/links", settingsIconFg }};
premiumIconPins: icon {{ "limits/pins", settingsIconFg }};
premiumAccountsCheckbox: RoundImageCheckbox(defaultPeerListCheckbox) {
imageRadius: 27px;
imageSmallRadius: 23px;
check: RoundCheckbox(defaultRoundCheckbox) {
size: 0px;
}
}
premiumAccountsLabelSize: size(22px, 15px);
premiumAccountsLabelPadding: margins(2px, 2px, 2px, 2px);
premiumAccountsLabelRadius: 6;
premiumAccountsNameTop: 13px;
premiumAccountsPadding: margins(0px, 20px, 0px, 14px);
premiumAccountsHeight: 105px;

View file

@ -10,13 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h"
#include "ui/controls/peer_list_dummy.h"
#include "ui/effects/premium_graphics.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/text/text_utilities.h"
#include "ui/toasts/common_toasts.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_domain.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/prepare_short_info_box.h" // PrepareShortInfoBox
#include "window/window_session_controller.h"
@ -879,6 +880,111 @@ void FileSizeLimitBox(
premium);
}
void AccountsLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session) {
const auto premium = session->premium();
const auto defaultLimit = Main::Domain::kMaxAccounts;
const auto premiumLimit = Main::Domain::kPremiumMaxAccounts;
const auto &accounts = session->domain().accounts();
const auto current = int(accounts.size());
auto text = tr::lng_accounts_limit1(
lt_count,
rpl::single<float64>(premiumLimit),
Ui::Text::RichLangValue);
box->setWidth(st::boxWideWidth);
const auto top = box->verticalLayout();
const auto group = std::make_shared<Ui::RadiobuttonGroup>(0);
Settings::AddSkip(top, st::premiumInfographicPadding.top());
Ui::Premium::AddBubbleRow(
top,
BoxShowFinishes(box),
0,
current,
(current == premiumLimit) ? premiumLimit : (current * 2),
std::nullopt,
&st::premiumIconFiles);
Settings::AddSkip(top, st::premiumLineTextSkip);
Ui::Premium::AddLimitRow(top, 0, std::nullopt, defaultLimit);
Settings::AddSkip(top, st::premiumInfographicPadding.bottom());
box->setTitle(tr::lng_accounts_limit_title());
auto padding = st::boxPadding;
padding.setTop(padding.bottom());
top->add(
object_ptr<Ui::FlatLabel>(
box,
std::move(text),
st::aboutRevokePublicLabel),
padding);
if (premium) {
box->addButton(tr::lng_box_ok(), [=] {
box->closeBox();
});
} else {
auto switchingLifetime = std::make_shared<rpl::lifetime>();
box->addButton(tr::lng_continue(), [=]() mutable {
const auto ref = QString();
const auto &accounts = session->domain().accounts();
const auto wasAccount = &session->account();
const auto nowAccount = accounts[group->value()].account.get();
if (wasAccount == nowAccount) {
Settings::ShowPremium(session, ref);
return;
}
if (*switchingLifetime) {
return;
}
*switchingLifetime = session->domain().activeSessionChanges(
) | rpl::start_with_next([=](Main::Session *session) mutable {
if (session) {
Settings::ShowPremium(session, ref);
}
if (switchingLifetime) {
base::take(switchingLifetime)->destroy();
}
});
session->domain().activate(nowAccount);
});
}
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
using Args = Ui::Premium::AccountsRowArgs;
auto &&entries = ranges::views::all(
accounts
) | ranges::views::transform([&](
const Main::Domain::AccountWithIndex &d) {
const auto user = d.account->session().user();
return Args::Entry{ user->name, PaintUserpicCallback(user, false) };
});
auto args = Args{
.group = group,
.st = st::premiumAccountsCheckbox,
.stName = st::shareBoxListItem.nameStyle,
.stNameFg = st::shareBoxListItem.nameFg,
.entries = std::move(entries) | ranges::to_vector,
};
box->addSkip(st::premiumAccountsPadding.top());
Ui::Premium::AddAccountsRow(box->verticalLayout(), std::move(args));
box->addSkip(st::premiumAccountsPadding.bottom());
}
QString LimitsPremiumRef(const QString &addition) {
return "double_limits__" + addition;
}

View file

@ -49,6 +49,9 @@ void CaptionLimitReachedBox(
void FileSizeLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session);
void AccountsLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session);
[[nodiscard]] QString LimitsPremiumRef(const QString &addition);

View file

@ -11,18 +11,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/gradient.h"
#include "ui/effects/numbers_animation.h"
#include "ui/text/text_options.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "styles/style_widgets.h"
#include <QtGui/QBrush>
namespace Ui {
namespace Premium {
namespace {
using TextFactory = Fn<QString(int)>;
constexpr auto kBubbleRadiusSubtractor = 2;
constexpr auto kDeflectionSmall = 20.;
constexpr auto kDeflection = 30.;
@ -133,7 +138,7 @@ int Bubble::height() const {
}
int Bubble::bubbleRadius() const {
return (_height - _tailSize.height()) / 2;
return (_height - _tailSize.height()) / 2 - kBubbleRadiusSubtractor;
}
int Bubble::filledWidth() const {
@ -430,7 +435,11 @@ void BubbleWidget::paintEvent(QPaintEvent *e) {
class Line final : public Ui::RpWidget {
public:
Line(not_null<Ui::RpWidget*> parent, int max, TextFactory textFactory);
Line(
not_null<Ui::RpWidget*> parent,
int max,
TextFactory textFactory,
int min);
protected:
void paintEvent(QPaintEvent *event) override;
@ -447,14 +456,20 @@ private:
Ui::Text::String _leftText;
Ui::Text::String _rightText;
Ui::Text::String _rightLabel;
Ui::Text::String _leftLabel;
};
Line::Line(not_null<Ui::RpWidget*> parent, int max, TextFactory textFactory)
Line::Line(
not_null<Ui::RpWidget*> parent,
int max,
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, textFactory(max)) {
, _rightLabel(st::defaultTextStyle, max ? textFactory(max) : QString())
, _leftLabel(st::defaultTextStyle, min ? textFactory(min) : QString()) {
resize(width(), st::requestsAcceptButton.height);
sizeValue(
@ -478,6 +493,13 @@ void Line::paintEvent(QPaintEvent *event) {
const auto textTop = (height() - _leftText.minHeight()) / 2;
p.setPen(st::windowFg);
_leftLabel.drawRight(
p,
textPadding,
textTop,
_leftWidth - textPadding,
_leftWidth,
style::al_right);
_leftText.drawLeft(
p,
textPadding,
@ -572,12 +594,139 @@ void AddBubbleRow(
void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
int max,
std::optional<tr::phrase<lngtag_count>> phrase) {
std::optional<tr::phrase<lngtag_count>> phrase,
int min) {
const auto line = parent->add(
object_ptr<Line>(parent, max, ProcessTextFactory(phrase)),
object_ptr<Line>(parent, max, ProcessTextFactory(phrase), min),
st::boxRowPadding);
}
void AddAccountsRow(
not_null<Ui::VerticalLayout*> parent,
AccountsRowArgs &&args) {
const auto container = parent->add(
object_ptr<Ui::FixedHeightWidget>(parent, st::premiumAccountsHeight),
st::boxRowPadding);
struct Account {
not_null<Ui::AbstractButton*> widget;
Ui::RoundImageCheckbox checkbox;
Ui::Text::String name;
QPixmap badge;
};
struct State {
std::vector<Account> accounts;
};
const auto state = container->lifetime().make_state<State>();
const auto group = args.group;
group->setChangedCallback([=](int value) {
for (auto i = 0; i < state->accounts.size(); i++) {
state->accounts[i].checkbox.setChecked(i == value);
}
});
const auto imageRadius = args.st.imageRadius;
const auto checkSelectWidth = args.st.selectWidth;
const auto nameFg = args.stNameFg;
const auto cacheBadge = [=](int center) {
const auto &padding = st::premiumAccountsLabelPadding;
const auto size = st::premiumAccountsLabelSize
+ QSize(
padding.left() + padding.right(),
padding.top() + padding.bottom());
auto badge = QPixmap(size * style::DevicePixelRatio());
badge.setDevicePixelRatio(style::DevicePixelRatio());
badge.fill(Qt::transparent);
Painter p(&badge);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
const auto rectOut = QRect(QPoint(), size);
const auto rectIn = rectOut - padding;
const auto radius = st::premiumAccountsLabelRadius;
p.setBrush(st::premiumButtonFg);
p.drawRoundedRect(rectOut, radius, radius);
const auto left = center - rectIn.width() / 2;
p.setBrush(QBrush(ComputeGradient(container, left, rectIn.width())));
p.drawRoundedRect(rectIn, radius / 2, radius / 2);
p.setPen(st::premiumButtonFg);
p.setFont(st::semiboldFont);
p.drawText(rectIn, u"+1"_q, style::al_center);
return badge;
};
for (auto &entry : args.entries) {
const auto widget = Ui::CreateChild<Ui::AbstractButton>(container);
auto name = Ui::Text::String(imageRadius * 2);
name.setText(args.stName, entry.name, Ui::NameTextOptions());
state->accounts.push_back(Account{
.widget = widget,
.checkbox = Ui::RoundImageCheckbox(
args.st,
[=] { widget->update(); },
base::take(entry.paintRoundImage)),
.name = std::move(name),
});
const auto index = int(state->accounts.size()) - 1;
state->accounts[index].checkbox.setChecked(index == group->value());
widget->paintRequest(
) | rpl::start_with_next([=] {
Painter p(widget);
const auto width = widget->width();
const auto photoLeft = (width - (imageRadius * 2)) / 2;
const auto photoTop = checkSelectWidth;
auto &account = state->accounts[index];
account.checkbox.paint(p, photoLeft, photoTop, width);
const auto &badgeSize = account.badge.size()
/ style::DevicePixelRatio();
p.drawPixmap(
(width - badgeSize.width()) / 2,
photoTop + (imageRadius * 2) - badgeSize.height() / 2,
account.badge);
p.setPen(nameFg);
p.setBrush(Qt::NoBrush);
account.name.drawLeftElided(
p,
0,
photoTop + imageRadius * 2 + st::premiumAccountsNameTop,
width,
width,
2,
style::al_top,
0,
-1,
0,
true);
}, widget->lifetime());
widget->setClickedCallback([=] {
group->setValue(index);
});
}
container->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
const auto count = state->accounts.size();
const auto columnWidth = size.width() / count;
for (auto i = 0; i < count; i++) {
state->accounts[i].widget->resize(columnWidth, size.height());
const auto left = columnWidth * i;
state->accounts[i].widget->moveToLeft(left, 0);
state->accounts[i].badge = cacheBadge(left + columnWidth / 2);
}
}, container->lifetime());
}
QGradientStops LimitGradientStops() {
return {
{ 0.0, st::premiumButtonBg1->c },

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtGui/QBrush>
#include "ui/effects/round_checkbox.h"
namespace tr {
template <typename ...>
@ -16,8 +16,14 @@ struct phrase;
enum lngtag_count : int;
namespace style {
struct RoundImageCheckbox;
struct TextStyle;
} // namespace style
namespace Ui {
class RadiobuttonGroup;
class VerticalLayout;
namespace Premium {
@ -34,7 +40,24 @@ void AddBubbleRow(
void AddLimitRow(
not_null<Ui::VerticalLayout*> parent,
int max,
std::optional<tr::phrase<lngtag_count>> phrase);
std::optional<tr::phrase<lngtag_count>> phrase,
int min = 0);
struct AccountsRowArgs final {
std::shared_ptr<Ui::RadiobuttonGroup> group;
const style::RoundImageCheckbox &st;
const style::TextStyle &stName;
const style::color &stNameFg;
struct Entry final {
QString name;
Ui::RoundImageCheckbox::PaintRoundImage paintRoundImage;
};
std::vector<Entry> entries;
};
void AddAccountsRow(
not_null<Ui::VerticalLayout*> parent,
AccountsRowArgs &&args);
[[nodiscard]] QGradientStops LimitGradientStops();
[[nodiscard]] QGradientStops ButtonGradientStops();