Added suggestion for annual premium to top bar in dialogs.

This commit is contained in:
23rd 2025-04-05 19:17:25 +03:00 committed by John Preston
parent 98d9357208
commit 640db8af7d
9 changed files with 118 additions and 20 deletions

View file

@ -3865,6 +3865,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_dialogs_top_bar_suggestions_birthday_title" = "Add your birthday! 🎂"; "lng_dialogs_top_bar_suggestions_birthday_title" = "Add your birthday! 🎂";
"lng_dialogs_top_bar_suggestions_birthday_about" = "Let your contacts know when youre celebrating."; "lng_dialogs_top_bar_suggestions_birthday_about" = "Let your contacts know when youre celebrating.";
"lng_dialogs_top_bar_suggestions_premium_annual_title" = "Telegram Premium with a {text} discount";
"lng_dialogs_top_bar_suggestions_premium_annual_about" = "Sign up for the annual payment plan for Telegram Premium now to get the discount.";
"lng_about_random" = "Send a {emoji} emoji to any chat to try your luck."; "lng_about_random" = "Send a {emoji} emoji to any chat to try your luck.";
"lng_about_random_send" = "Send"; "lng_about_random_send" = "Send";

View file

@ -25,6 +25,7 @@ Data::PremiumSubscriptionOption CreateSubscriptionOption(
* kDiscountDivider; * kDiscountDivider;
}(); }();
return { return {
.months = months,
.duration = Ui::FormatTTL(months * 86400 * 31), .duration = Ui::FormatTTL(months * 86400 * 31),
.discount = (discount > 0) .discount = (discount > 0)
? QString::fromUtf8("\xe2\x88\x92%1%").arg(discount) ? QString::fromUtf8("\xe2\x88\x92%1%").arg(discount)

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
struct PremiumSubscriptionOption { struct PremiumSubscriptionOption {
int months = 0;
QString duration; QString duration;
QString discount; QString discount;
QString costPerMonth; QString costPerMonth;

View file

@ -813,3 +813,10 @@ dialogsSponsoredButton: DialogRightButton(dialogRowOpenBot) {
} }
margin: margins(0px, 9px, 10px, 0px); margin: margins(0px, 9px, 10px, 0px);
} }
dialogsTopBarSuggestionTitleStyle: TextStyle(defaultTextStyle) {
font: font(semibold 12px);
}
dialogsTopBarSuggestionAboutStyle: TextStyle(defaultTextStyle) {
font: font(11px);
}

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "dialogs/dialogs_top_bar_suggestion.h" #include "dialogs/dialogs_top_bar_suggestion.h"
#include "api/api_premium.h"
#include "apiwrap.h"
#include "base/call_delayed.h" #include "base/call_delayed.h"
#include "core/application.h" #include "core/application.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
@ -17,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_app_config.h" #include "main/main_app_config.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "settings/settings_premium.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/wrap/slide_wrap.h" #include "ui/wrap/slide_wrap.h"
#include "window/window_controller.h" #include "window/window_controller.h"
@ -32,6 +35,7 @@ namespace {
} }
constexpr auto kSugSetBirthday = "BIRTHDAY_SETUP"_cs; constexpr auto kSugSetBirthday = "BIRTHDAY_SETUP"_cs;
constexpr auto kSugPremiumAnnual = "PREMIUM_ANNUAL"_cs;
} // namespace } // namespace
@ -46,12 +50,14 @@ object_ptr<Ui::SlideWrap<Ui::RpWidget>> CreateTopBarSuggestion(
wrap->toggle(false, anim::type::instant); wrap->toggle(false, anim::type::instant);
struct State { struct State {
rpl::lifetime birthdayLifetime; rpl::lifetime birthdayLifetime;
rpl::lifetime premiumLifetime;
}; };
const auto state = content->lifetime().make_state<State>(); const auto state = content->lifetime().make_state<State>();
const auto processCurrentSuggestion = [=](auto repeat) -> void { const auto processCurrentSuggestion = [=](auto repeat) -> void {
if (session->appConfig().suggestionCurrent(kSugSetBirthday.utf8()) if (session->appConfig().suggestionCurrent(kSugSetBirthday.utf8())
&& !Data::IsBirthdayToday(session->user()->birthday())) { && !Data::IsBirthdayToday(session->user()->birthday())) {
content->setRightIcon(TopBarSuggestionContent::RightIcon::Close);
content->setClickedCallback([=] { content->setClickedCallback([=] {
const auto controller = FindSessionController(parent); const auto controller = FindSessionController(parent);
if (!controller) { if (!controller) {
@ -86,6 +92,46 @@ object_ptr<Ui::SlideWrap<Ui::RpWidget>> CreateTopBarSuggestion(
tr::now, tr::now,
TextWithEntities::Simple)); TextWithEntities::Simple));
wrap->toggle(true, anim::type::normal); wrap->toggle(true, anim::type::normal);
} else if (session->premiumPossible()
&& !session->premium()
&& session->appConfig().suggestionCurrent(
kSugPremiumAnnual.utf8())) {
content->setRightIcon(TopBarSuggestionContent::RightIcon::Arrow);
const auto api = &session->api().premium();
const auto set = [=](QString discount) {
constexpr auto kMinus = QChar(0x2212);
content->setContent(
tr::lng_dialogs_top_bar_suggestions_premium_annual_title(
tr::now,
lt_text,
{ discount.replace(kMinus, QChar()) },
Ui::Text::Bold),
tr::lng_dialogs_top_bar_suggestions_premium_annual_about(
tr::now,
TextWithEntities::Simple));
content->setClickedCallback([=] {
const auto controller = FindSessionController(parent);
if (!controller) {
return;
}
Settings::ShowPremium(controller, "dialogs_hint");
session->appConfig().dismissSuggestion(
kSugPremiumAnnual.utf8());
repeat(repeat);
});
wrap->toggle(true, anim::type::normal);
};
api->statusTextValue(
) | rpl::start_with_next([=] {
for (const auto &option : api->subscriptionOptions()) {
if (option.months == 12) {
set(option.discount);
state->premiumLifetime.destroy();
return;
}
}
}, state->premiumLifetime);
api->reload();
} else { } else {
wrap->toggle(false, anim::type::normal); wrap->toggle(false, anim::type::normal);
base::call_delayed(st::slideWrapDuration * 2, wrap, [=] { base::call_delayed(st::slideWrapDuration * 2, wrap, [=] {

View file

@ -392,7 +392,9 @@ Widget::Widget(
) | rpl::start_with_next([=](int desiredHeight, float64 shown) { ) | rpl::start_with_next([=](int desiredHeight, float64 shown) {
const auto newHeight = desiredHeight * (1. - shown); const auto newHeight = desiredHeight * (1. - shown);
_topBarSuggestion->entity()->setMaximumHeight(newHeight); _topBarSuggestion->entity()->setMaximumHeight(newHeight);
_topBarSuggestion->entity()->setMinimumWidth(width()); _topBarSuggestion->entity()->setMinimumWidth((shown > 0)
? width()
: 0);
_topBarSuggestion->entity()->resize(width(), newHeight); _topBarSuggestion->entity()->resize(width(), newHeight);
}, _topBarSuggestion->lifetime()); }, _topBarSuggestion->lifetime());
} }

View file

@ -301,7 +301,7 @@ private:
base::Timer _chooseByDragTimer; base::Timer _chooseByDragTimer;
const Layout _layout = Layout::Main; const Layout _layout = Layout::Main;
int _narrowWidth = 0; const int _narrowWidth = 0;
std::unique_ptr<Ui::AbstractButton> _frozenAccountBar; std::unique_ptr<Ui::AbstractButton> _frozenAccountBar;

View file

@ -6,27 +6,57 @@ For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "dialogs/ui/dialogs_top_bar_suggestion_content.h" #include "dialogs/ui/dialogs_top_bar_suggestion_content.h"
#include "ui/ui_rpl_filter.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "styles/style_settings.h"
namespace Dialogs { namespace Dialogs {
namespace {
} // namespace
TopBarSuggestionContent::TopBarSuggestionContent(not_null<Ui::RpWidget*> p) TopBarSuggestionContent::TopBarSuggestionContent(not_null<Ui::RpWidget*> p)
: Ui::RippleButton(p, st::defaultRippleAnimationBgOver) : Ui::RippleButton(p, st::defaultRippleAnimationBgOver)
, _titleSt(st::semiboldTextStyle) , _titleSt(st::semiboldTextStyle)
, _contentTitleSt(st::semiboldTextStyle) , _contentTitleSt(st::dialogsTopBarSuggestionTitleStyle)
, _contentTextSt(st::defaultTextStyle) , _contentTextSt(st::dialogsTopBarSuggestionAboutStyle) {
, _rightHide( setRightIcon(RightIcon::Close);
base::make_unique_q<Ui::IconButton>( }
this,
st::dialogsCancelSearchInPeer)) { void TopBarSuggestionContent::setRightIcon(RightIcon icon) {
const auto rightHide = _rightHide.get(); if (icon == _rightIcon) {
sizeValue() | rpl::start_with_next([=](const QSize &s) { return;
rightHide->moveToRight(st::buttonRadius, st::lineWidth); }
}, rightHide->lifetime()); _rightHide = nullptr;
_rightArrow = nullptr;
_rightIcon = icon;
if (icon == RightIcon::Close) {
_rightHide = base::make_unique_q<Ui::IconButton>(
this,
st::dialogsCancelSearchInPeer);
const auto rightHide = _rightHide.get();
sizeValue() | rpl::filter_size(
) | rpl::start_with_next([=](const QSize &s) {
rightHide->moveToRight(st::buttonRadius, st::lineWidth);
}, rightHide->lifetime());
rightHide->show();
} else if (icon == RightIcon::Arrow) {
_rightArrow = base::make_unique_q<Ui::IconButton>(
this,
st::backButton);
const auto arrow = _rightArrow.get();
arrow->setIconOverride(
&st::settingsPremiumArrow,
&st::settingsPremiumArrowOver);
arrow->setAttribute(Qt::WA_TransparentForMouseEvents);
sizeValue() | rpl::filter_size(
) | rpl::start_with_next([=](const QSize &s) {
const auto &point = st::settingsPremiumArrowShift;
arrow->moveToLeft(
s.width() - arrow->width(),
point.y() + (s.height() - arrow->height()) / 2);
}, arrow->lifetime());
arrow->show();
}
} }
void TopBarSuggestionContent::draw(QPainter &p) { void TopBarSuggestionContent::draw(QPainter &p) {
@ -41,28 +71,26 @@ void TopBarSuggestionContent::draw(QPainter &p) {
const auto rightPadding = st::msgReplyBarSkip; const auto rightPadding = st::msgReplyBarSkip;
const auto topPadding = st::msgReplyPadding.top(); const auto topPadding = st::msgReplyPadding.top();
const auto availableWidthNoPhoto = r.width() const auto availableWidthNoPhoto = r.width()
- (_rightArrow ? _rightArrow->width() / 2 : 0) // Takes full height.
- leftPadding - leftPadding
- rightPadding; - rightPadding;
const auto availableWidth = availableWidthNoPhoto const auto availableWidth = availableWidthNoPhoto
- (_rightHide ? _rightHide->width() : 0); - (_rightHide ? _rightHide->width() : 0);
const auto titleRight = leftPadding const auto titleRight = leftPadding;
+ _titleSt.font->spacew * 2;
const auto hasSecondLineTitle = (titleRight const auto hasSecondLineTitle = (titleRight
> (availableWidth - _contentTitle.maxWidth())); > (availableWidth - _contentTitle.maxWidth()));
p.setPen(st::windowActiveTextFg); p.setPen(st::windowActiveTextFg);
p.setPen(st::windowFg); p.setPen(st::windowFg);
{ {
const auto left = leftPadding; const auto left = leftPadding;
const auto top = hasSecondLineTitle const auto top = topPadding;
? (topPadding + _titleSt.font->height)
: topPadding;
_contentTitle.draw(p, { _contentTitle.draw(p, {
.position = QPoint(left, top), .position = QPoint(left, top),
.outerWidth = hasSecondLineTitle .outerWidth = hasSecondLineTitle
? availableWidth ? availableWidth
: (availableWidth - titleRight), : (availableWidth - titleRight),
.availableWidth = availableWidth, .availableWidth = availableWidth,
.elisionLines = 1, .elisionLines = hasSecondLineTitle ? 2 : 1,
}); });
} }
{ {
@ -94,6 +122,7 @@ void TopBarSuggestionContent::draw(QPainter &p) {
: availableWidth, : availableWidth,
}; };
}; };
p.setPen(st::windowSubTextFg);
_contentText.draw(p, { _contentText.draw(p, {
.position = QPoint(left, top), .position = QPoint(left, top),
.outerWidth = availableWidth, .outerWidth = availableWidth,

View file

@ -18,6 +18,12 @@ namespace Dialogs {
class TopBarSuggestionContent : public Ui::RippleButton { class TopBarSuggestionContent : public Ui::RippleButton {
public: public:
enum class RightIcon {
None,
Close,
Arrow,
};
TopBarSuggestionContent(not_null<Ui::RpWidget*>); TopBarSuggestionContent(not_null<Ui::RpWidget*>);
void setContent( void setContent(
@ -27,6 +33,7 @@ public:
[[nodiscard]] rpl::producer<int> desiredHeightValue() const override; [[nodiscard]] rpl::producer<int> desiredHeightValue() const override;
void setHideCallback(Fn<void()>); void setHideCallback(Fn<void()>);
void setRightIcon(RightIcon);
protected: protected:
void paintEvent(QPaintEvent *) override; void paintEvent(QPaintEvent *) override;
@ -44,8 +51,11 @@ private:
rpl::variable<int> _lastPaintedContentTop = 0; rpl::variable<int> _lastPaintedContentTop = 0;
base::unique_qptr<Ui::IconButton> _rightHide; base::unique_qptr<Ui::IconButton> _rightHide;
base::unique_qptr<Ui::IconButton> _rightArrow;
Fn<void()> _hideCallback; Fn<void()> _hideCallback;
RightIcon _rightIcon = RightIcon::None;
std::shared_ptr<Ui::DynamicImage> _rightPhoto; std::shared_ptr<Ui::DynamicImage> _rightPhoto;
QImage _rightPhotoImage; QImage _rightPhotoImage;