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_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" = "Send";

View file

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

View file

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

View file

@ -813,3 +813,10 @@ dialogsSponsoredButton: DialogRightButton(dialogRowOpenBot) {
}
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 "api/api_premium.h"
#include "apiwrap.h"
#include "base/call_delayed.h"
#include "core/application.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 "main/main_app_config.h"
#include "main/main_session.h"
#include "settings/settings_premium.h"
#include "ui/text/text_utilities.h"
#include "ui/wrap/slide_wrap.h"
#include "window/window_controller.h"
@ -32,6 +35,7 @@ namespace {
}
constexpr auto kSugSetBirthday = "BIRTHDAY_SETUP"_cs;
constexpr auto kSugPremiumAnnual = "PREMIUM_ANNUAL"_cs;
} // namespace
@ -46,12 +50,14 @@ object_ptr<Ui::SlideWrap<Ui::RpWidget>> CreateTopBarSuggestion(
wrap->toggle(false, anim::type::instant);
struct State {
rpl::lifetime birthdayLifetime;
rpl::lifetime premiumLifetime;
};
const auto state = content->lifetime().make_state<State>();
const auto processCurrentSuggestion = [=](auto repeat) -> void {
if (session->appConfig().suggestionCurrent(kSugSetBirthday.utf8())
&& !Data::IsBirthdayToday(session->user()->birthday())) {
content->setRightIcon(TopBarSuggestionContent::RightIcon::Close);
content->setClickedCallback([=] {
const auto controller = FindSessionController(parent);
if (!controller) {
@ -86,6 +92,46 @@ object_ptr<Ui::SlideWrap<Ui::RpWidget>> CreateTopBarSuggestion(
tr::now,
TextWithEntities::Simple));
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 {
wrap->toggle(false, anim::type::normal);
base::call_delayed(st::slideWrapDuration * 2, wrap, [=] {

View file

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

View file

@ -301,7 +301,7 @@ private:
base::Timer _chooseByDragTimer;
const Layout _layout = Layout::Main;
int _narrowWidth = 0;
const int _narrowWidth = 0;
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
*/
#include "dialogs/ui/dialogs_top_bar_suggestion_content.h"
#include "ui/ui_rpl_filter.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_dialogs.h"
#include "styles/style_settings.h"
namespace Dialogs {
namespace {
} // namespace
TopBarSuggestionContent::TopBarSuggestionContent(not_null<Ui::RpWidget*> p)
: Ui::RippleButton(p, st::defaultRippleAnimationBgOver)
, _titleSt(st::semiboldTextStyle)
, _contentTitleSt(st::semiboldTextStyle)
, _contentTextSt(st::defaultTextStyle)
, _rightHide(
base::make_unique_q<Ui::IconButton>(
, _contentTitleSt(st::dialogsTopBarSuggestionTitleStyle)
, _contentTextSt(st::dialogsTopBarSuggestionAboutStyle) {
setRightIcon(RightIcon::Close);
}
void TopBarSuggestionContent::setRightIcon(RightIcon icon) {
if (icon == _rightIcon) {
return;
}
_rightHide = nullptr;
_rightArrow = nullptr;
_rightIcon = icon;
if (icon == RightIcon::Close) {
_rightHide = base::make_unique_q<Ui::IconButton>(
this,
st::dialogsCancelSearchInPeer)) {
st::dialogsCancelSearchInPeer);
const auto rightHide = _rightHide.get();
sizeValue() | rpl::start_with_next([=](const QSize &s) {
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) {
@ -41,28 +71,26 @@ void TopBarSuggestionContent::draw(QPainter &p) {
const auto rightPadding = st::msgReplyBarSkip;
const auto topPadding = st::msgReplyPadding.top();
const auto availableWidthNoPhoto = r.width()
- (_rightArrow ? _rightArrow->width() / 2 : 0) // Takes full height.
- leftPadding
- rightPadding;
const auto availableWidth = availableWidthNoPhoto
- (_rightHide ? _rightHide->width() : 0);
const auto titleRight = leftPadding
+ _titleSt.font->spacew * 2;
const auto titleRight = leftPadding;
const auto hasSecondLineTitle = (titleRight
> (availableWidth - _contentTitle.maxWidth()));
p.setPen(st::windowActiveTextFg);
p.setPen(st::windowFg);
{
const auto left = leftPadding;
const auto top = hasSecondLineTitle
? (topPadding + _titleSt.font->height)
: topPadding;
const auto top = topPadding;
_contentTitle.draw(p, {
.position = QPoint(left, top),
.outerWidth = hasSecondLineTitle
? availableWidth
: (availableWidth - titleRight),
.availableWidth = availableWidth,
.elisionLines = 1,
.elisionLines = hasSecondLineTitle ? 2 : 1,
});
}
{
@ -94,6 +122,7 @@ void TopBarSuggestionContent::draw(QPainter &p) {
: availableWidth,
};
};
p.setPen(st::windowSubTextFg);
_contentText.draw(p, {
.position = QPoint(left, top),
.outerWidth = availableWidth,

View file

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