Replaced Data::EarnHistoryEntry with Data::CreditsHistoryEntry.

This commit is contained in:
23rd 2025-06-30 12:52:42 +03:00 committed by John Preston
parent 96951576c1
commit 0fd7061671
10 changed files with 227 additions and 243 deletions

View file

@ -127,6 +127,8 @@ PRIVATE
api/api_confirm_phone.h
api/api_credits.cpp
api/api_credits.h
api/api_credits_history_entry.cpp
api/api_credits_history_entry.h
api/api_earn.cpp
api/api_earn.h
api/api_editing.cpp

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_credits.h"
#include "api/api_credits_history_entry.h"
#include "api/api_premium.h"
#include "api/api_statistics_data_deserialize.h"
#include "api/api_updates.h"
@ -27,145 +28,6 @@ namespace {
constexpr auto kTransactionsLimit = 100;
[[nodiscard]] Data::CreditsHistoryEntry HistoryFromTL(
const MTPStarsTransaction &tl,
not_null<PeerData*> peer) {
using HistoryPeerTL = MTPDstarsTransactionPeer;
using namespace Data;
const auto owner = &peer->owner();
const auto photo = tl.data().vphoto()
? owner->photoFromWeb(*tl.data().vphoto(), ImageLocation())
: nullptr;
auto extended = std::vector<CreditsHistoryMedia>();
if (const auto list = tl.data().vextended_media()) {
extended.reserve(list->v.size());
for (const auto &media : list->v) {
media.match([&](const MTPDmessageMediaPhoto &data) {
if (const auto inner = data.vphoto()) {
const auto photo = owner->processPhoto(*inner);
if (!photo->isNull()) {
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Photo,
.id = photo->id,
});
}
}
}, [&](const MTPDmessageMediaDocument &data) {
if (const auto inner = data.vdocument()) {
const auto document = owner->processDocument(
*inner,
data.valt_documents());
if (document->isAnimation()
|| document->isVideoFile()
|| document->isGifv()) {
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Video,
.id = document->id,
});
}
}
}, [&](const auto &) {});
}
}
const auto barePeerId = tl.data().vpeer().match([](
const HistoryPeerTL &p) {
return peerFromMTP(p.vpeer());
}, [](const auto &) {
return PeerId(0);
}).value;
const auto stargift = tl.data().vstargift();
const auto nonUniqueGift = stargift
? stargift->match([&](const MTPDstarGift &data) {
return &data;
}, [](const auto &) { return (const MTPDstarGift*)nullptr; })
: nullptr;
const auto reaction = tl.data().is_reaction();
const auto amount = CreditsAmountFromTL(tl.data().vamount());
const auto starrefAmount = CreditsAmountFromTL(
tl.data().vstarref_amount());
const auto starrefCommission
= tl.data().vstarref_commission_permille().value_or_empty();
const auto starrefBarePeerId = tl.data().vstarref_peer()
? peerFromMTP(*tl.data().vstarref_peer()).value
: 0;
const auto incoming = (amount >= CreditsAmount());
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<Data::StarGift>();
const auto giftStickerId = parsedGift ? parsedGift->document->id : 0;
return Data::CreditsHistoryEntry{
.id = qs(tl.data().vid()),
.title = qs(tl.data().vtitle().value_or_empty()),
.description = { qs(tl.data().vdescription().value_or_empty()) },
.date = base::unixtime::parse(tl.data().vdate().v),
.photoId = photo ? photo->id : 0,
.extended = std::move(extended),
.credits = CreditsAmountFromTL(tl.data().vamount()),
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
.barePeerId = saveActorId ? peer->id.value : barePeerId,
.bareGiveawayMsgId = uint64(
tl.data().vgiveaway_post_id().value_or_empty()),
.bareGiftStickerId = giftStickerId,
.bareActorId = saveActorId ? barePeerId : uint64(0),
.uniqueGift = parsedGift ? parsedGift->unique : nullptr,
.starrefAmount = paidMessagesCount ? CreditsAmount() : starrefAmount,
.starrefCommission = paidMessagesCount ? 0 : starrefCommission,
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId,
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
return Data::CreditsHistoryEntry::PeerType::Peer;
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
return Data::CreditsHistoryEntry::PeerType::PlayMarket;
}, [](const MTPDstarsTransactionPeerFragment &) {
return Data::CreditsHistoryEntry::PeerType::Fragment;
}, [](const MTPDstarsTransactionPeerAppStore &) {
return Data::CreditsHistoryEntry::PeerType::AppStore;
}, [](const MTPDstarsTransactionPeerUnsupported &) {
return Data::CreditsHistoryEntry::PeerType::Unsupported;
}, [](const MTPDstarsTransactionPeerPremiumBot &) {
return Data::CreditsHistoryEntry::PeerType::PremiumBot;
}, [](const MTPDstarsTransactionPeerAds &) {
return Data::CreditsHistoryEntry::PeerType::Ads;
}, [](const MTPDstarsTransactionPeerAPI &) {
return Data::CreditsHistoryEntry::PeerType::API;
}),
.subscriptionUntil = tl.data().vsubscription_period()
? base::unixtime::parse(base::unixtime::now()
+ tl.data().vsubscription_period()->v)
: QDateTime(),
.successDate = tl.data().vtransaction_date()
? base::unixtime::parse(tl.data().vtransaction_date()->v)
: QDateTime(),
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
.paidMessagesCount = paidMessagesCount,
.paidMessagesAmount = (paidMessagesCount
? starrefAmount
: CreditsAmount()),
.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(),
.giftUpgraded = tl.data().is_stargift_upgrade(),
.giftResale = tl.data().is_stargift_resale(),
.reaction = tl.data().is_reaction(),
.refunded = tl.data().is_refund(),
.pending = tl.data().is_pending(),
.failed = tl.data().is_failed(),
.in = incoming,
.gift = tl.data().is_gift() || stargift.has_value(),
};
}
[[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL(
const MTPStarsSubscription &tl,
not_null<PeerData*> peer) {
@ -202,7 +64,7 @@ constexpr auto kTransactionsLimit = 100;
if (const auto history = data.vhistory()) {
entries.reserve(history->v.size());
for (const auto &tl : history->v) {
entries.push_back(HistoryFromTL(tl, peer));
entries.push_back(CreditsHistoryEntryFromTL(tl, peer));
}
}
auto subscriptions = std::vector<Data::SubscriptionEntry>();

View file

@ -0,0 +1,167 @@
/*
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
*/
#include "api/api_credits_history_entry.h"
#include "api/api_premium.h"
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_credits.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "main/main_session.h"
namespace Api {
Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
const MTPStarsTransaction &tl,
not_null<PeerData*> peer) {
using HistoryPeerTL = MTPDstarsTransactionPeer;
using namespace Data;
const auto owner = &peer->owner();
const auto photo = tl.data().vphoto()
? owner->photoFromWeb(*tl.data().vphoto(), ImageLocation())
: nullptr;
auto extended = std::vector<CreditsHistoryMedia>();
if (const auto list = tl.data().vextended_media()) {
extended.reserve(list->v.size());
for (const auto &media : list->v) {
media.match([&](const MTPDmessageMediaPhoto &data) {
if (const auto inner = data.vphoto()) {
const auto photo = owner->processPhoto(*inner);
if (!photo->isNull()) {
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Photo,
.id = photo->id,
});
}
}
}, [&](const MTPDmessageMediaDocument &data) {
if (const auto inner = data.vdocument()) {
const auto document = owner->processDocument(
*inner,
data.valt_documents());
if (document->isAnimation()
|| document->isVideoFile()
|| document->isGifv()) {
extended.push_back(CreditsHistoryMedia{
.type = CreditsHistoryMediaType::Video,
.id = document->id,
});
}
}
}, [&](const auto &) {});
}
}
const auto barePeerId = tl.data().vpeer().match([](
const HistoryPeerTL &p) {
return peerFromMTP(p.vpeer());
}, [](const auto &) {
return PeerId(0);
}).value;
const auto stargift = tl.data().vstargift();
const auto nonUniqueGift = stargift
? stargift->match([&](const MTPDstarGift &data) {
return &data;
}, [](const auto &) { return (const MTPDstarGift*)nullptr; })
: nullptr;
const auto reaction = tl.data().is_reaction();
const auto amount = CreditsAmountFromTL(tl.data().vamount());
const auto starrefAmount = CreditsAmountFromTL(
tl.data().vstarref_amount());
const auto starrefCommission
= tl.data().vstarref_commission_permille().value_or_empty();
const auto starrefBarePeerId = tl.data().vstarref_peer()
? peerFromMTP(*tl.data().vstarref_peer()).value
: 0;
const auto incoming = (amount >= CreditsAmount());
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<Data::StarGift>();
const auto giftStickerId = parsedGift ? parsedGift->document->id : 0;
return Data::CreditsHistoryEntry{
.id = qs(tl.data().vid()),
.title = qs(tl.data().vtitle().value_or_empty()),
.description = { qs(tl.data().vdescription().value_or_empty()) },
.date = base::unixtime::parse(
tl.data().vads_proceeds_from_date().value_or(
tl.data().vdate().v)),
.photoId = photo ? photo->id : 0,
.extended = std::move(extended),
.credits = CreditsAmountFromTL(tl.data().vamount()),
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
.barePeerId = saveActorId ? peer->id.value : barePeerId,
.bareGiveawayMsgId = uint64(
tl.data().vgiveaway_post_id().value_or_empty()),
.bareGiftStickerId = giftStickerId,
.bareActorId = saveActorId ? barePeerId : uint64(0),
.uniqueGift = parsedGift ? parsedGift->unique : nullptr,
.starrefAmount = paidMessagesCount ? CreditsAmount() : starrefAmount,
.starrefCommission = paidMessagesCount ? 0 : starrefCommission,
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId,
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
return Data::CreditsHistoryEntry::PeerType::Peer;
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
return Data::CreditsHistoryEntry::PeerType::PlayMarket;
}, [](const MTPDstarsTransactionPeerFragment &) {
return Data::CreditsHistoryEntry::PeerType::Fragment;
}, [](const MTPDstarsTransactionPeerAppStore &) {
return Data::CreditsHistoryEntry::PeerType::AppStore;
}, [](const MTPDstarsTransactionPeerUnsupported &) {
return Data::CreditsHistoryEntry::PeerType::Unsupported;
}, [](const MTPDstarsTransactionPeerPremiumBot &) {
return Data::CreditsHistoryEntry::PeerType::PremiumBot;
}, [](const MTPDstarsTransactionPeerAds &) {
return Data::CreditsHistoryEntry::PeerType::Ads;
}, [](const MTPDstarsTransactionPeerAPI &) {
return Data::CreditsHistoryEntry::PeerType::API;
}),
.subscriptionUntil = tl.data().vsubscription_period()
? base::unixtime::parse(base::unixtime::now()
+ tl.data().vsubscription_period()->v)
: QDateTime(),
.adsProceedsToDate = tl.data().vads_proceeds_to_date()
? base::unixtime::parse(tl.data().vads_proceeds_to_date()->v)
: QDateTime(),
.successDate = tl.data().vtransaction_date()
? base::unixtime::parse(tl.data().vtransaction_date()->v)
: QDateTime(),
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
.paidMessagesCount = paidMessagesCount,
.paidMessagesAmount = (paidMessagesCount
? starrefAmount
: CreditsAmount()),
.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(),
.giftUpgraded = tl.data().is_stargift_upgrade(),
.giftResale = tl.data().is_stargift_resale(),
.reaction = tl.data().is_reaction(),
.refunded = tl.data().is_refund(),
.pending = tl.data().is_pending(),
.failed = tl.data().is_failed(),
.in = incoming,
.gift = tl.data().is_gift() || stargift.has_value(),
};
}
} // namespace Api

View file

@ -0,0 +1,22 @@
/*
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
class PeerData;
namespace Data {
struct CreditsHistoryEntry;
} // namespace Data
namespace Api {
[[nodiscard]] Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
const MTPStarsTransaction &tl,
not_null<PeerData*> peer);
} // namespace Api

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_statistics.h"
#include "api/api_credits_history_entry.h"
#include "api/api_statistics_data_deserialize.h"
#include "apiwrap.h"
#include "base/unixtime.h"
@ -766,53 +767,15 @@ void EarnStatistics::requestHistory(
const auto nextToken = result.data().vnext_offset().value_or_empty();
const auto &tlTransactions
const auto tlTransactions
= result.data().vhistory().value_or_empty();
auto list = std::vector<Data::EarnHistoryEntry>();
list.reserve(tlTransactions.size());
for (const auto &tlTransaction : tlTransactions) {
const auto &d = tlTransaction.data();
const auto isProceed = d.vads_proceeds_from_date()
|| d.vads_proceeds_to_date();
const auto isRefund = d.is_refund();
const auto amount = CreditsAmountFromTL(d.vamount());
if (isProceed) {
list.push_back(Data::EarnHistoryEntry{
.type = Data::EarnHistoryEntry::Type::In,
.amount = amount,
.date = base::unixtime::parse(
d.vads_proceeds_from_date()->v),
.dateTo = base::unixtime::parse(
d.vads_proceeds_to_date()->v),
});
} else if (isRefund) {
list.push_back(Data::EarnHistoryEntry{
.type = Data::EarnHistoryEntry::Type::Return,
.amount = amount,
.date = base::unixtime::parse(d.vdate().v),
// .provider = qs(d.vprovider()),
});
} else {
list.push_back(Data::EarnHistoryEntry{
.type = Data::EarnHistoryEntry::Type::Out,
.status = d.is_pending()
? Data::EarnHistoryEntry::Status::Pending
: d.is_failed()
? Data::EarnHistoryEntry::Status::Failed
: Data::EarnHistoryEntry::Status::Success,
.amount = amount,
.date = base::unixtime::parse(d.vdate().v),
// .provider = qs(d.vprovider()),
.successDate = d.vtransaction_date()
? base::unixtime::parse(d.vtransaction_date()->v)
: QDateTime(),
.successLink = d.vtransaction_url()
? qs(*d.vtransaction_url())
: QString(),
});
}
}
const auto peer = _isUser ? (PeerData*)user() : (PeerData*)channel();
auto list = ranges::views::all(
tlTransactions
) | ranges::views::transform([=](const auto &d) {
return CreditsHistoryEntryFromTL(d, peer);
}) | ranges::to_vector;
done(Data::EarnHistorySlice{
.list = std::move(list),
.total = int(tlTransactions.size()),

View file

@ -7,46 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_statistics_chart.h"
#include <QtCore/QDateTime>
#include "data/data_credits.h"
#include "data/data_statistics_chart.h"
namespace Data {
using EarnInt = uint64;
constexpr auto kEarnMultiplier = EarnInt(1);
struct EarnHistoryEntry final {
enum class Type {
In,
Out,
Return,
};
enum class Status {
Success,
Failed,
Pending,
};
Type type;
Status status;
CreditsAmount amount;
QDateTime date;
QDateTime dateTo;
QString provider;
QDateTime successDate;
QString successLink;
};
struct EarnHistorySlice final {
using OffsetToken = QString;
std::vector<EarnHistoryEntry> list;
std::vector<CreditsHistoryEntry> list;
int total = 0;
bool allLoaded = false;
OffsetToken token;

View file

@ -77,6 +77,11 @@ struct CreditsHistoryEntry final {
uint64 starrefRecipientId = 0;
PeerType peerType;
QDateTime subscriptionUntil;
// Currency properties.
QDateTime adsProceedsToDate;
QString provider; // Unused.
QDateTime successDate;
QString successLink;
int paidMessagesCount = 0;

View file

@ -118,7 +118,7 @@ void InnerWidget::fill() {
using namespace Info::ChannelEarn;
const auto container = this;
const auto &data = _state;
const auto multiplier = data.usdRate * Data::kEarnMultiplier;
const auto multiplier = data.usdRate;
constexpr auto kMinorLength = 3;
auto availableBalanceValue = rpl::single(

View file

@ -405,8 +405,7 @@ void InnerWidget::fill() {
const auto multiplier = data.usdRate;
const auto creditsToUsdMap = [=](CreditsAmount c) {
const auto creditsMultiplier = _state.creditsEarn.usdRate
* Data::kEarnMultiplier;
const auto creditsMultiplier = _state.creditsEarn.usdRate;
return c ? ToUsd(c, creditsMultiplier, 0) : QString();
};
@ -1101,7 +1100,7 @@ void InnerWidget::fill() {
const auto historyList = tabCurrencyList->entity();
const auto addHistoryEntry = [=](
const Data::EarnHistoryEntry &entry,
const Data::CreditsHistoryEntry &entry,
const tr::phrase<> &text) {
const auto wrap = historyList->add(
object_ptr<Ui::PaddingWrap<Ui::VerticalLayout>>(
@ -1115,9 +1114,7 @@ void InnerWidget::fill() {
text(),
st::channelEarnSemiboldLabel));
const auto isIn
= (entry.type == Data::EarnHistoryEntry::Type::Return
|| entry.type == Data::EarnHistoryEntry::Type::In);
const auto isIn = entry.in;
const auto recipient = Ui::Text::Wrapped(
{ entry.provider },
EntityType::Code);
@ -1134,18 +1131,17 @@ void InnerWidget::fill() {
Ui::AddSkip(inner, st::channelEarnHistoryTwoSkip);
}
const auto isFailed = entry.status
== Data::EarnHistoryEntry::Status::Failed;
const auto isPending = entry.status
== Data::EarnHistoryEntry::Status::Pending;
const auto dateText = (!entry.dateTo.isNull() || isFailed)
const auto isFailed = entry.failed;
const auto isPending = entry.pending;
const auto dateText = (!entry.adsProceedsToDate.isNull()
|| isFailed)
? (FormatDate(entry.date)
+ ' '
+ QChar(8212)
+ ' '
+ (isFailed
? tr::lng_channel_earn_history_out_failed(tr::now)
: FormatDate(entry.dateTo)))
: FormatDate(entry.adsProceedsToDate)))
: isPending
? tr::lng_channel_earn_history_pending(tr::now)
: FormatDate(entry.date);
@ -1166,12 +1162,12 @@ void InnerWidget::fill() {
st::channelEarnHistoryMajorLabel);
addEmojiToMajor(
majorLabel,
rpl::single(entry.amount),
rpl::single(entry.credits),
isIn,
{});
majorLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
majorLabel->setTextColorOverride(color);
const auto minorText = MinorPart(entry.amount);
const auto minorText = MinorPart(entry.credits);
const auto minorLabel = Ui::CreateChild<Ui::FlatLabel>(
wrap,
rpl::single(minorText),
@ -1200,7 +1196,7 @@ void InnerWidget::fill() {
st::channelEarnOverviewMajorLabel);
addEmojiToMajor(
majorLabel,
rpl::single(entry.amount),
rpl::single(entry.credits),
isIn,
{});
majorLabel->setAttribute(
@ -1317,15 +1313,14 @@ void InnerWidget::fill() {
}, wrap->lifetime());
};
const auto handleSlice = [=](const Data::EarnHistorySlice &s) {
using Type = Data::EarnHistoryEntry::Type;
for (const auto &entry : s.list) {
addHistoryEntry(
entry,
(entry.type == Type::In)
? tr::lng_channel_earn_history_in
: (entry.type == Type::Return)
(entry.refunded
? tr::lng_channel_earn_history_return
: tr::lng_channel_earn_history_out);
: entry.in
? tr::lng_channel_earn_history_in
: tr::lng_channel_earn_history_out));
}
historyList->resizeToWidth(listsContainer->width());
};

View file

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "statistics/statistics_data_deserialize.h"
#include "base/debug_log.h"
#include "data/data_channel_earn.h" // kEarnMultiplier.
#include "data/data_statistics_chart.h"
#include "statistics/statistics_types.h"
#include "ui/text/format_values.h" // kCreditsCurrency.
@ -76,10 +75,7 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
line.isHiddenOnStart = ranges::contains(hiddenLines, columnId);
line.y.resize(length);
for (auto i = 0; i < length; i++) {
using Currency = Data::StatisticalCurrency;
const auto multiplier = (result.currency == Currency::Credits)
? Data::kEarnMultiplier
: 1;
const auto multiplier = 1;
const auto value = ChartValue(
base::SafeRound(array.at(i + 1).toDouble()))
* multiplier;