diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ed7f44130..27f129a39 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1868,6 +1868,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_gift_sent_subtitle" = "Gift for {user}"; "lng_action_gift_sent_text#one" = "{user} can display this gift on their page or convert it to {count} Star."; "lng_action_gift_sent_text#other" = "{user} can display this gift on their page or convert it to {count} Stars."; +"lng_action_gift_premium_months#one" = "{count} Month Premium"; +"lng_action_gift_premium_months#other" = "{count} Months Premium"; +"lng_action_gift_premium_about" = "Subscription for exclusive Telegram features."; "lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile."; "lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile."; "lng_action_suggested_photo_button" = "View Photo"; @@ -3014,6 +3017,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_send_message" = "Enter Message"; "lng_gift_send_anonymous" = "Hide My Name"; "lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message."; +"lng_gift_send_premium_about" = "Only {user} will see your message."; "lng_gift_send_button" = "Send a Gift for {cost}"; "lng_gift_sent_title" = "Gift Sent!"; "lng_gift_sent_about#one" = "You spent **{count}** Star from your balance."; diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 3f2b1a16e..ace233f7d 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -92,6 +92,7 @@ struct GiftsDescriptor { struct GiftDetails { GiftDescriptor descriptor; TextWithEntities text; + uint64 randomId = 0; bool anonymous = false; }; @@ -161,8 +162,6 @@ auto GenerateGiftMedia( Element *replacing, const GiftDetails &data) -> Fn)>)> { - Expects(v::is(data.descriptor)); - return [=](Fn)> push) { const auto &descriptor = data.descriptor; auto pushText = [&]( @@ -181,16 +180,8 @@ auto GenerateGiftMedia( }; const auto sticker = [=] { using Tag = ChatHelpers::StickerLottieSize; - const auto &session = parent->history()->session(); - auto &packs = session.giftBoxStickersPacks(); - packs.load(); - auto sticker = v::match(descriptor, [&](GiftTypePremium data) { - return packs.lookup(data.months); - }, [&](GiftTypeStars data) { - return data.document - ? data.document - : packs.lookup(packs.monthsForStars(data.stars)); - }); + const auto session = &parent->history()->session(); + const auto sticker = LookupGiftSticker(session, descriptor); return StickerInBubblePart::Data{ .sticker = sticker, .size = st::chatIntroStickerSize, @@ -203,15 +194,28 @@ auto GenerateGiftMedia( replacing, sticker, st::giftBoxPreviewStickerPadding)); - const auto title = tr::lng_action_gift_got_subtitle( - tr::now, - lt_user, - parent->data()->history()->session().user()->shortName()); - auto textFallback = tr::lng_action_gift_got_stars_text( - tr::now, - lt_count, - v::get(descriptor).convertStars, - Ui::Text::RichLangValue); + const auto title = v::match(descriptor, [&](GiftTypePremium gift) { + return tr::lng_action_gift_premium_months( + tr::now, + lt_count, + gift.months); + }, [&](const GiftTypeStars &gift) { + return tr::lng_action_gift_got_subtitle( + tr::now, + lt_user, + parent->history()->session().user()->shortName()); + }); + auto textFallback = v::match(descriptor, [&](GiftTypePremium gift) { + return tr::lng_action_gift_premium_about( + tr::now, + Ui::Text::RichLangValue); + }, [&](const GiftTypeStars &gift) { + return tr::lng_action_gift_got_stars_text( + tr::now, + lt_count, + gift.convertStars, + Ui::Text::RichLangValue); + }); auto description = data.text.empty() ? std::move(textFallback) : data.text; @@ -221,7 +225,7 @@ auto GenerateGiftMedia( st::giftBoxPreviewTextPadding, {}, Core::MarkedTextContext{ - .session = &parent->data()->history()->session(), + .session = &parent->history()->session(), .customEmojiRepaint = [parent] { parent->repaint(); }, }); }; @@ -260,24 +264,34 @@ PreviewWrap::PreviewWrap( void ShowSentToast( not_null window, - GiftTypeStars gift) { + const GiftDescriptor &descriptor) { const auto &st = st::historyPremiumToast; const auto skip = st.padding.top(); const auto size = st.style.font->height * 2; - const auto leftSkip = skip + size + skip - st.padding.left(); - const auto strong = window->showToast({ - .title = tr::lng_gift_sent_title(tr::now), - .text = tr::lng_gift_sent_about( + const auto document = LookupGiftSticker(&window->session(), descriptor); + const auto leftSkip = document + ? (skip + size + skip - st.padding.left()) + : 0; + auto text = v::match(descriptor, [&](const GiftTypePremium &gift) { + return tr::lng_action_gift_premium_about( + tr::now, + Ui::Text::RichLangValue); + }, [&](const GiftTypeStars &gift) { + return tr::lng_gift_sent_about( tr::now, lt_count, gift.stars, - Ui::Text::RichLangValue), + Ui::Text::RichLangValue); + }); + const auto strong = window->showToast({ + .title = tr::lng_gift_sent_title(tr::now), + .text = std::move(text), .padding = rpl::single(QMargins(leftSkip, 0, 0, 0)), .st = &st, .attach = RectPart::Top, .duration = kSentToastDuration, }).get(); - if (!strong) { + if (!strong || !document) { return; } const auto widget = strong->widget(); @@ -286,7 +300,6 @@ void ShowSentToast( preview->resize(size, size); preview->show(); - const auto document = gift.document; const auto bytes = document->createMediaView()->bytes(); const auto filepath = document->filepath(); const auto ratio = style::DevicePixelRatio(); @@ -807,14 +820,38 @@ struct GiftPriceTabs { return field; } +void SendGift( + not_null window, + not_null peer, + std::shared_ptr api, + const GiftDetails &details, + Fn done) { + v::match(details.descriptor, [&](const GiftTypePremium &gift) { + auto invoice = api->invoice(1, gift.months); + invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ + .users = { peer->asUser() }, + .message = details.text, + }; + Payments::CheckoutProcess::Start(std::move(invoice), done); + }, [&](const GiftTypeStars &gift) { + const auto processNonPanelPaymentFormFactory + = Payments::ProcessNonPanelPaymentFormFactory(window, done); + Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{ + .giftId = gift.id, + .randomId = details.randomId, + .message = details.text, + .user = peer->asUser(), + .anonymous = details.anonymous, + }, done, processNonPanelPaymentFormFactory); + }); +} + void SendGiftBox( not_null box, not_null window, not_null peer, + std::shared_ptr api, const GiftDescriptor &descriptor) { - Expects(v::is(descriptor)); - - const auto gift = v::get(descriptor); box->setStyle(st::giftBox); box->setWidth(st::boxWideWidth); box->setTitle(tr::lng_gift_send_title()); @@ -841,15 +878,17 @@ void SendGiftBox( struct State { rpl::variable details; std::shared_ptr media; - uint64 randomId = 0; bool submitting = false; }; const auto state = box->lifetime().make_state(); state->details = GiftDetails{ .descriptor = descriptor, + .randomId = base::RandomValue(), }; - state->media = gift.document->createMediaView(); - state->media->checkStickerLarge(); + const auto document = LookupGiftSticker(&window->session(), descriptor); + if ((state->media = document ? document->createMediaView() : nullptr)) { + state->media->checkStickerLarge(); + } const auto container = box->verticalLayout(); container->add(object_ptr( @@ -901,25 +940,33 @@ void SendGiftBox( &window->session(), { .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow }); - AddDivider(container); - AddSkip(container); - container->add( - object_ptr( - container, - tr::lng_gift_send_anonymous(), - st::settingsButtonNoIcon) - )->toggleOn(rpl::single(false))->toggledValue( - ) | rpl::start_with_next([=](bool toggled) { - auto now = state->details.current(); - now.anonymous = toggled; - state->details = std::move(now); - }, container->lifetime()); - AddSkip(container); - AddDividerText(container, tr::lng_gift_send_anonymous_about( - lt_user, - rpl::single(peer->shortName()), - lt_recipient, - rpl::single(peer->shortName()))); + if (v::is(descriptor)) { + AddDivider(container); + AddSkip(container); + container->add( + object_ptr( + container, + tr::lng_gift_send_anonymous(), + st::settingsButtonNoIcon) + )->toggleOn(rpl::single(false))->toggledValue( + ) | rpl::start_with_next([=](bool toggled) { + auto now = state->details.current(); + now.anonymous = toggled; + state->details = std::move(now); + }, container->lifetime()); + AddSkip(container); + } + v::match(descriptor, [&](const GiftTypePremium &) { + AddDividerText(container, tr::lng_gift_send_premium_about( + lt_user, + rpl::single(peer->shortName()))); + }, [&](const GiftTypeStars &) { + AddDividerText(container, tr::lng_gift_send_anonymous_about( + lt_user, + rpl::single(peer->shortName()), + lt_recipient, + rpl::single(peer->shortName()))); + }); const auto buttonWidth = st::boxWideWidth - st::giftBox.buttonPadding.left() @@ -929,26 +976,19 @@ void SendGiftBox( return; } state->submitting = true; - state->randomId = base::RandomValue(); const auto details = state->details.current(); const auto weak = Ui::MakeWeak(box); const auto done = [=](Payments::CheckoutResult result) { if (result == Payments::CheckoutResult::Paid) { const auto copy = state->media; window->showPeerHistory(peer); - ShowSentToast(window, gift); + ShowSentToast(window, descriptor); } if (const auto strong = weak.data()) { box->closeBox(); } }; - Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{ - .giftId = gift.id, - .randomId = state->randomId, - .message = details.text, - .user = peer->asUser(), - .anonymous = details.anonymous, - }, done, Payments::ProcessNonPanelPaymentFormFactory(window, done)); + SendGift(window, peer, api, details, done); }); SetButtonMarkedLabel( button, @@ -967,19 +1007,6 @@ void SendGiftBox( }, button->lifetime()); } -void SendPremiumGift( - not_null window, - not_null peer, - std::shared_ptr api, - const GiftTypePremium &gift, - Fn done) { - auto invoice = api->invoice(1, gift.months); - invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ - { peer->asUser() } - }; - Payments::CheckoutProcess::Start(std::move(invoice), done); -} - [[nodiscard]] object_ptr MakeGiftsList( not_null window, not_null peer, @@ -999,6 +1026,13 @@ void SendPremiumGift( const auto shadow = st::defaultDropdownMenu.wrap.shadow; const auto extend = shadow.extend; + auto &packs = window->session().giftBoxStickersPacks(); + packs.updated() | rpl::start_with_next([=] { + for (const auto &button : state->buttons) { + button->update(); + } + }, raw->lifetime()); + std::move( gifts ) | rpl::start_with_next([=](const GiftsDescriptor &gifts) { @@ -1049,14 +1083,15 @@ void SendPremiumGift( } else { state->sending = true; } - SendPremiumGift( + SendGift( window, peer, api, - v::get(descriptor), + GiftDetails{ descriptor }, premiumSent); } else { - window->show(Box(SendGiftBox, window, peer, descriptor)); + window->show( + Box(SendGiftBox, window, peer, api, descriptor)); } }); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.cpp index f510540c2..a4c98c416 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.cpp @@ -22,6 +22,10 @@ GiftBoxPack::GiftBoxPack(not_null session) GiftBoxPack::~GiftBoxPack() = default; +rpl::producer<> GiftBoxPack::updated() const { + return _updated.events(); +} + int GiftBoxPack::monthsForStars(int stars) const { if (stars <= 1000) { return 3; @@ -112,6 +116,7 @@ void GiftBoxPack::applySet(const MTPDmessages_stickerSet &data) { } }); } + _updated.fire({}); } } // namespace Stickers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.h b/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.h index 1e6c0e934..798259c25 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_gift_box_pack.h @@ -28,6 +28,7 @@ public: [[nodiscard]] int monthsForStars(int stars) const; [[nodiscard]] DocumentData *lookup(int months) const; [[nodiscard]] Data::FileOrigin origin() const; + [[nodiscard]] rpl::producer<> updated() const; private: using SetId = uint64; @@ -36,6 +37,7 @@ private: const not_null _session; const std::vector _localMonths; + rpl::event_stream<> _updated; std::vector _documents; SetId _setId = 0; uint64 _accessHash = 0; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 62a8e933b..440cffcda 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -463,10 +463,6 @@ bool UserData::canAddContact() const { return canShareThisContact() && !isContact(); } -bool UserData::canReceiveGifts() const { - return flags() & UserDataFlag::CanReceiveGifts; -} - bool UserData::canShareThisContactFast() const { return !_phone.isEmpty(); } @@ -584,14 +580,10 @@ 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 | Flag::VoiceMessagesForbidden | Flag::ReadDatesPrivate @@ -602,7 +594,6 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { ? 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()) | (update.is_voice_messages_forbidden() diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index e84d02ac0..f016a9d4d 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -67,7 +67,7 @@ enum class UserDataFlag : uint32 { DiscardMinPhoto = (1 << 12), Self = (1 << 13), Premium = (1 << 14), - CanReceiveGifts = (1 << 15), + //CanReceiveGifts = (1 << 15), VoiceMessagesForbidden = (1 << 16), PersonalPhoto = (1 << 17), StoriesHidden = (1 << 18), @@ -146,8 +146,6 @@ 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/history/view/media/history_view_premium_gift.cpp b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp index a1afbe051..e12ab3d3f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp @@ -43,7 +43,7 @@ PremiumGift::PremiumGift( PremiumGift::~PremiumGift() = default; int PremiumGift::top() { - return st::msgServiceGiftBoxStickerTop; + return starGift() ? 0 : st::msgServiceGiftBoxStickerTop; } QSize PremiumGift::size() { @@ -66,7 +66,7 @@ QString PremiumGift::title() { return tr::lng_gift_stars_title(tr::now, lt_count, count); } return gift() - ? tr::lng_premium_summary_title(tr::now) + ? tr::lng_action_gift_premium_months(tr::now, lt_count, _data.count) : _data.unclaimed ? tr::lng_prize_unclaimed_title(tr::now) : tr::lng_prize_title(tr::now); @@ -99,10 +99,14 @@ TextWithEntities PremiumGift::subtitle() { tr::now, lt_user, Ui::Text::Bold(_parent->history()->peer->shortName()), - Ui::Text::WithEntities) + Ui::Text::RichLangValue) : tr::lng_gift_stars_incoming(tr::now, Ui::Text::WithEntities); } else if (gift()) { - return { GiftDuration(_data.count) }; + return !_data.message.empty() + ? _data.message + : tr::lng_action_gift_premium_about( + tr::now, + Ui::Text::RichLangValue); } const auto name = _data.channel ? _data.channel->name() : "channel"; auto result = (_data.unclaimed diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp index dc236146a..45b26cd35 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp @@ -109,6 +109,10 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) { _button = QRect(skipx, skipy, outer, inner.height()); } +bool GiftButton::documentResolved() const { + return _player || _mediaLifetime; +} + void GiftButton::setDocument(not_null document) { const auto media = document->createMediaView(); media->checkStickerLarge(); @@ -159,6 +163,12 @@ void GiftButton::resizeEvent(QResizeEvent *e) { } void GiftButton::paintEvent(QPaintEvent *e) { + if (!documentResolved()) { + if (const auto document = _delegate->lookupSticker(_descriptor)) { + setDocument(document); + } + } + auto p = QPainter(this); const auto hidden = v::is(_descriptor) && v::get(_descriptor).hidden;; @@ -399,8 +409,17 @@ QImage Delegate::background() { } DocumentData *Delegate::lookupSticker(const GiftDescriptor &descriptor) { - const auto &session = _window->session(); - auto &packs = session.giftBoxStickersPacks(); + return LookupGiftSticker(&_window->session(), descriptor); +} + +not_null Delegate::hiddenMark() { + return _hiddenMark.get(); +} + +DocumentData *LookupGiftSticker( + not_null session, + const GiftDescriptor &descriptor) { + auto &packs = session->giftBoxStickersPacks(); packs.load(); return v::match(descriptor, [&](GiftTypePremium data) { return packs.lookup(data.months); @@ -411,8 +430,4 @@ DocumentData *Delegate::lookupSticker(const GiftDescriptor &descriptor) { }); } -not_null Delegate::hiddenMark() { - return _hiddenMark.get(); -} - } // namespace Info::PeerGifts diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h index b8188454d..5c63e8885 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h @@ -16,6 +16,10 @@ namespace HistoryView { class StickerPlayer; } // namespace HistoryView +namespace Main { +class Session; +} // namespace Main + namespace Ui { class DynamicImage; } // namespace Ui @@ -86,6 +90,8 @@ private: void resizeEvent(QResizeEvent *e) override; void setDocument(not_null document); + [[nodiscard]] bool documentResolved() const; + void unsubscribe(); const not_null _delegate; @@ -126,4 +132,8 @@ private: }; +[[nodiscard]] DocumentData *LookupGiftSticker( + not_null session, + const GiftDescriptor &descriptor); + } // namespace Info::PeerGifts diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index 098b0aae9..0a364a9b1 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -143,6 +143,7 @@ struct InvoicePremiumGiftCodeGiveaway { struct InvoicePremiumGiftCodeUsers { std::vector> users; ChannelData *boostPeer = nullptr; + TextWithEntities message; }; struct InvoicePremiumGiftCode { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 00df587bc..4745fa377 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1231,7 +1231,6 @@ void Filler::addGiftPremium() { || user->isSelf() || user->isBot() || user->isNotificationsUser() - || !user->canReceiveGifts() || user->isRepliesChat() || user->isVerifyCodes() || !user->session().premiumCanBuy()) { @@ -1240,7 +1239,7 @@ void Filler::addGiftPremium() { const auto navigation = _controller; _addAction(tr::lng_profile_gift_premium(tr::now), [=] { - Ui::ChooseStarGiftRecipient(navigation); + Ui::ShowStarGiftBox(navigation, user); }, &st::menuIconGiftPremium); }