From f4c739ab92a7878f97f1c2045f56244c687a8980 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 4 Mar 2025 18:43:06 +0400 Subject: [PATCH] Improve transactions history for new stuff. --- Telegram/Resources/langs/lang.strings | 6 ++ Telegram/SourceFiles/api/api_credits.cpp | 20 ++++- .../SourceFiles/boxes/gift_premium_box.cpp | 30 ++++++-- .../boxes/peers/edit_peer_info_box.cpp | 6 ++ .../boxes/peers/edit_peer_info_box.h | 4 + .../SourceFiles/core/local_url_handlers.cpp | 24 ++++++ Telegram/SourceFiles/core/stars_amount.h | 9 +++ Telegram/SourceFiles/data/data_credits.h | 8 +- .../peer_gifts/info_peer_gifts_common.cpp | 4 +- .../info_statistics_list_controllers.cpp | 19 ++--- .../settings/settings_credits_graphics.cpp | 73 ++++++++++++++----- .../ui/effects/credits_graphics.cpp | 10 ++- 12 files changed, 171 insertions(+), 42 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 250ba9584..d5abcfbad 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2681,6 +2681,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_summary_history_entry_inner_in" = "In-App Purchase"; "lng_credits_summary_balance" = "Balance"; "lng_credits_commission" = "{amount} commission"; +"lng_credits_paid_messages_fee#one" = "Fee for {count} Message"; +"lng_credits_paid_messages_fee#other" = "Fee for {count} Messages"; +"lng_credits_paid_messages_fee_about" = "You receive {percent} of the price that you charge for each incoming message. {link}"; +"lng_credits_paid_messages_fee_about_link" = "Change Fee {emoji}"; +"lng_credits_paid_messages_full" = "Full Price"; +"lng_credits_premium_gift_duration" = "Duration"; "lng_credits_more_options" = "More Options"; "lng_credits_balance_me" = "your balance"; "lng_credits_buy_button" = "Buy More Stars"; diff --git a/Telegram/SourceFiles/api/api_credits.cpp b/Telegram/SourceFiles/api/api_credits.cpp index bdb10d02d..0dc21c636 100644 --- a/Telegram/SourceFiles/api/api_credits.cpp +++ b/Telegram/SourceFiles/api/api_credits.cpp @@ -90,7 +90,13 @@ constexpr auto kTransactionsLimit = 100; ? peerFromMTP(*tl.data().vstarref_peer()).value : 0; const auto incoming = (amount >= StarsAmount()); - const auto saveActorId = (reaction || !extended.empty()) && incoming; + const auto paidMessagesCount + = tl.data().vpaid_messages().value_or_empty(); + const auto premiumMonthsForStars + = tl.data().vpremium_gift_months().value_or_empty(); + const auto saveActorId = (reaction + || !extended.empty() + || paidMessagesCount) && incoming; const auto parsedGift = stargift ? FromTL(&peer->session(), *stargift) : std::optional(); @@ -110,9 +116,9 @@ constexpr auto kTransactionsLimit = 100; .bareGiftStickerId = giftStickerId, .bareActorId = saveActorId ? barePeerId : uint64(0), .uniqueGift = parsedGift ? parsedGift->unique : nullptr, - .starrefAmount = starrefAmount, - .starrefCommission = starrefCommission, - .starrefRecipientId = starrefBarePeerId, + .starrefAmount = paidMessagesCount ? StarsAmount() : starrefAmount, + .starrefCommission = paidMessagesCount ? 0 : starrefCommission, + .starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId, .peerType = tl.data().vpeer().match([](const HistoryPeerTL &) { return Data::CreditsHistoryEntry::PeerType::Peer; }, [](const MTPDstarsTransactionPeerPlayMarket &) { @@ -138,9 +144,15 @@ constexpr auto kTransactionsLimit = 100; ? base::unixtime::parse(tl.data().vtransaction_date()->v) : QDateTime(), .successLink = qs(tl.data().vtransaction_url().value_or_empty()), + .paidMessagesCount = paidMessagesCount, + .paidMessagesAmount = (paidMessagesCount + ? starrefAmount + : StarsAmount()), + .paidMessagesCommission = paidMessagesCount ? starrefCommission : 0, .starsConverted = int(nonUniqueGift ? nonUniqueGift->vconvert_stars().v : 0), + .premiumMonthsForStars = premiumMonthsForStars, .floodSkip = int(tl.data().vfloodskip_number().value_or(0)), .converted = stargift && incoming, .stargift = stargift.has_value(), diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index db9eaf3a9..1d12e7f81 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox. #include "boxes/star_gift_box.h" // ShowStarGiftBox. #include "boxes/transfer_gift_box.h" // ShowTransferGiftBox. +#include "core/ui_integration.h" #include "data/data_boosts.h" #include "data/data_changes.h" #include "data/data_channel.h" @@ -58,7 +59,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/gradient_round_button.h" -#include "ui/widgets/label_with_custom_emoji.h" #include "ui/widgets/tooltip.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" @@ -66,6 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_peer_menu.h" // ShowChooseRecipientBox. #include "window/window_session_controller.h" #include "styles/style_boxes.h" +#include "styles/style_credits.h" #include "styles/style_giveaway.h" #include "styles/style_info.h" #include "styles/style_layers.h" @@ -1272,8 +1273,8 @@ void AddStarGiftTable( const auto selfBareId = session->userPeerId().value; const auto giftToSelf = (peerId == session->userPeerId()) && (entry.in || entry.bareGiftOwnerId == selfBareId); - const auto giftToChannel = entry.giftSavedId - && peerIsChannel(PeerId(entry.bareGiftListPeerId)); + const auto giftToChannel = entry.giftChannelSavedId + && peerIsChannel(PeerId(entry.bareEntryOwnerId)); const auto raw = std::make_shared(nullptr); const auto showTooltip = [=]( @@ -1394,14 +1395,14 @@ void AddStarGiftTable( ? MakePeerTableValue(table, show, PeerId(entry.bareActorId)) : MakeHiddenPeerTableValue(table)), st::giveawayGiftCodePeerMargin); - if (entry.bareGiftListPeerId) { + if (entry.bareEntryOwnerId) { AddTableRow( table, tr::lng_credits_box_history_entry_peer(), MakePeerTableValue( table, show, - PeerId(entry.bareGiftListPeerId)), + PeerId(entry.bareEntryOwnerId)), st::giveawayGiftCodePeerMargin); } } else if (peerId && !giftToSelf) { @@ -1775,6 +1776,25 @@ void AddCreditsHistoryEntryTable( tr::lng_credits_box_history_entry_subscription( Ui::Text::WithEntities)); } + if (entry.paidMessagesAmount) { + auto value = Ui::Text::IconEmoji(&st::starIconEmojiColored); + const auto full = (entry.in ? 1 : -1) + * (entry.credits + entry.paidMessagesAmount); + const auto starsText = Lang::FormatStarsAmountDecimal(full); + AddTableRow( + table, + tr::lng_credits_paid_messages_full(), + rpl::single(value.append(' ' + starsText))); + } + if (const auto months = entry.premiumMonthsForStars) { + AddTableRow( + table, + tr::lng_credits_premium_gift_duration(), + tr::lng_months( + lt_count, + rpl::single(1. * months), + Ui::Text::WithEntities)); + } if (!entry.id.isEmpty()) { auto label = MakeMaybeMultilineTokenValue(table, entry.id, st); label->setClickHandlerFilter([=](const auto &...) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 0299e7f9b..a457f084e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -2724,3 +2724,9 @@ bool EditPeerInfoBox::Available(not_null peer) { return false; } } + +void ShowEditChatPermissions( + not_null navigation, + not_null peer) { + ShowEditPermissions(navigation, peer); +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 9844320cf..b8787afac 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -56,3 +56,7 @@ private: not_null _peer; }; + +void ShowEditChatPermissions( + not_null navigation, + not_null peer); diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index c1e678579..adc083d79 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/edit_birthday_box.h" #include "ui/integration.h" #include "payments/payments_non_panel_process.h" +#include "boxes/peers/edit_peer_info_box.h" #include "boxes/share_box.h" #include "boxes/connection_box.h" #include "boxes/gift_premium_box.h" @@ -1014,6 +1015,25 @@ bool CopyUsername( return true; } +bool EditPaidMessagesFee( + Window::SessionController *controller, + const Match &match, + const QVariant &context) { + if (!controller) { + return false; + } + const auto peerId = PeerId(match->captured(1).toULongLong()); + if (const auto id = peerToChannel(peerId)) { + const auto channel = controller->session().data().channelLoaded(id); + if (channel && channel->canEditPermissions()) { + ShowEditChatPermissions(controller, channel); + } + } else { + controller->show(Box(EditMessagesPrivacyBox, controller)); + } + return true; +} + bool ShowStarsExamples( Window::SessionController *controller, const Match &match, @@ -1517,6 +1537,10 @@ const std::vector &InternalUrlHandlers() { u"^username_regular/([a-zA-Z0-9\\-\\_\\.]+)@([0-9]+)$"_q, CopyUsername, }, + { + u"^edit_paid_messages_fee/([0-9]+)$"_q, + EditPaidMessagesFee, + }, { u"^stars_examples$"_q, ShowStarsExamples, diff --git a/Telegram/SourceFiles/core/stars_amount.h b/Telegram/SourceFiles/core/stars_amount.h index fc9c8df90..ca0e6fbe2 100644 --- a/Telegram/SourceFiles/core/stars_amount.h +++ b/Telegram/SourceFiles/core/stars_amount.h @@ -60,6 +60,11 @@ public: normalize(); return *this; } + inline StarsAmount operator-() const { + auto result = *this; + result *= -1; + return result; + } friend inline auto operator<=>(StarsAmount, StarsAmount) = default; friend inline bool operator==(StarsAmount, StarsAmount) = default; @@ -97,3 +102,7 @@ private: [[nodiscard]] inline StarsAmount operator*(StarsAmount a, int64 b) { return a *= b; } + +[[nodiscard]] inline StarsAmount operator*(int64 a, StarsAmount b) { + return b *= a; +} diff --git a/Telegram/SourceFiles/data/data_credits.h b/Telegram/SourceFiles/data/data_credits.h index adf692218..f569cdd97 100644 --- a/Telegram/SourceFiles/data/data_credits.h +++ b/Telegram/SourceFiles/data/data_credits.h @@ -66,8 +66,8 @@ struct CreditsHistoryEntry final { uint64 bareGiftStickerId = 0; uint64 bareGiftOwnerId = 0; uint64 bareActorId = 0; - uint64 bareGiftListPeerId = 0; - uint64 giftSavedId = 0; + uint64 bareEntryOwnerId = 0; + uint64 giftChannelSavedId = 0; uint64 stargiftId = 0; std::shared_ptr uniqueGift; StarsAmount starrefAmount; @@ -77,11 +77,15 @@ struct CreditsHistoryEntry final { QDateTime subscriptionUntil; QDateTime successDate; QString successLink; + int paidMessagesCount = 0; + StarsAmount paidMessagesAmount; + int paidMessagesCommission = 0; int limitedCount = 0; int limitedLeft = 0; int starsConverted = 0; int starsToUpgrade = 0; int starsUpgradedBySender = 0; + int premiumMonthsForStars = 0; int floodSkip = 0; bool converted : 1 = false; bool anonymous : 1 = false; 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 98b521566..dfd323656 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp @@ -100,7 +100,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) { data.currency, true)); if (const auto stars = data.stars) { - const auto starsText = QString::number(stars); + const auto starsText = Lang::FormatCountDecimal(stars); _byStars.setMarkedText( st::giftBoxByStarsStyle, tr::lng_gift_premium_by_stars( @@ -139,7 +139,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) { (unique ? tr::lng_gift_price_unique(tr::now, Ui::Text::WithEntities) : _delegate->star().append( - ' ' + QString::number(data.info.stars))), + ' ' + Lang::FormatCountDecimal(data.info.stars))), kMarkupTextOptions, _delegate->textContext()); if (!_stars) { diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 799416844..e08bc563e 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -797,7 +797,6 @@ private: PaintRoundImageCallback _paintUserpicCallback; std::optional _rightLabel; - QString _title; QString _name; Ui::Text::String _description; @@ -850,9 +849,13 @@ void CreditsRow::init() { const auto name = !isSpecial ? PeerListRow::generateName() : Ui::GenerateEntryName(_entry).text; - _name = _entry.title.isEmpty() - ? name - : (!_entry.subscriptionUntil.isNull() && !isSpecial) + _name = _entry.paidMessagesCount + ? tr::lng_credits_paid_messages_fee( + tr::now, + lt_count, + _entry.paidMessagesCount) + : ((!_entry.subscriptionUntil.isNull() && !isSpecial) + || _entry.title.isEmpty()) ? name : _entry.title; setSkipPeerBadge(true); @@ -861,6 +864,8 @@ void CreditsRow::init() { tr::now, lt_count_decimal, _entry.floodSkip) + : _entry.paidMessagesCount + ? name : (!_entry.subscriptionUntil.isNull() && !_entry.title.isEmpty()) ? _entry.title : _entry.refunded @@ -943,11 +948,7 @@ const Data::SubscriptionEntry &CreditsRow::subscription() const { } QString CreditsRow::generateName() { - return (!_entry.title.isEmpty() && !_entry.subscriptionUntil.isNull()) - ? _name - : _entry.title.isEmpty() - ? _name - : _entry.title; + return _name; } PaintRoundImageCallback CreditsRow::generatePaintUserpicCallback(bool force) { diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index c6f3d15d5..d42f40ee8 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -178,10 +178,10 @@ private: const Data::CreditsHistoryEntry &entry) { return !entry.stargift ? Data::SavedStarGiftId() - : (entry.bareGiftListPeerId && entry.giftSavedId) + : (entry.bareEntryOwnerId && entry.giftChannelSavedId) ? Data::SavedStarGiftId::Chat( - session->data().peer(PeerId(entry.bareGiftListPeerId)), - entry.giftSavedId) + session->data().peer(PeerId(entry.bareEntryOwnerId)), + entry.giftChannelSavedId) : Data::SavedStarGiftId::User(MsgId(entry.bareMsgId)); } @@ -950,9 +950,9 @@ void GenericCreditsEntryBox( const auto giftToSelf = isStarGift && (e.barePeerId == selfPeerId) && (e.in || e.bareGiftOwnerId == selfPeerId); - const auto giftChannel = (isStarGift && e.giftSavedId) + const auto giftChannel = (isStarGift && e.giftChannelSavedId) ? session->data().peer( - PeerId(e.bareGiftListPeerId))->asChannel() + PeerId(e.bareEntryOwnerId))->asChannel() : nullptr; const auto giftToChannel = (giftChannel != nullptr); const auto giftToChannelCanManage = giftToChannel @@ -1051,7 +1051,7 @@ void GenericCreditsEntryBox( content->add(object_ptr>( content, GenericEntryPhoto(content, callback, stUser.photoSize))); - } else if (peer && !e.gift) { + } else if (peer && !e.gift && !e.premiumMonthsForStars) { if (e.subscriptionUntil.isNull() && s.until.isNull()) { content->add(object_ptr>( content, @@ -1061,7 +1061,7 @@ void GenericCreditsEntryBox( content, SubscriptionUserpic(content, peer, stUser.photoSize))); } - } else if (e.gift || isPrize) { + } else if (e.gift || isPrize || e.premiumMonthsForStars) { struct State final { DocumentData *sticker = nullptr; std::shared_ptr media; @@ -1079,7 +1079,9 @@ void GenericCreditsEntryBox( auto &packs = session->giftBoxStickersPacks(); const auto document = starGiftSticker ? starGiftSticker - : packs.lookup(packs.monthsForStars(e.credits.whole())); + : packs.lookup(e.premiumMonthsForStars + ? e.premiumMonthsForStars + : packs.monthsForStars(e.credits.whole())); if (document && document->sticker()) { state->sticker = document; state->media = document->createMediaView(); @@ -1159,6 +1161,13 @@ void GenericCreditsEntryBox( ? tr::lng_credits_box_history_entry_giveaway_name(tr::now) : (!e.subscriptionUntil.isNull() && e.title.isEmpty()) ? tr::lng_credits_box_history_entry_subscription(tr::now) + : e.paidMessagesCount + ? tr::lng_credits_paid_messages_fee( + tr::now, + lt_count, + e.paidMessagesCount) + : e.premiumMonthsForStars + ? tr::lng_premium_summary_title(tr::now) : !e.title.isEmpty() ? e.title : e.starrefCommission @@ -1315,6 +1324,12 @@ void GenericCreditsEntryBox( rpl::single(e.description), st::creditsBoxAbout))); } + + const auto arrowEmoji = Ui::Text::SingleCustomEmoji( + owner->customEmojiManager().registerInternalEmoji( + st::topicButtonArrow, + st::channelEarnLearnArrowMargins, + true)); if (!uniqueGift && starGiftCanManage) { Ui::AddSkip(content); const auto about = box->addRow( @@ -1380,14 +1395,9 @@ void GenericCreditsEntryBox( } else if (isStarGift) { } else if (e.gift || isPrize) { Ui::AddSkip(content); - const auto arrow = Ui::Text::SingleCustomEmoji( - owner->customEmojiManager().registerInternalEmoji( - st::topicButtonArrow, - st::channelEarnLearnArrowMargins, - true)); auto link = tr::lng_credits_box_history_entry_gift_about_link( lt_emoji, - rpl::single(arrow), + rpl::single(arrowEmoji), Ui::Text::RichLangValue ) | rpl::map([](TextWithEntities text) { return Ui::Text::Link( @@ -1411,6 +1421,31 @@ void GenericCreditsEntryBox( Ui::Text::RichLangValue), { .session = session }, st::creditsBoxAbout))); + } else if (e.paidMessagesCommission && e.barePeerId) { + Ui::AddSkip(content); + auto link = tr::lng_credits_paid_messages_fee_about_link( + lt_emoji, + rpl::single(arrowEmoji), + Ui::Text::RichLangValue + ) | rpl::map([id = e.barePeerId](TextWithEntities text) { + return Ui::Text::Link( + std::move(text), + u"internal:edit_paid_messages_fee/"_q + QString::number(id)); + }); + const auto percent = 100. - (e.paidMessagesCommission / 10.); + box->addRow(object_ptr>( + box, + Ui::CreateLabelWithCustomEmoji( + box, + tr::lng_credits_paid_messages_fee_about( + lt_percent, + rpl::single( + Ui::Text::Bold(QString::number(percent) + '%')), + lt_link, + std::move(link), + Ui::Text::RichLangValue), + { .session = session }, + st::creditsBoxAbout))); } Ui::AddSkip(content); @@ -1695,7 +1730,7 @@ void GenericCreditsEntryBox( : canUpgradeFree ? tr::lng_gift_upgrade_free() : (canToggle && !e.savedToProfile) - ? (e.giftSavedId + ? (e.giftChannelSavedId ? tr::lng_gift_show_on_channel : tr::lng_gift_show_on_page)() : tr::lng_box_ok())); @@ -1882,8 +1917,8 @@ void SavedStarGiftBox( .bareGiftStickerId = data.info.document->id, .bareGiftOwnerId = owner->id.value, .bareActorId = data.fromId.value, - .bareGiftListPeerId = chatGiftPeer ? chatGiftPeer->id.value : 0, - .giftSavedId = data.manageId.chatSavedId(), + .bareEntryOwnerId = chatGiftPeer ? chatGiftPeer->id.value : 0, + .giftChannelSavedId = data.manageId.chatSavedId(), .stargiftId = data.info.id, .uniqueGift = data.info.unique, .peerType = Data::CreditsHistoryEntry::PeerType::Peer, @@ -1927,8 +1962,8 @@ void StarGiftViewBox( ? data.unique->ownerId.value : toId.value), .bareActorId = (toChannel ? data.channelFrom->id.value : 0), - .bareGiftListPeerId = (toChannel ? data.channel->id.value : 0), - .giftSavedId = data.channelSavedId, + .bareEntryOwnerId = (toChannel ? data.channel->id.value : 0), + .giftChannelSavedId = data.channelSavedId, .stargiftId = data.stargiftId, .uniqueGift = data.unique, .peerType = Data::CreditsHistoryEntry::PeerType::Peer, diff --git a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp index d472d6867..0fbf095cc 100644 --- a/Telegram/SourceFiles/ui/effects/credits_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/credits_graphics.cpp @@ -554,7 +554,15 @@ TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) { Info::BotStarRef::FormatCommission(entry.starrefCommission) }, TextWithEntities::Simple) - : (entry.floodSkip + : entry.paidMessagesCount + ? tr::lng_credits_paid_messages_fee( + tr::now, + lt_count, + entry.paidMessagesCount, + TextWithEntities::Simple) + : (entry.premiumMonthsForStars + ? tr::lng_premium_summary_title + : entry.floodSkip ? tr::lng_credits_box_history_entry_api : entry.reaction ? tr::lng_credits_box_history_entry_reaction_name