From c7c8ebed1391fce383e8da65b160e6b15248071f Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 3 Jul 2022 17:56:12 +0300 Subject: [PATCH] Added initial box for premium gifts. --- Telegram/CMakeLists.txt | 2 + .../Resources/icons/menu/gift_premium.png | Bin 0 -> 799 bytes .../Resources/icons/menu/gift_premium@2x.png | Bin 0 -> 1523 bytes .../Resources/icons/menu/gift_premium@3x.png | Bin 0 -> 2245 bytes Telegram/Resources/langs/lang.strings | 16 +- .../SourceFiles/boxes/gift_premium_box.cpp | 298 ++++++++++++++++++ Telegram/SourceFiles/boxes/gift_premium_box.h | 31 ++ Telegram/SourceFiles/data/data_user.cpp | 9 + Telegram/SourceFiles/data/data_user.h | 3 + Telegram/SourceFiles/ui/effects/premium.style | 17 + Telegram/SourceFiles/ui/menu_icons.style | 1 + .../SourceFiles/window/window_peer_menu.cpp | 20 ++ .../window/window_session_controller.cpp | 11 +- .../window/window_session_controller.h | 4 + 14 files changed, 407 insertions(+), 5 deletions(-) create mode 100644 Telegram/Resources/icons/menu/gift_premium.png create mode 100644 Telegram/Resources/icons/menu/gift_premium@2x.png create mode 100644 Telegram/Resources/icons/menu/gift_premium@3x.png create mode 100644 Telegram/SourceFiles/boxes/gift_premium_box.cpp create mode 100644 Telegram/SourceFiles/boxes/gift_premium_box.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index a9daf106e2..e698cbb9be 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -240,6 +240,8 @@ PRIVATE boxes/edit_color_box.h boxes/edit_privacy_box.cpp boxes/edit_privacy_box.h + boxes/gift_premium_box.cpp + boxes/gift_premium_box.h boxes/language_box.cpp boxes/language_box.h boxes/local_storage_box.cpp diff --git a/Telegram/Resources/icons/menu/gift_premium.png b/Telegram/Resources/icons/menu/gift_premium.png new file mode 100644 index 0000000000000000000000000000000000000000..cb18442df92a93f893fe2dfdaa36f1ea648ec9b5 GIT binary patch literal 799 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 + +GiftOptions GiftOptionFromTL( + const MTPDuserFull &data, + int monthlyAmount, + QString monthlyCurrency) { + auto result = GiftOptions(); + const auto gifts = data.vpremium_gifts(); + if (!gifts) { + return result; + } + result.reserve(gifts->v.size()); + for (const auto &gift : gifts->v) { + const auto &option = gift.match([](const MTPDpremiumGiftOption &d + ) -> const MTPDpremiumGiftOption & { + return d; + }); + const auto botUrl = qs(option.vbot_url()); + const auto months = option.vmonths().v; + const auto amount = option.vamount().v; + const auto currency = qs(option.vcurrency()); + if (monthlyCurrency != currency) { + monthlyAmount = 500; // 5 USD. + } + const auto discount = [&] { + const auto percent = monthlyAmount * months / float64(amount) + - 1.; + return std::round(percent * 100. / kDiscountDivider) + * kDiscountDivider; + }(); + auto info = Ui::Premium::GiftInfo{ + .duration = Ui::FormatTTL(months * 86400 * 31), + .discount = QString("\u2212%1%").arg(discount), + .perMonth = tr::lng_premium_gift_per( + tr::now, + lt_cost, + Ui::FillAmountAndCurrency( + amount / float64(months), + currency)), + .total = Ui::FillAmountAndCurrency(amount, currency), + }; + result.push_back({ .url = botUrl, .info = std::move(info) }); + } + return result; +} + +void GiftBox( + not_null box, + not_null controller, + not_null user, + GiftOptions options) { + const auto boxWidth = st::boxWideWidth; + box->setWidth(boxWidth); + box->setNoContentMargin(true); + const auto buttonsParent = box->verticalLayout().get(); + + struct State { + rpl::event_stream buttonText; + }; + const auto state = box->lifetime().make_state(); + + const auto userpicPadding = st::premiumGiftUserpicPadding; + const auto top = box->addRow(object_ptr( + buttonsParent, + userpicPadding.top() + + userpicPadding.bottom() + + st::defaultUserpicButton.size.height())); + + const auto userpic = Ui::CreateChild( + top, + user, + Ui::UserpicButton::Role::Custom, + st::defaultUserpicButton); + userpic->setAttribute(Qt::WA_TransparentForMouseEvents); + top->widthValue( + ) | rpl::start_with_next([=](int width) { + userpic->moveToLeft( + (width - userpic->width()) / 2, + userpicPadding.top()); + }, userpic->lifetime()); + + const auto close = Ui::CreateChild( + buttonsParent, + st::infoTopBarClose); + close->setClickedCallback([=] { box->closeBox(); }); + + buttonsParent->widthValue( + ) | rpl::start_with_next([=](int width) { + close->moveToRight(0, 0, width); + }, close->lifetime()); + + // Header. + const auto &padding = st::premiumGiftAboutPadding; + const auto available = boxWidth - padding.left() - padding.right(); + const auto &stTitle = st::premiumPreviewAboutTitle; + auto titleLabel = object_ptr( + box, + tr::lng_premium_gift_title(), + stTitle); + titleLabel->resizeToWidth(available); + box->addRow( + object_ptr>( + box, + std::move(titleLabel)), + st::premiumGiftTitlePadding); + + auto textLabel = object_ptr( + box, + tr::lng_premium_gift_about( + lt_user, + user->session().changes().peerFlagsValue( + user, + Data::PeerUpdate::Flag::Name + ) | rpl::map([=] { return TextWithEntities{ user->firstName }; }), + Ui::Text::RichLangValue), + st::premiumPreviewAbout); + textLabel->setTextColorOverride(stTitle.textFg->c); + textLabel->resizeToWidth(available); + box->addRow( + object_ptr>(box, std::move(textLabel)), + padding); + + // List. + const auto group = std::make_shared(); + group->setChangedCallback([=](int value) { + Expects(value < options.size() && value >= 0); + auto text = tr::lng_premium_gift_button( + tr::now, + lt_cost, + options[value].info.total); + state->buttonText.fire(std::move(text)); + }); + Ui::Premium::AddGiftOptions( + buttonsParent, + group, + ranges::views::all( + options + ) | ranges::views::transform([](const GiftOption &option) { + return option.info; + }) | ranges::to_vector); + + // Footer. + auto terms = object_ptr( + box, + tr::lng_premium_gift_terms( + lt_link, + tr::lng_premium_gift_terms_link( + ) | rpl::map([=](const QString &t) { + return Ui::Text::Link(t, 1); + }), + Ui::Text::WithEntities), + st::premiumGiftTerms); + terms->setLink(1, std::make_shared([=] { + box->closeBox(); + Settings::ShowPremium(&user->session(), QString()); + })); + terms->resizeToWidth(available); + box->addRow( + object_ptr>(box, std::move(terms)), + st::premiumGiftTermsPadding); + + // Button. + const auto &stButton = st::premiumGiftBox; + box->setStyle(stButton); + auto raw = Settings::CreateSubscribeButton({ + controller, + box, + [] { return QString("gift"); }, + state->buttonText.events(), + Ui::Premium::GiftGradientStops(), + }); + auto button = object_ptr::fromRaw(raw); + button->resizeToWidth(boxWidth + - stButton.buttonPadding.left() + - stButton.buttonPadding.right()); + button->setClickedCallback([=] { + const auto value = group->value(); + Assert(value < options.size() && value >= 0); + + const auto local = Core::TryConvertUrlToLocal(options[value].url); + if (local.isEmpty()) { + return; + } + UrlClickHandler::Open( + local, + QVariant::fromValue(ClickHandlerContext{ + .botStartAutoSubmit = true, + .sessionWindow = base::make_weak(controller.get()), + })); + }); + box->setShowFinishedCallback([raw = button.data()]{ + raw->startGlareAnimation(); + }); + box->addButton(std::move(button)); + + group->setValue(0); + + Data::PeerPremiumValue( + user + ) | rpl::skip(1) | rpl::start_with_next([=] { + box->closeBox(); + }, box->lifetime()); +} + +} // namespace + +GiftPremiumValidator::GiftPremiumValidator( + not_null controller) +: _controller(controller) +, _api(&_controller->session().mtp()) { +} + +void GiftPremiumValidator::cancel() { + _requestId = 0; +} + +void GiftPremiumValidator::showBox(not_null user) { + if (_requestId) { + return; + } + _controller->session().api().premium().reload(); + _requestId = _api.request(MTPusers_GetFullUser( + user->inputUser + )).done([=](const MTPusers_UserFull &result) { + if (!_requestId) { + // Canceled. + return; + } + _requestId = 0; +// _controller->api().processFullPeer(peer, result); + const auto &data = result.match([]( + const MTPDusers_userFull &d) -> const MTPDusers_userFull & { + return d; + }); + _controller->session().data().processUsers(data.vusers()); + _controller->session().data().processChats(data.vchats()); + + const auto &fullUser = data.vfull_user().match( + [](const MTPDuserFull &d) -> const MTPDuserFull & { + return d; + }); + auto options = GiftOptionFromTL( + fullUser, + _controller->session().api().premium().monthlyAmount(), + _controller->session().api().premium().monthlyCurrency()); + if (!options.empty()) { + _controller->show( + Box(GiftBox, _controller, user, std::move(options))); + } + }).fail([=] { + _requestId = 0; + }).send(); +} diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.h b/Telegram/SourceFiles/boxes/gift_premium_box.h new file mode 100644 index 0000000000..cc11b23886 --- /dev/null +++ b/Telegram/SourceFiles/boxes/gift_premium_box.h @@ -0,0 +1,31 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "mtproto/sender.h" + +class UserData; + +namespace Window { +class SessionController; +} // namespace Window + +class GiftPremiumValidator final { +public: + GiftPremiumValidator(not_null controller); + + void showBox(not_null user); + void cancel(); + +private: + const not_null _controller; + MTP::Sender _api; + + mtpRequestId _requestId = 0; + +}; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 1e1a7c38d1..51bce614ab 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -253,6 +253,10 @@ bool UserData::canAddContact() const { return canShareThisContact() && !isContact(); } +bool UserData::canReceiveGifts() const { + return flags() & UserDataFlag::CanReceiveGifts; +} + bool UserData::canShareThisContactFast() const { return !_phone.isEmpty(); } @@ -309,14 +313,19 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(user, pinned->v); } + const auto canReceiveGifts = (update.vflags().v + & MTPDuserFull::Flag::f_premium_gifts) + && update.vpremium_gifts(); using Flag = UserDataFlag; const auto mask = Flag::Blocked | Flag::HasPhoneCalls | Flag::PhoneCallsPrivate + | Flag::CanReceiveGifts | Flag::CanPinMessages; user->setFlags((user->flags() & ~mask) | (update.is_phone_calls_private() ? Flag::PhoneCallsPrivate : Flag()) | (update.is_phone_calls_available() ? Flag::HasPhoneCalls : Flag()) + | (canReceiveGifts ? Flag::CanReceiveGifts : Flag()) | (update.is_can_pin_message() ? Flag::CanPinMessages : Flag()) | (update.is_blocked() ? Flag::Blocked : Flag())); user->setIsBlocked(update.is_blocked()); diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 1528f4b087..cabd43681d 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -51,6 +51,7 @@ enum class UserDataFlag { DiscardMinPhoto = (1 << 12), Self = (1 << 13), Premium = (1 << 14), + CanReceiveGifts = (1 << 15), }; inline constexpr bool is_flag_type(UserDataFlag) { return true; }; using UserDataFlags = base::flags; @@ -106,6 +107,8 @@ public: [[nodiscard]] bool canShareThisContact() const; [[nodiscard]] bool canAddContact() const; + [[nodiscard]] bool canReceiveGifts() const; + // In Data::Session::processUsers() we check only that. // When actually trying to share contact we perform // a full check by canShareThisContact() call. diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index fbaea38341..24ab0663e3 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ using "ui/basic.style"; using "ui/widgets/widgets.style"; +using "settings/settings.style"; // Preview. premiumPreviewBox: Box(defaultBox) { @@ -96,6 +97,7 @@ premiumAccountsNameTop: 13px; premiumAccountsPadding: margins(0px, 20px, 0px, 14px); premiumAccountsHeight: 105px; +// Gift. premiumGiftRowHeight: 56px; premiumGiftRowBorderWidth: 2px; premiumGiftRowBorderRadius: 9px; @@ -107,3 +109,18 @@ premiumGiftRowBadgeHeight: 18px; premiumGiftRowBadgeRadius: 4px; premiumGiftRowBadgeMargins: margins(5px, 1px, 5px, 0px); +premiumGiftUserpicPadding: margins(10px, 27px, 18px, 13px); +premiumGiftTitlePadding: margins(18px, 0px, 18px, 0px); +premiumGiftAboutPadding: margins(18px, 5px, 18px, 23px); +premiumGiftTermsPadding: margins(18px, 27px, 18px, 0px); + +premiumGiftTerms: FlatLabel(settingLocalPasscodeDescription) { + style: TextStyle(defaultTextStyle) { + font: font(11px); + linkFont: font(11px); + linkFontOver: font(11px underline); + } +} +premiumGiftBox: Box(premiumPreviewBox) { + buttonPadding: margins(12px, 12px, 12px, 12px); +} diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style index 08e360f827..10a503c139 100644 --- a/Telegram/SourceFiles/ui/menu_icons.style +++ b/Telegram/SourceFiles/ui/menu_icons.style @@ -87,6 +87,7 @@ menuIconFile: icon {{ "menu/file", menuIconColor }}; menuIconPhoto: icon {{ "menu/image", menuIconColor }}; menuIconAddToFolder: icon {{ "menu/add_to_folder", menuIconColor }}; menuIconLeave: icon {{ "menu/leave", menuIconColor }}; +menuIconGiftPremium: icon {{ "menu/gift_premium", menuIconColor }}; menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }}; menuIconTTLAnyTextPosition: point(11px, 22px); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 66908d7247..d9210d99a5 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -202,6 +202,7 @@ private: void addNewMembers(); void addDeleteContact(); void addTTLSubmenu(bool addSeparator); + void addGiftPremium(); not_null _controller; Dialogs::EntryState _request; @@ -806,6 +807,24 @@ void Filler::addTTLSubmenu(bool addSeparator) { } } +void Filler::addGiftPremium() { + const auto user = _peer->asUser(); + if (!user + || user->isInaccessible() + || user->isSelf() + || user->isBot() + || user->isNotificationsUser() + || !user->canReceiveGifts() + || user->isRepliesChat()) { + return; + } + + const auto navigation = _controller; + _addAction(tr::lng_profile_gift_premium(tr::now), [=] { + navigation->showGiftPremiumBox(user); + }, &st::menuIconGiftPremium); +} + void Filler::fill() { if (_folder) { fillArchiveActions(); @@ -862,6 +881,7 @@ void Filler::fillProfileActions() { addNewContact(); addShareContact(); addEditContact(); + addGiftPremium(); addBotToGroup(); addNewMembers(); addManageChat(); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 8b6b2512b1..40a5961ff6 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -617,7 +617,8 @@ SessionController::SessionController( , _activeChatsFilter(session->data().chatsFilters().defaultId()) , _defaultChatTheme(std::make_shared()) , _chatStyle(std::make_unique()) -, _cachedReactionIconFactory(std::make_unique()) { +, _cachedReactionIconFactory(std::make_unique()) +, _giftPremiumValidator(GiftPremiumValidator(this)) { init(); _chatStyleTheme = _defaultChatTheme; @@ -748,6 +749,14 @@ void SessionController::showEditPeerBox(PeerData *peer) { session().api().requestFullPeer(peer); } +void SessionController::showGiftPremiumBox(UserData *user) { + if (user) { + _giftPremiumValidator.showBox(user); + } else { + _giftPremiumValidator.cancel(); + } +} + void SessionController::init() { if (session().supportMode()) { initSupportMode(); diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index f6a6733ea8..bd696e229e 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/observer.h" #include "base/weak_ptr.h" #include "base/timer.h" +#include "boxes/gift_premium_box.h" // GiftPremiumValidator. #include "data/data_chat_participant_status.h" #include "dialogs/dialogs_key.h" #include "ui/layers/layer_widget.h" @@ -354,6 +355,7 @@ public: Dialogs::RowDescriptor from = {}) const; void showEditPeerBox(PeerData *peer); + void showGiftPremiumBox(UserData *user); void enableGifPauseReason(GifPauseReason reason); void disableGifPauseReason(GifPauseReason reason); @@ -599,6 +601,8 @@ private: using ReactionIconFactory = HistoryView::Reactions::CachedIconFactory; std::unique_ptr _cachedReactionIconFactory; + GiftPremiumValidator _giftPremiumValidator; + QString _premiumRef; rpl::lifetime _lifetime;