diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b298c8863..32e4e0525 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1703,6 +1703,46 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_premium_success" = "You've successfully subscribed to Telegram Premium!"; "lng_premium_unavailable" = "This feature requires subscription to **Telegram Premium**.\n\nUnfortunately, **Telegram Premium** is not available in your region."; +// Doubled Limits. +"lng_premium_double_limits_subtitle_channels" = "Groups and Channels"; +"lng_premium_double_limits_about_channels#one" = "Join up to {count} channels and large groups"; +"lng_premium_double_limits_about_channels#other" = "Join up to {count} channels and large groups"; + +"lng_premium_double_limits_subtitle_pins" = "Pinned Chats"; +"lng_premium_double_limits_about_pins#one" = "Pin up to {count} chats in your main chat list"; +"lng_premium_double_limits_about_pins#other" = "Pin up to {count} chats in your main chat list"; + +"lng_premium_double_limits_subtitle_links" = "Public Links"; +"lng_premium_double_limits_about_links#one" = "Reserve up to {count} t.me/name links"; +"lng_premium_double_limits_about_links#other" = "Reserve up to {count} t.me/name links"; + +"lng_premium_double_limits_subtitle_gifs" = "Saved GIFs"; +"lng_premium_double_limits_about_gifs#one" = "Save up to {count} GIFs in your Favorite GIFs"; +"lng_premium_double_limits_about_gifs#other" = "Save up to {count} GIFs in your Favorite GIFs"; + +"lng_premium_double_limits_subtitle_stickers" = "Favorite Stickers"; +"lng_premium_double_limits_about_stickers#one" = "Save up to {count} stickers in your Favorite stickers"; +"lng_premium_double_limits_about_stickers#other" = "Save up to {count} stickers in your Favorite stickers"; + +"lng_premium_double_limits_subtitle_bio" = "Bio"; +"lng_premium_double_limits_about_bio" = "Add more symbols and use links in your bio"; + +"lng_premium_double_limits_subtitle_captions" = "Captions"; +"lng_premium_double_limits_about_captions" = "Use longer description for your photos and videos"; + +"lng_premium_double_limits_subtitle_folders" = "Folders"; +"lng_premium_double_limits_about_folders#one" = "Organize your chats into {count} folders"; +"lng_premium_double_limits_about_folders#other" = "Organize your chats into {count} folders"; + +"lng_premium_double_limits_subtitle_folder_chats" = "Chats per Folder"; +"lng_premium_double_limits_about_folder_chats#one" = "Add up to {count} chats into each of your folders"; +"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#other" = "Connect {count} accounts with different mobile numbers"; +// + "lng_accounts_limit_title" = "Limit Reached"; "lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected accounts."; "lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts."; diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp index 622bf4a44..2a5777c8e 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_streaming.h" #include "lang/lang_keys.h" #include "main/main_session.h" +#include "main/main_domain.h" // kMaxAccounts #include "ui/chat/chat_theme.h" #include "ui/chat/chat_style.h" #include "ui/layers/generic_box.h" @@ -29,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/gradient_round_button.h" #include "ui/wrap/padding_wrap.h" #include "ui/boxes/confirm_box.h" +#include "boxes/premium_limits_box.h" // AppConfigLimit #include "settings/settings_premium.h" #include "lottie/lottie_single_player.h" #include "history/view/media/history_view_sticker.h" @@ -1293,3 +1295,178 @@ void PremiumUnavailableBox(not_null box) { .inform = true, }); } + +void DoubledLimitsPreviewBox( + not_null box, + not_null session) { + auto entries = std::vector(); + { + const auto premium = AppConfigLimit( + session, + "channels_limit_premium", + 500 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_channels(), + tr::lng_premium_double_limits_about_channels( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "channels_limit_default", + 500), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "dialogs_folder_pinned_limit_premium", + 5 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_pins(), + tr::lng_premium_double_limits_about_pins( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "dialogs_folder_pinned_limit_default", + 5), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "channels_public_limit_premium", + 10 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_links(), + tr::lng_premium_double_limits_about_links( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "channels_public_limit_default", + 10), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "saved_gifs_limit_premium", + 200 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_gifs(), + tr::lng_premium_double_limits_about_gifs( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "saved_gifs_limit_default", + 200), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "stickers_faved_limit_premium", + 5 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_stickers(), + tr::lng_premium_double_limits_about_stickers( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "stickers_faved_limit_default", + 5), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "about_length_limit_premium", + 70 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_bio(), + tr::lng_premium_double_limits_about_bio( + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "about_length_limit_default", + 70), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "caption_length_limit_premium", + 1024 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_captions(), + tr::lng_premium_double_limits_about_captions( + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "caption_length_limit_default", + 1024), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "dialog_filters_limit_premium", + 10 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_folders(), + tr::lng_premium_double_limits_about_folders( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "dialog_filters_limit_default", + 10), + premium, + }); + } + { + const auto premium = AppConfigLimit( + session, + "dialog_filters_chats_limit_premium", + 100 * 2); + entries.push_back(Ui::Premium::ListEntry{ + tr::lng_premium_double_limits_subtitle_folder_chats(), + tr::lng_premium_double_limits_about_folder_chats( + lt_count, + rpl::single(float64(premium)), + Ui::Text::RichLangValue), + AppConfigLimit( + session, + "dialog_filters_chats_limit_default", + 100), + premium, + }); + } + 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)), + Ui::Text::RichLangValue), + Main::Domain::kMaxAccounts, + Main::Domain::kPremiumMaxAccounts, + QString::number(Main::Domain::kMaxAccounts + 1) + QChar('+'), + }); + Ui::Premium::ShowListBox(box, std::move(entries)); +} diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.h b/Telegram/SourceFiles/boxes/premium_preview_box.h index f6fe1ec1d..17f26f9dc 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.h +++ b/Telegram/SourceFiles/boxes/premium_preview_box.h @@ -17,10 +17,18 @@ namespace Window { class SessionController; } // namespace Window +namespace Main { +class Session; +} // namespace Main + void ShowStickerPreviewBox( not_null controller, not_null document); +void DoubledLimitsPreviewBox( + not_null box, + not_null session); + enum class PremiumPreview { MoreUpload, FasterDownload, diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 2f601f433..28052a8fe 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -288,6 +288,9 @@ premiumPreviewBox: Box(defaultBox) { font: font(13px semibold); } } +premiumPreviewDoubledLimitsBox: Box(premiumPreviewBox) { + buttonPadding: margins(12px, 12px, 12px, 12px); +} premiumPreviewAboutTitlePadding: margins(18px, 19px, 18px, 0px); premiumPreviewAboutTitle: FlatLabel(defaultFlatLabel) { minWidth: 240px; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 21ceedc3d..f9e69c96c 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -449,6 +449,9 @@ settingsPremiumTopBarClose: IconButton(infoTopBarClose) { } settingsPremiumRowTitlePadding: margins(60px, 5px, 46px, 3px); settingsPremiumRowAboutPadding: margins(60px, 0px, 46px, 6px); +settingsPremiumPreviewTitlePadding: margins(24px, 13px, 24px, 3px); +settingsPremiumPreviewAboutPadding: margins(24px, 0px, 24px, 11px); +settingsPremiumPreviewLinePadding: margins(18px, 0px, 18px, 8px); settingsPremiumTitlePadding: margins(0px, 20px, 0px, 16px); settingsPremiumAbout: FlatLabel(defaultFlatLabel) { diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index c8c624376..d453d5b57 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_wrap_widget.h" // Info::Wrap. #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData. #include "lang/lang_keys.h" -#include "main/main_session.h" #include "boxes/premium_preview_box.h" #include "settings/settings_common.h" #include "settings/settings_premium.h" @@ -782,6 +781,33 @@ void Premium::setupContent() { arrow->moveToRight(0, (s.height() - arrow->height()) / 2); }, arrow->lifetime()); + button->setClickedCallback([=, controller = _controller] { + controller->show(Box([=](not_null box) { + DoubledLimitsPreviewBox(box, &controller->session()); + + auto callback = [=] { + SendScreenAccept(controller); + StartPremiumPayment(controller, _ref); + }; + const auto button = CreateSubscribeButton( + controller, + box, + std::move(callback)); + box->setStyle(st::premiumPreviewDoubledLimitsBox); + box->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto &padding = + st::premiumPreviewDoubledLimitsBox.buttonPadding; + button->resizeToWidth(width + - padding.left() + - padding.right()); + button->moveToLeft(padding.left(), padding.top()); + }, button->lifetime()); + box->addButton( + object_ptr::fromRaw(button)); + })); + }); + iconContainers.push_back(dummy); }; @@ -815,10 +841,7 @@ void Premium::setupContent() { const auto from = iconContainers.front()->y(); const auto to = iconContainers.back()->y() + iconSize.height(); auto gradient = QLinearGradient(0, 0, 0, to - from); - gradient.setColorAt(0.0, st::premiumIconBg1->c); - gradient.setColorAt(.28, st::premiumIconBg2->c); - gradient.setColorAt(.55, st::premiumButtonBg2->c); - gradient.setColorAt(1.0, st::premiumButtonBg1->c); + gradient.setStops(Ui::Premium::FullHeightGradientStops()); for (auto i = 0; i < int(icons.size()); i++) { const auto &iconContainer = iconContainers[i]; diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp index 6e340db9b..7baf72aa5 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp @@ -11,11 +11,14 @@ 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_utilities.h" +#include "ui/layers/generic_box.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_settings.h" #include "styles/style_layers.h" #include "styles/style_widgets.h" @@ -452,6 +455,8 @@ public: TextFactory textFactory, int min); + void setColorOverride(QBrush brush); + protected: void paintEvent(QPaintEvent *event) override; @@ -469,6 +474,8 @@ private: Ui::Text::String _rightLabel; Ui::Text::String _leftLabel; + std::optional _overrideBrush; + }; Line::Line( @@ -495,6 +502,14 @@ Line::Line( }, lifetime()); } +void Line::setColorOverride(QBrush brush) { + if (brush.style() == Qt::NoBrush) { + _overrideBrush = std::nullopt; + } else { + _overrideBrush = brush; + } +} + void Line::paintEvent(QPaintEvent *event) { Painter p(this); @@ -568,11 +583,15 @@ void Line::recache(const QSize &s) { halfRect.setRight(r.center().x()); pathRect.addRect(halfRect); - auto gradient = ComputeGradient( - this, - (_leftPixmap.width() / style::DevicePixelRatio()) + r.x(), - r.width()); - p.fillPath(pathRound + pathRect, QBrush(std::move(gradient))); + if (_overrideBrush) { + p.fillPath(pathRound + pathRect, *_overrideBrush); + } else { + auto gradient = ComputeGradient( + this, + (_leftPixmap.width() / style::DevicePixelRatio()) + r.x(), + r.width()); + p.fillPath(pathRound + pathRect, QBrush(std::move(gradient))); + } _rightPixmap = std::move(rightPixmap); } @@ -612,7 +631,7 @@ void AddLimitRow( int max, std::optional> phrase, int min) { - const auto line = parent->add( + parent->add( object_ptr(parent, max, ProcessTextFactory(phrase), min), st::boxRowPadding); } @@ -774,5 +793,101 @@ QGradientStops LockGradientStops() { return ButtonGradientStops(); } +QGradientStops FullHeightGradientStops() { + return { + { 0.0, st::premiumIconBg1->c }, + { .28, st::premiumIconBg2->c }, + { .55, st::premiumButtonBg2->c }, + { 1.0, st::premiumButtonBg1->c }, + }; +} + +void ShowListBox( + not_null box, + std::vector entries) { + + const auto &stLabel = st::defaultFlatLabel; + const auto &titlePadding = st::settingsPremiumPreviewTitlePadding; + const auto &descriptionPadding = st::settingsPremiumPreviewAboutPadding; + + auto lines = std::vector(); + lines.reserve(int(entries.size())); + + const auto content = box->verticalLayout(); + for (auto &entry : entries) { + const auto subtitle = content->add( + object_ptr( + content, + base::take(entry.subtitle) | rpl::map(Ui::Text::Bold), + stLabel), + titlePadding); + const auto description = content->add( + object_ptr( + content, + base::take(entry.description), + st::boxDividerLabel), + descriptionPadding); + + const auto limitRow = content->add( + object_ptr( + content, + entry.rightNumber, + TextFactory([=, text = ProcessTextFactory(std::nullopt)]( + int n) { + if (entry.customRightText && (n == entry.rightNumber)) { + return *entry.customRightText; + } else { + return text(n); + } + }), + entry.leftNumber), + st::settingsPremiumPreviewLinePadding); + lines.push_back(limitRow); + } + + content->resizeToWidth(content->height()); + + // Color lines. + Assert(lines.size() > 2); + const auto from = lines.front()->y(); + const auto to = lines.back()->y() + lines.back()->height(); + auto gradient = QLinearGradient(0, 0, 0, to - from); + + { + auto stops = Ui::Premium::FullHeightGradientStops(); + for (auto &stop : stops) { + stop.first = std::abs(stop.first - 1.); + } + gradient.setStops(std::move(stops)); + } + + for (auto i = 0; i < int(lines.size()); i++) { + const auto &line = lines[i]; + + const auto pointTop = line->y() - from; + const auto pointBottom = pointTop + line->height(); + const auto ratioTop = pointTop / float64(to - from); + const auto ratioBottom = pointBottom / float64(to - from); + + auto resultGradient = QLinearGradient( + QPointF(), + QPointF(0, pointBottom - pointTop)); + + resultGradient.setColorAt( + .0, + anim::gradient_color_at(gradient, ratioTop)); + resultGradient.setColorAt( + .1, + anim::gradient_color_at(gradient, ratioBottom)); + + const auto brush = QBrush(resultGradient); + line->setColorOverride(brush); + } + box->addSkip(st::settingsPremiumPreviewLinePadding.bottom()); + + box->setTitle(tr::lng_premium_summary_subtitle_double_limits()); + box->setWidth(st::boxWideWidth); +} + } // namespace Premium } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.h b/Telegram/SourceFiles/ui/effects/premium_graphics.h index 8b9ce583f..c15643320 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.h +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.h @@ -23,6 +23,7 @@ struct TextStyle; namespace Ui { +class GenericBox; class RadiobuttonGroup; class VerticalLayout; @@ -63,6 +64,18 @@ void AddAccountsRow( [[nodiscard]] QGradientStops LimitGradientStops(); [[nodiscard]] QGradientStops ButtonGradientStops(); [[nodiscard]] QGradientStops LockGradientStops(); +[[nodiscard]] QGradientStops FullHeightGradientStops(); + +struct ListEntry final { + rpl::producer subtitle; + rpl::producer description; + int leftNumber = 0; + int rightNumber = 0; + std::optional customRightText; +}; +void ShowListBox( + not_null box, + std::vector entries); } // namespace Premium } // namespace Ui