mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Added box for premium accounts limits.
This commit is contained in:
parent
e09b624b84
commit
78246aada7
6 changed files with 308 additions and 9 deletions
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue