From 4840a9094b5eaf45264d6760d844c2a47ecd46f3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Jun 2025 22:30:55 +0400 Subject: [PATCH] Check amounts of stars/TON. --- Telegram/Resources/langs/lang.strings | 5 + Telegram/SourceFiles/api/api_suggest_post.cpp | 24 ++-- Telegram/SourceFiles/core/credits_amount.h | 6 +- .../SourceFiles/data/components/credits.cpp | 56 +++++++- .../SourceFiles/data/components/credits.h | 19 ++- Telegram/SourceFiles/data/data_channel.cpp | 8 +- Telegram/SourceFiles/data/data_channel.h | 8 ++ Telegram/SourceFiles/data/data_session.cpp | 23 +-- Telegram/SourceFiles/data/data_user.cpp | 2 + .../history/history_item_helpers.cpp | 130 +++++++++++++---- .../history/history_item_helpers.h | 17 ++- .../SourceFiles/history/history_widget.cpp | 51 +++---- Telegram/SourceFiles/history/history_widget.h | 4 +- .../controls/history_view_suggest_options.cpp | 134 ++++++++++++++++-- .../controls/history_view_suggest_options.h | 7 +- .../view/history_view_chat_section.cpp | 18 +-- .../history/view/history_view_chat_section.h | 2 +- .../inline_bots/bot_attach_web_view.cpp | 4 +- .../media/stories/media_stories_reply.cpp | 39 ++--- .../media/stories/media_stories_reply.h | 2 +- .../settings/settings_credits_graphics.cpp | 9 ++ .../settings/settings_credits_graphics.h | 6 +- Telegram/SourceFiles/ui/chat/chat.style | 8 ++ .../SourceFiles/window/window_peer_menu.cpp | 42 +++--- 24 files changed, 474 insertions(+), 150 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 10eaf2cf9f..078d50f4ab 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2911,6 +2911,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts."; "lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}."; "lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages."; +"lng_credits_small_balance_for_suggest" = "Buy **Stars** to suggest post to {channel}."; "lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram."; "lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars."; "lng_credits_enough" = "You have enough stars at the moment. {link}"; @@ -4478,6 +4479,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_suggest_warn_text_stars" = "You won't receive **Stars** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published."; "lng_suggest_warn_text_ton" = "You won't receive **TON** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published."; "lng_suggest_warn_delete_anyway" = "Delete Anyway"; +"lng_suggest_low_ton_title" = "{amount} TON Needed"; +"lng_suggest_low_ton_text" = "Buy **TON** to suggest message to {channel} and others."; +"lng_suggest_low_ton_fragment" = "Buy on Fragment"; +"lng_suggest_low_ton_fragment_url" = "https://fragment.com/ads/topup"; "lng_reply_in_another_title" = "Reply in..."; "lng_reply_in_another_chat" = "Reply in Another Chat"; diff --git a/Telegram/SourceFiles/api/api_suggest_post.cpp b/Telegram/SourceFiles/api/api_suggest_post.cpp index b28699d618..a4d6dbf366 100644 --- a/Telegram/SourceFiles/api/api_suggest_post.cpp +++ b/Telegram/SourceFiles/api/api_suggest_post.cpp @@ -198,18 +198,8 @@ void SendSuggest( SendSuggest(show, item, state, modify, done, stars); } }; - const auto checked = state->sendPayment.check( - show, - item->history()->peer, - 1, - starsApproved, - withPaymentApproved); - if (!checked) { - return; - } const auto isForward = item->Get(); auto action = SendAction(item->history()); - action.options.suggest.exists = 1; if (suggestion) { action.options.suggest.date = suggestion->date; @@ -218,12 +208,22 @@ void SendSuggest( action.options.suggest.ton = suggestion->price.ton() ? 1 : 0; } modify(action.options.suggest); - action.options.starsApproved = starsApproved; action.replyTo.monoforumPeerId = item->history()->amMonoforumAdmin() ? item->sublistPeerId() : PeerId(); action.replyTo.messageId = item->fullId(); + + const auto checked = state->sendPayment.check( + show, + item->history()->peer, + action.options, + 1, + withPaymentApproved); + if (!checked) { + return; + } + show->session().api().sendAction(action); show->session().api().forwardMessages({ .items = { item }, @@ -302,7 +302,7 @@ void SuggestOfferForMessage( }; using namespace HistoryView; auto priceBox = Box(ChooseSuggestPriceBox, SuggestPriceBoxArgs{ - .session = &show->session(), + .peer = item->history()->peer, .done = done, .value = values, .mode = mode, diff --git a/Telegram/SourceFiles/core/credits_amount.h b/Telegram/SourceFiles/core/credits_amount.h index 1704e28161..12b26239c4 100644 --- a/Telegram/SourceFiles/core/credits_amount.h +++ b/Telegram/SourceFiles/core/credits_amount.h @@ -101,8 +101,10 @@ public: return result; } - friend inline auto operator<=>(CreditsAmount, CreditsAmount) = default; - friend inline bool operator==(CreditsAmount, CreditsAmount) = default; + friend inline constexpr auto operator<=>(CreditsAmount, CreditsAmount) + = default; + friend inline constexpr bool operator==(CreditsAmount, CreditsAmount) + = default; [[nodiscard]] CreditsAmount abs() const { return (_whole < 0) ? CreditsAmount(-_whole, -_nano) : *this; diff --git a/Telegram/SourceFiles/data/components/credits.cpp b/Telegram/SourceFiles/data/components/credits.cpp index 5323305331..bbd27a2ba2 100644 --- a/Telegram/SourceFiles/data/components/credits.cpp +++ b/Telegram/SourceFiles/data/components/credits.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/components/credits.h" +#include "apiwrap.h" #include "api/api_credits.h" #include "data/data_user.h" #include "main/main_app_config.h" @@ -93,6 +94,54 @@ rpl::producer Credits::balanceValue() const { return _nonLockedBalance.value(); } +void Credits::tonLoad(bool force) { + if (_tonRequestId + || (!force + && _tonLastLoaded + && _tonLastLoaded + kReloadThreshold > crl::now())) { + return; + } + _tonRequestId = _session->api().request(MTPpayments_GetStarsStatus( + MTP_flags(MTPpayments_GetStarsStatus::Flag::f_ton), + MTP_inputPeerSelf() + )).done([=](const MTPpayments_StarsStatus &result) { + _tonRequestId = 0; + const auto amount = CreditsAmountFromTL(result.data().vbalance()); + if (amount.ton()) { + apply(amount); + } else if (amount.empty()) { + apply(CreditsAmount(0, CreditsType::Ton)); + } else { + LOG(("API Error: Got weird balance.")); + } + }).fail([=](const MTP::Error &error) { + _tonRequestId = 0; + LOG(("API Error: Couldn't get TON balance, error: %1" + ).arg(error.type())); + }).send(); +} + +bool Credits::tonLoaded() const { + return _tonLastLoaded != 0; +} + +rpl::producer Credits::tonLoadedValue() const { + if (tonLoaded()) { + return rpl::single(true); + } + return rpl::single( + false + ) | rpl::then(_tonLoadedChanges.events() | rpl::map_to(true)); +} + +CreditsAmount Credits::tonBalance() const { + return _tonBalance.current(); +} + +rpl::producer Credits::tonBalanceValue() const { + return _tonBalance.value(); +} + void Credits::updateNonLockedValue() { _nonLockedBalance = (_balance >= _locked) ? (_balance - _locked) @@ -133,7 +182,12 @@ void Credits::invalidate() { void Credits::apply(CreditsAmount balance) { if (balance.ton()) { - _balanceTon = balance; + _tonBalance = balance; + + const auto was = std::exchange(_tonLastLoaded, crl::now()); + if (!was) { + _tonLoadedChanges.fire({}); + } } else { _balance = balance; updateNonLockedValue(); diff --git a/Telegram/SourceFiles/data/components/credits.h b/Telegram/SourceFiles/data/components/credits.h index a26715f473..053b3f02a4 100644 --- a/Telegram/SourceFiles/data/components/credits.h +++ b/Telegram/SourceFiles/data/components/credits.h @@ -19,12 +19,8 @@ public: ~Credits(); void load(bool force = false); - void apply(CreditsAmount balance); - void apply(PeerId peerId, CreditsAmount balance); - [[nodiscard]] bool loaded() const; [[nodiscard]] rpl::producer loadedValue() const; - [[nodiscard]] CreditsAmount balance() const; [[nodiscard]] CreditsAmount balance(PeerId peerId) const; [[nodiscard]] rpl::producer balanceValue() const; @@ -33,6 +29,15 @@ public: [[nodiscard]] rpl::producer<> refreshedByPeerId(PeerId peerId); + void tonLoad(bool force = false); + [[nodiscard]] bool tonLoaded() const; + [[nodiscard]] rpl::producer tonLoadedValue() const; + [[nodiscard]] CreditsAmount tonBalance() const; + [[nodiscard]] rpl::producer tonBalanceValue() const; + + void apply(CreditsAmount balance); + void apply(PeerId peerId, CreditsAmount balance); + [[nodiscard]] bool statsEnabled() const; void applyCurrency(PeerId peerId, uint64 balance); @@ -56,13 +61,17 @@ private: base::flat_map _cachedPeerCurrencyBalances; CreditsAmount _balance; - CreditsAmount _balanceTon; CreditsAmount _locked; rpl::variable _nonLockedBalance; rpl::event_stream<> _loadedChanges; crl::time _lastLoaded = 0; float64 _rate = 0.; + rpl::variable _tonBalance; + rpl::event_stream<> _tonLoadedChanges; + crl::time _tonLastLoaded = false; + mtpRequestId _tonRequestId = 0; + bool _statsEnabled = false; rpl::event_stream _refreshedByPeerId; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index e9a3843c31..2f02a41d3f 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -1262,7 +1262,9 @@ void ApplyChannelUpdate( | Flag::PaidMediaAllowed | Flag::CanViewCreditsRevenue | Flag::StargiftsAvailable - | Flag::PaidMessagesAvailable; + | Flag::PaidMessagesAvailable + | Flag::HasStarsPerMessage + | Flag::StarsPerMessageKnown; channel->setFlags((channel->flags() & ~mask) | (update.is_can_set_username() ? Flag::CanSetUsername : Flag()) | (update.is_can_view_participants() @@ -1289,7 +1291,9 @@ void ApplyChannelUpdate( : Flag()) | (update.is_paid_messages_available() ? Flag::PaidMessagesAvailable - : Flag())); + : Flag()) + | (channel->starsPerMessage() ? Flag::HasStarsPerMessage : Flag()) + | Flag::StarsPerMessageKnown); channel->setUserpicPhoto(update.vchat_photo()); if (const auto migratedFrom = update.vmigrated_from_chat_id()) { channel->addFlags(Flag::Megagroup); diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 214ac56b86..11f74847f7 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -83,6 +83,8 @@ enum class ChannelDataFlag : uint64 { MonoforumAdmin = (1ULL << 40), MonoforumDisabled = (1ULL << 41), ForumTabs = (1ULL << 42), + HasStarsPerMessage = (1ULL << 43), + StarsPerMessageKnown = (1ULL << 44), }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; using ChannelDataFlags = base::flags; @@ -280,6 +282,12 @@ public: [[nodiscard]] bool paidMessagesAvailable() const { return flags() & Flag::PaidMessagesAvailable; } + [[nodiscard]] bool hasStarsPerMessage() const { + return flags() & Flag::HasStarsPerMessage; + } + [[nodiscard]] bool starsPerMessageKnown() const { + return flags() & Flag::StarsPerMessageKnown; + } [[nodiscard]] bool useSubsectionTabs() const; [[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights( diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 31d3b117da..1ec256053a 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -992,7 +992,14 @@ not_null Session::processChat(const MTPChat &data) { ? Flag::StoriesHidden : Flag()) | Flag::AutoTranslation - | Flag::Monoforum; + | Flag::Monoforum + | Flag::HasStarsPerMessage + | Flag::StarsPerMessageKnown; + const auto hasStarsPerMessage + = data.vsend_paid_messages_stars().has_value(); + if (!hasStarsPerMessage) { + channel->setStarsPerMessage(0); + } const auto storiesState = minimal ? std::optional() : data.is_stories_unavailable() @@ -1034,7 +1041,13 @@ not_null Session::processChat(const MTPChat &data) { ? Flag::StoriesHidden : Flag()) | (data.is_autotranslation() ? Flag::AutoTranslation : Flag()) - | (data.is_monoforum() ? Flag::Monoforum : Flag()); + | (data.is_monoforum() ? Flag::Monoforum : Flag()) + | (hasStarsPerMessage + ? (Flag::HasStarsPerMessage + | (channel->starsPerMessageKnown() + ? Flag::StarsPerMessageKnown + : Flag())) + : Flag::StarsPerMessageKnown); channel->setFlags((channel->flags() & ~flagsMask) | flagsSet); channel->setBotVerifyDetailsIcon( data.vbot_verification_icon().value_or_empty()); @@ -1047,12 +1060,6 @@ not_null Session::processChat(const MTPChat &data) { } channel->setPhoto(data.vphoto()); - const auto hasStarsPerMessage - = data.vsend_paid_messages_stars().has_value(); - if (!hasStarsPerMessage) { - channel->setStarsPerMessage(0); - } - if (const auto monoforum = data.vlinked_monoforum_id()) { if (const auto linked = channelLoaded(monoforum->v)) { channel->setMonoforumLink(linked); diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 7dfd473d52..c0cfae99b5 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -719,6 +719,7 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { | Flag::CanPinMessages | Flag::VoiceMessagesForbidden | Flag::ReadDatesPrivate + | Flag::HasStarsPerMessage | Flag::MessageMoneyRestrictionsKnown | Flag::RequiresPremiumToWrite; user->setFlags((user->flags() & ~mask) @@ -732,6 +733,7 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { ? Flag::VoiceMessagesForbidden : Flag()) | (update.is_read_dates_private() ? Flag::ReadDatesPrivate : Flag()) + | (user->starsPerMessage() ? Flag::HasStarsPerMessage : Flag()) | Flag::MessageMoneyRestrictionsKnown | (update.is_contact_require_premium() ? Flag::RequiresPremiumToWrite diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 81f52890cb..06eb877ff6 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_stories.h" #include "data/data_user.h" +#include "history/view/controls/history_view_suggest_options.h" #include "history/history.h" #include "history/history_item_components.h" #include "main/main_account.h" @@ -189,20 +190,27 @@ Data::SendErrorWithThread GetErrorForSending( std::optional ComputePaymentDetails( not_null peer, int messagesCount) { - if (const auto user = peer->asUser()) { - if (user->hasStarsPerMessage() - && !user->messageMoneyRestrictionsKnown()) { - user->updateFull(); - return {}; - } - } else if (const auto channel = peer->asChannel()) { - if (!channel->isFullLoaded()) { - channel->updateFull(); - return {}; - } + const auto user = peer->asUser(); + const auto channel = user ? nullptr : peer->asChannel(); + const auto has = (user && user->hasStarsPerMessage()) + || (channel && channel->hasStarsPerMessage()); + if (!has) { + return SendPaymentDetails(); } - if (!peer->session().credits().loaded()) { + + const auto known1 = peer->session().credits().loaded(); + if (!known1) { peer->session().credits().load(); + } + + const auto known2 = user + ? user->messageMoneyRestrictionsKnown() + : channel->starsPerMessageKnown(); + if (!known2) { + peer->updateFull(); + } + + if (!known1 || !known2) { return {}; } else if (const auto perMessage = peer->starsPerMessageChecked()) { return SendPaymentDetails{ @@ -213,6 +221,21 @@ std::optional ComputePaymentDetails( return SendPaymentDetails(); } +bool SuggestPaymentDataReady( + not_null peer, + SuggestPostOptions suggest) { + if (!suggest.exists || !suggest.price()) { + return true; + } else if (suggest.ton && !peer->session().credits().tonLoaded()) { + peer->session().credits().tonLoad(); + return false; + } else if (!suggest.ton && !peer->session().credits().loaded()) { + peer->session().credits().load(); + return false; + } + return true; +} + object_ptr MakeSendErrorBox( const Data::SendErrorWithThread &error, bool withTitle) { @@ -250,13 +273,15 @@ void ShowSendPaidConfirm( not_null peer, SendPaymentDetails details, Fn confirmed, - PaidConfirmStyles styles) { + PaidConfirmStyles styles, + int suggestStarsPrice) { return ShowSendPaidConfirm( navigation->uiShow(), peer, details, confirmed, - styles); + styles, + suggestStarsPrice); } void ShowSendPaidConfirm( @@ -264,13 +289,15 @@ void ShowSendPaidConfirm( not_null peer, SendPaymentDetails details, Fn confirmed, - PaidConfirmStyles styles) { + PaidConfirmStyles styles, + int suggestStarsPrice) { ShowSendPaidConfirm( std::move(show), std::vector>{ peer }, details, confirmed, - styles); + styles, + suggestStarsPrice); } void ShowSendPaidConfirm( @@ -278,7 +305,8 @@ void ShowSendPaidConfirm( const std::vector> &peers, SendPaymentDetails details, Fn confirmed, - PaidConfirmStyles styles) { + PaidConfirmStyles styles, + int suggestStarsPrice) { Expects(!peers.empty()); const auto singlePeer = (peers.size() > 1) @@ -286,7 +314,7 @@ void ShowSendPaidConfirm( : peers.front().get(); const auto singlePeerId = singlePeer ? singlePeer->id : PeerId(); const auto check = [=] { - const auto required = details.stars; + const auto required = details.stars + suggestStarsPrice; if (!required) { return; } @@ -296,10 +324,13 @@ void ShowSendPaidConfirm( confirmed(); } }; - Settings::MaybeRequestBalanceIncrease( + using namespace Settings; + MaybeRequestBalanceIncrease( show, required, - Settings::SmallBalanceForMessage{ .recipientId = singlePeerId }, + (suggestStarsPrice + ? SmallBalanceSource(SmallBalanceForSuggest{ singlePeerId }) + : SmallBalanceForMessage{ singlePeerId }), done); }; auto usersOnly = true; @@ -388,15 +419,15 @@ void ShowSendPaidConfirm( bool SendPaymentHelper::check( not_null navigation, not_null peer, + Api::SendOptions options, int messagesCount, - int starsApproved, Fn resend, PaidConfirmStyles styles) { return check( navigation->uiShow(), peer, + options, messagesCount, - starsApproved, std::move(resend), styles); } @@ -404,17 +435,27 @@ bool SendPaymentHelper::check( bool SendPaymentHelper::check( std::shared_ptr show, not_null peer, + Api::SendOptions options, int messagesCount, - int starsApproved, Fn resend, PaidConfirmStyles styles) { clear(); + const auto suggest = options.suggest; + const auto starsApproved = options.starsApproved; + const auto suggestPriceStars = suggest.ton + ? 0 + : int(base::SafeRound(suggest.price().value())); + const auto suggestPriceTon = suggest.ton + ? suggest.price() + : CreditsAmount(); const auto details = ComputePaymentDetails(peer, messagesCount); - if (!details) { + const auto suggestDetails = SuggestPaymentDataReady(peer, suggest); + if (!details || !suggestDetails) { _resend = [=] { resend(starsApproved); }; - if (!peer->session().credits().loaded()) { + if ((!details || !suggest.ton) + && !peer->session().credits().loaded()) { peer->session().credits().loadedValue( ) | rpl::filter( rpl::mappers::_1 @@ -425,6 +466,18 @@ bool SendPaymentHelper::check( }, _lifetime); } + if ((!suggestDetails && suggest.ton) + && !peer->session().credits().tonLoaded()) { + peer->session().credits().tonLoadedValue( + ) | rpl::filter( + rpl::mappers::_1 + ) | rpl::take(1) | rpl::start_with_next([=] { + if (const auto callback = base::take(_resend)) { + callback(); + } + }, _lifetime); + } + peer->session().changes().peerUpdates( peer, Data::PeerUpdate::Flag::FullInfo @@ -438,7 +491,32 @@ bool SendPaymentHelper::check( } else if (const auto stars = details->stars; stars > starsApproved) { ShowSendPaidConfirm(show, peer, *details, [=] { resend(stars); - }, styles); + }, styles, suggestPriceStars); + return false; + } else if (suggestPriceStars + && (CreditsAmount(details->stars + suggestPriceStars) + > peer->session().credits().balance())) { + const auto peerId = peer->id; + const auto forMessages = details->stars; + const auto required = forMessages + suggestPriceStars; + const auto done = [=](Settings::SmallBalanceResult result) { + if (result == Settings::SmallBalanceResult::Success + || result == Settings::SmallBalanceResult::Already) { + resend(forMessages); + } + }; + using namespace Settings; + MaybeRequestBalanceIncrease( + show, + required, + SmallBalanceForSuggest{ peerId }, + done); + return false; + } + if (suggestPriceTon + && suggestPriceTon > peer->session().credits().tonBalance()) { + show->show( + Box(HistoryView::InsufficientTonBox, peer, suggestPriceTon)); return false; } return true; diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index 11270500f6..d1c472dd58 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -149,6 +149,10 @@ struct SendPaymentDetails { not_null peer, int messagesCount); +[[nodiscard]] bool SuggestPaymentDataReady( + not_null peer, + SuggestPostOptions suggest); + struct PaidConfirmStyles { const style::FlatLabel *label = nullptr; const style::Checkbox *checkbox = nullptr; @@ -158,34 +162,37 @@ void ShowSendPaidConfirm( not_null peer, SendPaymentDetails details, Fn confirmed, - PaidConfirmStyles styles = {}); + PaidConfirmStyles styles = {}, + int suggestStarsPrice = 0); void ShowSendPaidConfirm( std::shared_ptr show, not_null peer, SendPaymentDetails details, Fn confirmed, - PaidConfirmStyles styles = {}); + PaidConfirmStyles styles = {}, + int suggestStarsPrice = 0); void ShowSendPaidConfirm( std::shared_ptr show, const std::vector> &peers, SendPaymentDetails details, Fn confirmed, - PaidConfirmStyles styles = {}); + PaidConfirmStyles styles = {}, + int suggestStarsPrice = 0); class SendPaymentHelper final { public: [[nodiscard]] bool check( not_null navigation, not_null peer, + Api::SendOptions options, int messagesCount, - int starsApproved, Fn resend, PaidConfirmStyles styles = {}); [[nodiscard]] bool check( std::shared_ptr show, not_null peer, + Api::SendOptions options, int messagesCount, - int starsApproved, Fn resend, PaidConfirmStyles styles = {}); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2dc081cde4..7b6c602cf0 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4540,7 +4540,7 @@ void HistoryWidget::saveEditMessage(Api::SendOptions options) { }; const auto checked = checkSendPayment( 1 + int(_forwardPanel->items().size()), - options.starsApproved, + options, withPaymentApproved); if (!checked) { return; @@ -4629,15 +4629,15 @@ void HistoryWidget::sendVoice(const VoiceToSend &data) { copy.options.starsApproved = approved; sendVoice(copy); }; + auto action = prepareSendAction(data.options); const auto checked = checkSendPayment( 1 + int(_forwardPanel->items().size()), - data.options.starsApproved, + action.options, withPaymentApproved); if (!checked) { return; } - auto action = prepareSendAction(data.options); session().api().sendVoiceMessage( data.bytes, data.waveform, @@ -4678,7 +4678,7 @@ void HistoryWidget::send(Api::SendOptions options) { message.textWithTags, ignoreSlowmodeCountdown, withPaymentApproved, - options.starsApproved)) { + message.action.options)) { return; } @@ -5011,14 +5011,14 @@ FullMsgId HistoryWidget::cornerButtonsCurrentId() { bool HistoryWidget::checkSendPayment( int messagesCount, - int starsApproved, + Api::SendOptions options, Fn withPaymentApproved) { return _peer && _sendPayment.check( controller(), _peer, + options, messagesCount, - starsApproved, std::move(withPaymentApproved)); } @@ -5209,9 +5209,11 @@ void HistoryWidget::sendBotCommand( copy.starsApproved = approved; sendBotCommand(request, copy); }; + + const auto action = prepareSendAction(options); const auto checked = checkSendPayment( 1, - options.starsApproved, + action.options, withPaymentApproved); if (!checked) { return; @@ -5226,7 +5228,7 @@ void HistoryWidget::sendBotCommand( ? request.command : Bot::WrapCommandInChat(_peer, request.command, request.context); - auto message = Api::MessageToSend(prepareSendAction(options)); + auto message = Api::MessageToSend(action); message.textWithTags = { toSend, TextWithTags::Tags() }; message.action.replyTo = request.replyTo ? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/) @@ -6233,7 +6235,7 @@ bool HistoryWidget::showSendMessageError( const TextWithTags &textWithTags, bool ignoreSlowmodeCountdown, Fn withPaymentApproved, - int starsApproved) { + Api::SendOptions options) { if (!_canSendMessages) { return false; } @@ -6254,7 +6256,7 @@ bool HistoryWidget::showSendMessageError( return withPaymentApproved && !checkSendPayment( request.messagesCount, - starsApproved, + options, withPaymentApproved); } @@ -6369,6 +6371,11 @@ void HistoryWidget::sendingFilesConfirmed( void HistoryWidget::sendingFilesConfirmed( std::shared_ptr bundle, Api::SendOptions options) { + const auto compress = bundle->way.sendImagesAsPhotos(); + const auto type = compress ? SendMediaType::Photo : SendMediaType::File; + auto action = prepareSendAction(options); + action.clearDraft = false; + const auto withPaymentApproved = [=](int approved) { auto copy = options; copy.starsApproved = approved; @@ -6376,16 +6383,12 @@ void HistoryWidget::sendingFilesConfirmed( }; const auto checked = checkSendPayment( bundle->totalCount, - options.starsApproved, + action.options, withPaymentApproved); if (!checked) { return; } - const auto compress = bundle->way.sendImagesAsPhotos(); - const auto type = compress ? SendMediaType::Photo : SendMediaType::File; - auto action = prepareSendAction(options); - action.clearDraft = false; if (bundle->sendComment) { auto message = Api::MessageToSend(action); message.textWithTags = base::take(bundle->caption); @@ -7715,9 +7718,13 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { copy.options.starsApproved = approved; sendInlineResult(copy); }; + + auto action = prepareSendAction(result.options); + action.generateLocal = true; + const auto checked = checkSendPayment( 1, - result.options.starsApproved, + action.options, withPaymentApproved); if (!checked) { return; @@ -7725,9 +7732,6 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { controller()->sendingAnimation().appendSending( result.messageSendingFrom); - - auto action = prepareSendAction(result.options); - action.generateLocal = true; session().api().sendInlineResult( result.bot, result.result.get(), @@ -8336,7 +8340,7 @@ bool HistoryWidget::sendExistingDocument( }; const auto checked = checkSendPayment( 1, - messageToSend.action.options.starsApproved, + messageToSend.action.options, withPaymentApproved); if (!checked) { return false; @@ -8375,6 +8379,7 @@ bool HistoryWidget::sendExistingPhoto( } else if (showSlowmodeError()) { return false; } + const auto action = prepareSendAction(options); const auto withPaymentApproved = [=](int approved) { auto copy = options; @@ -8383,15 +8388,13 @@ bool HistoryWidget::sendExistingPhoto( }; const auto checked = checkSendPayment( 1, - options.starsApproved, + action.options, withPaymentApproved); if (!checked) { return false; } - Api::SendExistingPhoto( - Api::MessageToSend(prepareSendAction(options)), - photo); + Api::SendExistingPhoto(Api::MessageToSend(action), photo); hideSelectorControlsAnimated(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 8c6a61f8b3..7166ea8768 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -371,7 +371,7 @@ private: [[nodiscard]] bool checkSendPayment( int messagesCount, - int starsApproved, + Api::SendOptions options, Fn withPaymentApproved); void checkSuggestToGigagroup(); @@ -489,7 +489,7 @@ private: const TextWithTags &textWithTags, bool ignoreSlowmodeCountdown, Fn withPaymentApproved = nullptr, - int starsApproved = 0); + Api::SendOptions options = {}); void sendingFilesConfirmed( Ui::PreparedList &&list, diff --git a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp index 36d34afca6..9b3015dbec 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "chat_helpers/compose/compose_show.h" #include "core/ui_integration.h" +#include "data/components/credits.h" #include "data/stickers/data_custom_emoji.h" #include "data/data_channel.h" #include "data/data_media_types.h" @@ -19,9 +20,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "info/channel_statistics/earn/earn_icons.h" #include "lang/lang_keys.h" +#include "lottie/lottie_icon.h" #include "main/main_app_config.h" #include "main/main_session.h" #include "settings/settings_common.h" +#include "settings/settings_credits_graphics.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" #include "ui/boxes/choose_date_time.h" @@ -30,8 +33,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/fields/input_field.h" #include "ui/widgets/buttons.h" #include "ui/wrap/slide_wrap.h" +#include "ui/basic_click_handlers.h" #include "ui/painter.h" +#include "ui/rect.h" #include "ui/vertical_list.h" +#include "styles/style_boxes.h" #include "styles/style_channel_earn.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" @@ -81,13 +87,19 @@ void ChooseSuggestPriceBox( std::vector