Update API scheme on layer 206.

This commit is contained in:
John Preston 2025-06-23 18:10:07 +04:00
parent dc19f2e76c
commit 0fa50f1951
57 changed files with 556 additions and 348 deletions

View file

@ -470,6 +470,7 @@ PRIVATE
core/crash_report_window.h core/crash_report_window.h
core/crash_reports.cpp core/crash_reports.cpp
core/crash_reports.h core/crash_reports.h
core/credits_amount.h
core/deadlock_detector.h core/deadlock_detector.h
core/file_utilities.cpp core/file_utilities.cpp
core/file_utilities.h core/file_utilities.h
@ -483,7 +484,6 @@ PRIVATE
core/sandbox.h core/sandbox.h
core/shortcuts.cpp core/shortcuts.cpp
core/shortcuts.h core/shortcuts.h
core/stars_amount.h
core/ui_integration.cpp core/ui_integration.cpp
core/ui_integration.h core/ui_integration.h
core/update_checker.cpp core/update_checker.cpp

View file

@ -19,7 +19,7 @@ MTPSuggestedPost SuggestToMTP(SuggestPostOptions suggest) {
return suggest.exists return suggest.exists
? MTP_suggestedPost( ? MTP_suggestedPost(
MTP_flags(suggest.date ? Flag::f_schedule_date : Flag()), MTP_flags(suggest.date ? Flag::f_schedule_date : Flag()),
MTP_long(suggest.stars), StarsAmountToTL(suggest.price()),
MTP_int(suggest.date)) MTP_int(suggest.date))
: MTPSuggestedPost(); : MTPSuggestedPost();
} }

View file

@ -80,16 +80,15 @@ constexpr auto kTransactionsLimit = 100;
}, [](const auto &) { return (const MTPDstarGift*)nullptr; }) }, [](const auto &) { return (const MTPDstarGift*)nullptr; })
: nullptr; : nullptr;
const auto reaction = tl.data().is_reaction(); const auto reaction = tl.data().is_reaction();
const auto amount = Data::FromTL(tl.data().vstars()); const auto amount = CreditsAmountFromTL(tl.data().vstars());
const auto starrefAmount = tl.data().vstarref_amount() const auto starrefAmount = CreditsAmountFromTL(
? Data::FromTL(*tl.data().vstarref_amount()) tl.data().vstarref_amount());
: StarsAmount();
const auto starrefCommission const auto starrefCommission
= tl.data().vstarref_commission_permille().value_or_empty(); = tl.data().vstarref_commission_permille().value_or_empty();
const auto starrefBarePeerId = tl.data().vstarref_peer() const auto starrefBarePeerId = tl.data().vstarref_peer()
? peerFromMTP(*tl.data().vstarref_peer()).value ? peerFromMTP(*tl.data().vstarref_peer()).value
: 0; : 0;
const auto incoming = (amount >= StarsAmount()); const auto incoming = (amount >= CreditsAmount());
const auto paidMessagesCount const auto paidMessagesCount
= tl.data().vpaid_messages().value_or_empty(); = tl.data().vpaid_messages().value_or_empty();
const auto premiumMonthsForStars const auto premiumMonthsForStars
@ -108,7 +107,7 @@ constexpr auto kTransactionsLimit = 100;
.date = base::unixtime::parse(tl.data().vdate().v), .date = base::unixtime::parse(tl.data().vdate().v),
.photoId = photo ? photo->id : 0, .photoId = photo ? photo->id : 0,
.extended = std::move(extended), .extended = std::move(extended),
.credits = Data::FromTL(tl.data().vstars()), .credits = CreditsAmountFromTL(tl.data().vstars()),
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()), .bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
.barePeerId = saveActorId ? peer->id.value : barePeerId, .barePeerId = saveActorId ? peer->id.value : barePeerId,
.bareGiveawayMsgId = uint64( .bareGiveawayMsgId = uint64(
@ -116,7 +115,7 @@ constexpr auto kTransactionsLimit = 100;
.bareGiftStickerId = giftStickerId, .bareGiftStickerId = giftStickerId,
.bareActorId = saveActorId ? barePeerId : uint64(0), .bareActorId = saveActorId ? barePeerId : uint64(0),
.uniqueGift = parsedGift ? parsedGift->unique : nullptr, .uniqueGift = parsedGift ? parsedGift->unique : nullptr,
.starrefAmount = paidMessagesCount ? StarsAmount() : starrefAmount, .starrefAmount = paidMessagesCount ? CreditsAmount() : starrefAmount,
.starrefCommission = paidMessagesCount ? 0 : starrefCommission, .starrefCommission = paidMessagesCount ? 0 : starrefCommission,
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId, .starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId,
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) { .peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
@ -147,7 +146,7 @@ constexpr auto kTransactionsLimit = 100;
.paidMessagesCount = paidMessagesCount, .paidMessagesCount = paidMessagesCount,
.paidMessagesAmount = (paidMessagesCount .paidMessagesAmount = (paidMessagesCount
? starrefAmount ? starrefAmount
: StarsAmount()), : CreditsAmount()),
.paidMessagesCommission = paidMessagesCount ? starrefCommission : 0, .paidMessagesCommission = paidMessagesCount ? starrefCommission : 0,
.starsConverted = int(nonUniqueGift .starsConverted = int(nonUniqueGift
? nonUniqueGift->vconvert_stars().v ? nonUniqueGift->vconvert_stars().v
@ -216,7 +215,7 @@ constexpr auto kTransactionsLimit = 100;
return Data::CreditsStatusSlice{ return Data::CreditsStatusSlice{
.list = std::move(entries), .list = std::move(entries),
.subscriptions = std::move(subscriptions), .subscriptions = std::move(subscriptions),
.balance = Data::FromTL(status.data().vbalance()), .balance = CreditsAmountFromTL(status.data().vbalance()),
.subscriptionsMissingBalance .subscriptionsMissingBalance
= status.data().vsubscriptions_missing_balance().value_or_empty(), = status.data().vsubscriptions_missing_balance().value_or_empty(),
.allLoaded = !status.data().vnext_offset().has_value() .allLoaded = !status.data().vnext_offset().has_value()
@ -300,11 +299,14 @@ void CreditsStatus::request(
using TLResult = MTPpayments_StarsStatus; using TLResult = MTPpayments_StarsStatus;
_requestId = _api.request(MTPpayments_GetStarsStatus( _requestId = _api.request(MTPpayments_GetStarsStatus(
MTP_flags(0),
_peer->isSelf() ? MTP_inputPeerSelf() : _peer->input _peer->isSelf() ? MTP_inputPeerSelf() : _peer->input
)).done([=](const TLResult &result) { )).done([=](const TLResult &result) {
_requestId = 0; _requestId = 0;
const auto &balance = result.data().vbalance(); const auto &balance = result.data().vbalance();
_peer->session().credits().apply(_peer->id, Data::FromTL(balance)); _peer->session().credits().apply(
_peer->id,
CreditsAmountFromTL(balance));
if (const auto onstack = done) { if (const auto onstack = done) {
onstack(StatusFromTL(result, _peer)); onstack(StatusFromTL(result, _peer));
} }
@ -420,13 +422,15 @@ rpl::producer<rpl::no_value, QString> CreditsEarnStatistics::request() {
)).done([=](const MTPpayments_StarsRevenueStats &result) { )).done([=](const MTPpayments_StarsRevenueStats &result) {
const auto &data = result.data(); const auto &data = result.data();
const auto &status = data.vstatus().data(); const auto &status = data.vstatus().data();
using Data::FromTL;
_data = Data::CreditsEarnStatistics{ _data = Data::CreditsEarnStatistics{
.revenueGraph = StatisticalGraphFromTL( .revenueGraph = StatisticalGraphFromTL(
data.vrevenue_graph()), data.vrevenue_graph()),
.currentBalance = FromTL(status.vcurrent_balance()), .currentBalance = CreditsAmountFromTL(
.availableBalance = FromTL(status.vavailable_balance()), status.vcurrent_balance()),
.overallRevenue = FromTL(status.voverall_revenue()), .availableBalance = CreditsAmountFromTL(
status.vavailable_balance()),
.overallRevenue = CreditsAmountFromTL(
status.voverall_revenue()),
.usdRate = data.vusd_rate().v, .usdRate = data.vusd_rate().v,
.isWithdrawalEnabled = status.is_withdrawal_enabled(), .isWithdrawalEnabled = status.is_withdrawal_enabled(),
.nextWithdrawalAt = status.vnext_withdrawal_at() .nextWithdrawalAt = status.vnext_withdrawal_at()

View file

@ -220,7 +220,9 @@ void SendSuggest(
auto action = SendAction(item->history()); auto action = SendAction(item->history());
action.options.suggest.exists = 1; action.options.suggest.exists = 1;
action.options.suggest.date = suggestion->date; action.options.suggest.date = suggestion->date;
action.options.suggest.stars = suggestion->stars; action.options.suggest.priceWhole = suggestion->price.whole();
action.options.suggest.priceNano = suggestion->price.nano();
action.options.suggest.ton = suggestion->price.ton() ? 1 : 0;
action.options.starsApproved = starsApproved; action.options.starsApproved = starsApproved;
action.replyTo.monoforumPeerId = item->history()->amMonoforumAdmin() action.replyTo.monoforumPeerId = item->history()->amMonoforumAdmin()
? item->sublistPeerId() ? item->sublistPeerId()
@ -308,8 +310,10 @@ void SuggestApprovalPrice(
.session = &controller->session(), .session = &controller->session(),
.done = done, .done = done,
.value = { .value = {
.exists = true, .exists = uint32(1),
.stars = uint32(suggestion->stars), .priceWhole = uint32(suggestion->price.whole()),
.priceNano = uint32(suggestion->price.nano()),
.ton = uint32(suggestion->price.ton() ? 1 : 0),
.date = suggestion->date, .date = suggestion->date,
}, },
.mode = SuggestMode::Change, .mode = SuggestMode::Change,
@ -403,8 +407,10 @@ std::shared_ptr<ClickHandler> SuggestChangesClickHandler(
.monoforumPeerId = monoforumPeerId, .monoforumPeerId = monoforumPeerId,
}, },
SuggestPostOptions{ SuggestPostOptions{
.exists = 1, .exists = uint32(1),
.stars = uint32(suggestion->stars), .priceWhole = uint32(suggestion->price.whole()),
.priceNano = uint32(suggestion->price.nano()),
.ton = uint32(suggestion->price.ton() ? 1 : 0),
.date = suggestion->date, .date = suggestion->date,
}, },
cursor, cursor,

View file

@ -2756,6 +2756,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
_session->credits().apply(data); _session->credits().apply(data);
} break; } break;
case mtpc_updateStarsTonBalance: {
const auto &data = update.c_updateStarsBalance();
_session->credits().apply(data);
} break;
case mtpc_updatePaidReactionPrivacy: { case mtpc_updatePaidReactionPrivacy: {
const auto &data = update.c_updatePaidReactionPrivacy(); const auto &data = update.c_updatePaidReactionPrivacy();
_session->api().globalPrivacy().updatePaidReactionShownPeer( _session->api().globalPrivacy().updatePaidReactionShownPeer(

View file

@ -99,7 +99,7 @@ void GiftCreditsBox(
Main::MakeSessionShow(box->uiShow(), &peer->session()), Main::MakeSessionShow(box->uiShow(), &peer->session()),
box->verticalLayout(), box->verticalLayout(),
peer, peer,
StarsAmount(), CreditsAmount(),
[=] { gifted(); box->uiShow()->hideLayer(); }, [=] { gifted(); box->uiShow()->hideLayer(); },
tr::lng_credits_summary_options_subtitle(), tr::lng_credits_summary_options_subtitle(),
{}); {});

View file

@ -411,7 +411,7 @@ void AddTableRow(
table->st().defaultValue.style.font->height); table->st().defaultValue.style.font->height);
const auto label = Ui::CreateChild<Ui::FlatLabel>( const auto label = Ui::CreateChild<Ui::FlatLabel>(
raw, raw,
Lang::FormatStarsAmountDecimal(entry.credits), Lang::FormatCreditsAmountDecimal(entry.credits),
table->st().defaultValue, table->st().defaultValue,
st::defaultPopupMenu); st::defaultPopupMenu);
@ -1630,8 +1630,8 @@ void AddCreditsHistoryEntryTable(
const auto full = int(base::SafeRound(entry.credits.value() const auto full = int(base::SafeRound(entry.credits.value()
/ (1. - (entry.starrefCommission / 1000.)))); / (1. - (entry.starrefCommission / 1000.))));
auto value = Ui::Text::IconEmoji(&st::starIconEmojiColored); auto value = Ui::Text::IconEmoji(&st::starIconEmojiColored);
const auto starsText = Lang::FormatStarsAmountDecimal( const auto starsText = Lang::FormatCreditsAmountDecimal(
StarsAmount{ full }); CreditsAmount{ full });
AddTableRow( AddTableRow(
table, table,
tr::lng_credits_box_history_entry_gift_full_price(), tr::lng_credits_box_history_entry_gift_full_price(),
@ -1787,7 +1787,7 @@ void AddCreditsHistoryEntryTable(
auto value = Ui::Text::IconEmoji(&st::starIconEmojiColored); auto value = Ui::Text::IconEmoji(&st::starIconEmojiColored);
const auto full = (entry.in ? 1 : -1) const auto full = (entry.in ? 1 : -1)
* (entry.credits + entry.paidMessagesAmount); * (entry.credits + entry.paidMessagesAmount);
const auto starsText = Lang::FormatStarsAmountDecimal(full); const auto starsText = Lang::FormatCreditsAmountDecimal(full);
AddTableRow( AddTableRow(
table, table,
tr::lng_credits_paid_messages_full(), tr::lng_credits_paid_messages_full(),

View file

@ -1095,8 +1095,8 @@ void Controller::fillDirectMessagesButton() {
: rpl::single(Ui::Text::IconEmoji( : rpl::single(Ui::Text::IconEmoji(
&st::starIconEmojiColored &st::starIconEmojiColored
).append(' ').append( ).append(' ').append(
Lang::FormatStarsAmountDecimal( Lang::FormatCreditsAmountDecimal(
StarsAmount{ starsPerMessage }))); CreditsAmount{ starsPerMessage })));
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();
AddButtonWithText( AddButtonWithText(
_controls.buttonsLayout, _controls.buttonsLayout,
@ -1907,7 +1907,7 @@ void Controller::fillBotCreditsButton() {
auto &lifetime = _controls.buttonsLayout->lifetime(); auto &lifetime = _controls.buttonsLayout->lifetime();
const auto state = lifetime.make_state<State>(); const auto state = lifetime.make_state<State>();
if (const auto balance = _peer->session().credits().balance(_peer->id)) { if (const auto balance = _peer->session().credits().balance(_peer->id)) {
state->balance = Lang::FormatStarsAmountDecimal(balance); state->balance = Lang::FormatCreditsAmountDecimal(balance);
} }
const auto wrap = _controls.buttonsLayout->add( const auto wrap = _controls.buttonsLayout->add(
@ -1932,7 +1932,7 @@ void Controller::fillBotCreditsButton() {
if (data.balance) { if (data.balance) {
wrap->toggle(true, anim::type::normal); wrap->toggle(true, anim::type::normal);
} }
state->balance = Lang::FormatStarsAmountDecimal(data.balance); state->balance = Lang::FormatCreditsAmountDecimal(data.balance);
}); });
} }
{ {

View file

@ -2007,7 +2007,7 @@ void SoldOutBox(
Data::CreditsHistoryEntry{ Data::CreditsHistoryEntry{
.firstSaleDate = base::unixtime::parse(gift.info.firstSaleDate), .firstSaleDate = base::unixtime::parse(gift.info.firstSaleDate),
.lastSaleDate = base::unixtime::parse(gift.info.lastSaleDate), .lastSaleDate = base::unixtime::parse(gift.info.lastSaleDate),
.credits = StarsAmount(gift.info.stars), .credits = CreditsAmount(gift.info.stars),
.bareGiftStickerId = gift.info.document->id, .bareGiftStickerId = gift.info.document->id,
.peerType = Data::CreditsHistoryEntry::PeerType::Peer, .peerType = Data::CreditsHistoryEntry::PeerType::Peer,
.limitedCount = gift.info.limitedCount, .limitedCount = gift.info.limitedCount,
@ -2039,8 +2039,8 @@ void AddUpgradeButton(
tr::lng_gift_send_unique( tr::lng_gift_send_unique(
lt_price, lt_price,
rpl::single(star.append(' ' rpl::single(star.append(' '
+ Lang::FormatStarsAmountDecimal( + Lang::FormatCreditsAmountDecimal(
StarsAmount{ cost }))), CreditsAmount{ cost }))),
Text::WithEntities), Text::WithEntities),
st::boxLabel, st::boxLabel,
st::defaultPopupMenu, st::defaultPopupMenu,
@ -2355,9 +2355,9 @@ void SendGiftBox(
tr::lng_gift_send_stars_balance( tr::lng_gift_send_stars_balance(
lt_amount, lt_amount,
peer->session().credits().balanceValue( peer->session().credits().balanceValue(
) | rpl::map([=](StarsAmount amount) { ) | rpl::map([=](CreditsAmount amount) {
return base::duplicate(star).append( return base::duplicate(star).append(
Lang::FormatStarsAmountDecimal(amount)); Lang::FormatCreditsAmountDecimal(amount));
}), }),
lt_link, lt_link,
tr::lng_gift_send_stars_balance_link( tr::lng_gift_send_stars_balance_link(
@ -4614,8 +4614,8 @@ void UpgradeBox(
? tr::lng_gift_upgrade_button( ? tr::lng_gift_upgrade_button(
lt_price, lt_price,
rpl::single(star.append( rpl::single(star.append(
' ' + Lang::FormatStarsAmountDecimal( ' ' + Lang::FormatCreditsAmountDecimal(
StarsAmount{ cost }))), CreditsAmount{ cost }))),
Ui::Text::WithEntities) Ui::Text::WithEntities)
: tr::lng_gift_upgrade_confirm(Ui::Text::WithEntities)), : tr::lng_gift_upgrade_confirm(Ui::Text::WithEntities)),
&controller->session(), &controller->session(),

View file

@ -0,0 +1,155 @@
/*
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 "base/basic_types.h"
class MTPstarsAmount;
namespace tl {
template <typename bare>
class boxed;
} // namespace tl
using MTPStarsAmount = tl::boxed<MTPstarsAmount>;
inline constexpr auto kOneStarInNano = int64(1'000'000'000);
enum class CreditsType {
Stars,
Ton,
};
class CreditsAmount {
public:
CreditsAmount() = default;
explicit CreditsAmount(
int64 whole,
CreditsType type = CreditsType::Stars)
: _ton((type == CreditsType::Ton) ? 1 : 0)
, _whole(whole) {
}
CreditsAmount(
int64 whole,
int64 nano,
CreditsType type = CreditsType::Stars)
: _ton((type == CreditsType::Ton) ? 1 : 0)
, _whole(whole)
, _nano(nano) {
normalize();
}
[[nodiscard]] int64 whole() const {
return _whole;
}
[[nodiscard]] int64 nano() const {
return _nano;
}
[[nodiscard]] double value() const {
return double(_whole) + double(_nano) / kOneStarInNano;
}
[[nodiscard]] bool ton() const {
return (_ton == 1);
}
[[nodiscard]] bool stars() const {
return (_ton == 0);
}
[[nodiscard]] CreditsType type() const {
return !_ton ? CreditsType::Stars : CreditsType::Ton;
}
[[nodiscard]] bool empty() const {
return !_whole && !_nano;
}
[[nodiscard]] inline bool operator!() const {
return empty();
}
[[nodiscard]] inline explicit operator bool() const {
return !empty();
}
inline CreditsAmount &operator+=(CreditsAmount other) {
_whole += other._whole;
_nano += other._nano;
normalize();
return *this;
}
inline CreditsAmount &operator-=(CreditsAmount other) {
_whole -= other._whole;
_nano -= other._nano;
normalize();
return *this;
}
inline CreditsAmount &operator*=(int64 multiplier) {
_whole *= multiplier;
_nano *= multiplier;
normalize();
return *this;
}
inline CreditsAmount operator-() const {
auto result = *this;
result *= -1;
return result;
}
friend inline auto operator<=>(CreditsAmount, CreditsAmount) = default;
friend inline bool operator==(CreditsAmount, CreditsAmount) = default;
[[nodiscard]] CreditsAmount abs() const {
return (_whole < 0) ? CreditsAmount(-_whole, -_nano) : *this;
}
private:
void normalize() {
if (_nano < 0) {
const auto shifts = (-_nano + kOneStarInNano - 1)
/ kOneStarInNano;
_nano += shifts * kOneStarInNano;
_whole -= shifts;
} else if (_nano >= kOneStarInNano) {
const auto shifts = _nano / kOneStarInNano;
_nano -= shifts * kOneStarInNano;
_whole += shifts;
}
}
int64 _ton : 2 = 0;
int64 _whole : 62 = 0;
int64 _nano = 0;
};
[[nodiscard]] inline CreditsAmount operator+(
CreditsAmount a,
CreditsAmount b) {
return a += b;
}
[[nodiscard]] inline CreditsAmount operator-(
CreditsAmount a,
CreditsAmount b) {
return a -= b;
}
[[nodiscard]] inline CreditsAmount operator*(CreditsAmount a, int64 b) {
return a *= b;
}
[[nodiscard]] inline CreditsAmount operator*(int64 a, CreditsAmount b) {
return b *= a;
}
[[nodiscard]] CreditsAmount CreditsAmountFromTL(
const MTPStarsAmount &amount);
[[nodiscard]] CreditsAmount CreditsAmountFromTL(
const MTPStarsAmount *amount);
[[nodiscard]] MTPStarsAmount StarsAmountToTL(CreditsAmount amount);

View file

@ -1,108 +0,0 @@
/*
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 "base/basic_types.h"
inline constexpr auto kOneStarInNano = int64(1'000'000'000);
class StarsAmount {
public:
StarsAmount() = default;
explicit StarsAmount(int64 whole) : _whole(whole) {}
StarsAmount(int64 whole, int64 nano) : _whole(whole), _nano(nano) {
normalize();
}
[[nodiscard]] int64 whole() const {
return _whole;
}
[[nodiscard]] int64 nano() const {
return _nano;
}
[[nodiscard]] double value() const {
return double(_whole) + double(_nano) / kOneStarInNano;
}
[[nodiscard]] bool empty() const {
return !_whole && !_nano;
}
[[nodiscard]] inline bool operator!() const {
return empty();
}
[[nodiscard]] inline explicit operator bool() const {
return !empty();
}
inline StarsAmount &operator+=(StarsAmount other) {
_whole += other._whole;
_nano += other._nano;
normalize();
return *this;
}
inline StarsAmount &operator-=(StarsAmount other) {
_whole -= other._whole;
_nano -= other._nano;
normalize();
return *this;
}
inline StarsAmount &operator*=(int64 multiplier) {
_whole *= multiplier;
_nano *= multiplier;
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;
[[nodiscard]] StarsAmount abs() const {
return (_whole < 0) ? StarsAmount(-_whole, -_nano) : *this;
}
private:
int64 _whole = 0;
int64 _nano = 0;
void normalize() {
if (_nano < 0) {
const auto shifts = (-_nano + kOneStarInNano - 1)
/ kOneStarInNano;
_nano += shifts * kOneStarInNano;
_whole -= shifts;
} else if (_nano >= kOneStarInNano) {
const auto shifts = _nano / kOneStarInNano;
_nano -= shifts * kOneStarInNano;
_whole += shifts;
}
}
};
[[nodiscard]] inline StarsAmount operator+(StarsAmount a, StarsAmount b) {
return a += b;
}
[[nodiscard]] inline StarsAmount operator-(StarsAmount a, StarsAmount b) {
return a -= b;
}
[[nodiscard]] inline StarsAmount operator*(StarsAmount a, int64 b) {
return a *= b;
}
[[nodiscard]] inline StarsAmount operator*(int64 a, StarsAmount b) {
return b *= a;
}

View file

@ -19,11 +19,6 @@ constexpr auto kReloadThreshold = 60 * crl::time(1000);
} // namespace } // namespace
StarsAmount FromTL(const MTPStarsAmount &value) {
const auto &data = value.data();
return StarsAmount(data.vamount().v, data.vnanos().v);
}
Credits::Credits(not_null<Main::Session*> session) Credits::Credits(not_null<Main::Session*> session)
: _session(session) : _session(session)
, _reload([=] { load(true); }) { , _reload([=] { load(true); }) {
@ -32,7 +27,7 @@ Credits::Credits(not_null<Main::Session*> session)
Credits::~Credits() = default; Credits::~Credits() = default;
void Credits::apply(const MTPDupdateStarsBalance &data) { void Credits::apply(const MTPDupdateStarsBalance &data) {
apply(FromTL(data.vbalance())); apply(CreditsAmountFromTL(data.vbalance()));
} }
rpl::producer<float64> Credits::rateValue( rpl::producer<float64> Credits::rateValue(
@ -80,13 +75,13 @@ rpl::producer<bool> Credits::loadedValue() const {
) | rpl::then(_loadedChanges.events() | rpl::map_to(true)); ) | rpl::then(_loadedChanges.events() | rpl::map_to(true));
} }
StarsAmount Credits::balance() const { CreditsAmount Credits::balance() const {
return _nonLockedBalance.current(); return _nonLockedBalance.current();
} }
StarsAmount Credits::balance(PeerId peerId) const { CreditsAmount Credits::balance(PeerId peerId) const {
const auto it = _cachedPeerBalances.find(peerId); const auto it = _cachedPeerBalances.find(peerId);
return (it != _cachedPeerBalances.end()) ? it->second : StarsAmount(); return (it != _cachedPeerBalances.end()) ? it->second : CreditsAmount();
} }
uint64 Credits::balanceCurrency(PeerId peerId) const { uint64 Credits::balanceCurrency(PeerId peerId) const {
@ -94,19 +89,19 @@ uint64 Credits::balanceCurrency(PeerId peerId) const {
return (it != _cachedPeerCurrencyBalances.end()) ? it->second : 0; return (it != _cachedPeerCurrencyBalances.end()) ? it->second : 0;
} }
rpl::producer<StarsAmount> Credits::balanceValue() const { rpl::producer<CreditsAmount> Credits::balanceValue() const {
return _nonLockedBalance.value(); return _nonLockedBalance.value();
} }
void Credits::updateNonLockedValue() { void Credits::updateNonLockedValue() {
_nonLockedBalance = (_balance >= _locked) _nonLockedBalance = (_balance >= _locked)
? (_balance - _locked) ? (_balance - _locked)
: StarsAmount(); : CreditsAmount();
} }
void Credits::lock(StarsAmount count) { void Credits::lock(CreditsAmount count) {
Expects(loaded()); Expects(loaded());
Expects(count >= StarsAmount(0)); Expects(count >= CreditsAmount(0));
Expects(_locked + count <= _balance); Expects(_locked + count <= _balance);
_locked += count; _locked += count;
@ -114,8 +109,8 @@ void Credits::lock(StarsAmount count) {
updateNonLockedValue(); updateNonLockedValue();
} }
void Credits::unlock(StarsAmount count) { void Credits::unlock(CreditsAmount count) {
Expects(count >= StarsAmount(0)); Expects(count >= CreditsAmount(0));
Expects(_locked >= count); Expects(_locked >= count);
_locked -= count; _locked -= count;
@ -123,12 +118,12 @@ void Credits::unlock(StarsAmount count) {
updateNonLockedValue(); updateNonLockedValue();
} }
void Credits::withdrawLocked(StarsAmount count) { void Credits::withdrawLocked(CreditsAmount count) {
Expects(count >= StarsAmount(0)); Expects(count >= CreditsAmount(0));
Expects(_locked >= count); Expects(_locked >= count);
_locked -= count; _locked -= count;
apply(_balance >= count ? (_balance - count) : StarsAmount(0)); apply(_balance >= count ? (_balance - count) : CreditsAmount(0));
invalidate(); invalidate();
} }
@ -136,7 +131,7 @@ void Credits::invalidate() {
_reload.call(); _reload.call();
} }
void Credits::apply(StarsAmount balance) { void Credits::apply(CreditsAmount balance) {
_balance = balance; _balance = balance;
updateNonLockedValue(); updateNonLockedValue();
@ -146,7 +141,7 @@ void Credits::apply(StarsAmount balance) {
} }
} }
void Credits::apply(PeerId peerId, StarsAmount balance) { void Credits::apply(PeerId peerId, CreditsAmount balance) {
_cachedPeerBalances[peerId] = balance; _cachedPeerBalances[peerId] = balance;
_refreshedByPeerId.fire_copy(peerId); _refreshedByPeerId.fire_copy(peerId);
} }
@ -166,3 +161,27 @@ bool Credits::statsEnabled() const {
} }
} // namespace Data } // namespace Data
CreditsAmount CreditsAmountFromTL(const MTPStarsAmount &amount) {
return amount.match([&](const MTPDstarsAmount &data) {
return CreditsAmount(
data.vamount().v,
data.vnanos().v,
CreditsType::Stars);
}, [&](const MTPDstarsTonAmount &data) {
return CreditsAmount(
data.vamount().v / uint64(1'000'000'000),
data.vamount().v % uint64(1'000'000'000),
CreditsType::Ton);
});
}
CreditsAmount CreditsAmountFromTL(const MTPStarsAmount *amount) {
return amount ? CreditsAmountFromTL(*amount) : CreditsAmount();
}
MTPStarsAmount StarsAmountToTL(CreditsAmount amount) {
return amount.ton() ? MTP_starsTonAmount(
MTP_long(amount.whole() * uint64(1'000'000'000) + amount.nano())
) : MTP_starsAmount(MTP_long(amount.whole()), MTP_int(amount.nano()));
}

View file

@ -13,23 +13,21 @@ class Session;
namespace Data { namespace Data {
[[nodiscard]] StarsAmount FromTL(const MTPStarsAmount &value);
class Credits final { class Credits final {
public: public:
explicit Credits(not_null<Main::Session*> session); explicit Credits(not_null<Main::Session*> session);
~Credits(); ~Credits();
void load(bool force = false); void load(bool force = false);
void apply(StarsAmount balance); void apply(CreditsAmount balance);
void apply(PeerId peerId, StarsAmount balance); void apply(PeerId peerId, CreditsAmount balance);
[[nodiscard]] bool loaded() const; [[nodiscard]] bool loaded() const;
[[nodiscard]] rpl::producer<bool> loadedValue() const; [[nodiscard]] rpl::producer<bool> loadedValue() const;
[[nodiscard]] StarsAmount balance() const; [[nodiscard]] CreditsAmount balance() const;
[[nodiscard]] StarsAmount balance(PeerId peerId) const; [[nodiscard]] CreditsAmount balance(PeerId peerId) const;
[[nodiscard]] rpl::producer<StarsAmount> balanceValue() const; [[nodiscard]] rpl::producer<CreditsAmount> balanceValue() const;
[[nodiscard]] rpl::producer<float64> rateValue( [[nodiscard]] rpl::producer<float64> rateValue(
not_null<PeerData*> ownedBotOrChannel); not_null<PeerData*> ownedBotOrChannel);
@ -40,9 +38,9 @@ public:
void applyCurrency(PeerId peerId, uint64 balance); void applyCurrency(PeerId peerId, uint64 balance);
[[nodiscard]] uint64 balanceCurrency(PeerId peerId) const; [[nodiscard]] uint64 balanceCurrency(PeerId peerId) const;
void lock(StarsAmount count); void lock(CreditsAmount count);
void unlock(StarsAmount count); void unlock(CreditsAmount count);
void withdrawLocked(StarsAmount count); void withdrawLocked(CreditsAmount count);
void invalidate(); void invalidate();
void apply(const MTPDupdateStarsBalance &data); void apply(const MTPDupdateStarsBalance &data);
@ -54,12 +52,12 @@ private:
std::unique_ptr<rpl::lifetime> _loader; std::unique_ptr<rpl::lifetime> _loader;
base::flat_map<PeerId, StarsAmount> _cachedPeerBalances; base::flat_map<PeerId, CreditsAmount> _cachedPeerBalances;
base::flat_map<PeerId, uint64> _cachedPeerCurrencyBalances; base::flat_map<PeerId, uint64> _cachedPeerCurrencyBalances;
StarsAmount _balance; CreditsAmount _balance;
StarsAmount _locked; CreditsAmount _locked;
rpl::variable<StarsAmount> _nonLockedBalance; rpl::variable<CreditsAmount> _nonLockedBalance;
rpl::event_stream<> _loadedChanges; rpl::event_stream<> _loadedChanges;
crl::time _lastLoaded = 0; crl::time _lastLoaded = 0;
float64 _rate = 0.; float64 _rate = 0.;

View file

@ -59,7 +59,7 @@ struct CreditsHistoryEntry final {
QDateTime lastSaleDate; QDateTime lastSaleDate;
PhotoId photoId = 0; PhotoId photoId = 0;
std::vector<CreditsHistoryMedia> extended; std::vector<CreditsHistoryMedia> extended;
StarsAmount credits; CreditsAmount credits;
uint64 bareMsgId = 0; uint64 bareMsgId = 0;
uint64 barePeerId = 0; uint64 barePeerId = 0;
uint64 bareGiveawayMsgId = 0; uint64 bareGiveawayMsgId = 0;
@ -72,7 +72,7 @@ struct CreditsHistoryEntry final {
uint64 stargiftId = 0; uint64 stargiftId = 0;
std::shared_ptr<UniqueGift> uniqueGift; std::shared_ptr<UniqueGift> uniqueGift;
Fn<std::vector<CreditsHistoryEntry>()> pinnedSavedGifts; Fn<std::vector<CreditsHistoryEntry>()> pinnedSavedGifts;
StarsAmount starrefAmount; CreditsAmount starrefAmount;
int starrefCommission = 0; int starrefCommission = 0;
uint64 starrefRecipientId = 0; uint64 starrefRecipientId = 0;
PeerType peerType; PeerType peerType;
@ -80,7 +80,7 @@ struct CreditsHistoryEntry final {
QDateTime successDate; QDateTime successDate;
QString successLink; QString successLink;
int paidMessagesCount = 0; int paidMessagesCount = 0;
StarsAmount paidMessagesAmount; CreditsAmount paidMessagesAmount;
int paidMessagesCommission = 0; int paidMessagesCommission = 0;
int limitedCount = 0; int limitedCount = 0;
int limitedLeft = 0; int limitedLeft = 0;
@ -115,7 +115,7 @@ struct CreditsStatusSlice final {
using OffsetToken = QString; using OffsetToken = QString;
std::vector<CreditsHistoryEntry> list; std::vector<CreditsHistoryEntry> list;
std::vector<SubscriptionEntry> subscriptions; std::vector<SubscriptionEntry> subscriptions;
StarsAmount balance; CreditsAmount balance;
uint64 subscriptionsMissingBalance = 0; uint64 subscriptionsMissingBalance = 0;
bool allLoaded = false; bool allLoaded = false;
OffsetToken token; OffsetToken token;

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "core/stars_amount.h" #include "core/credits_amount.h"
#include "data/data_statistics_chart.h" #include "data/data_statistics_chart.h"
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
@ -22,9 +22,9 @@ struct CreditsEarnStatistics final {
&& overallRevenue; && overallRevenue;
} }
Data::StatisticalGraph revenueGraph; Data::StatisticalGraph revenueGraph;
StarsAmount currentBalance; CreditsAmount currentBalance;
StarsAmount availableBalance; CreditsAmount availableBalance;
StarsAmount overallRevenue; CreditsAmount overallRevenue;
float64 usdRate = 0.; float64 usdRate = 0.;
bool isWithdrawalEnabled = false; bool isWithdrawalEnabled = false;
QDateTime nextWithdrawalAt; QDateTime nextWithdrawalAt;

View file

@ -21,11 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h" #include "storage/localstorage.h"
namespace Data { namespace Data {
namespace {
constexpr auto kMaxSuggestStars = 1'000'000'000;
} // namespace
WebPageDraft WebPageDraft::FromItem(not_null<HistoryItem*> item) { WebPageDraft WebPageDraft::FromItem(not_null<HistoryItem*> item) {
const auto previewMedia = item->media(); const auto previewMedia = item->media();
@ -122,10 +117,10 @@ void ApplyPeerCloudDraft(
const auto &data = suggested->data(); const auto &data = suggested->data();
suggest.exists = 1; suggest.exists = 1;
suggest.date = data.vschedule_date().value_or_empty(); suggest.date = data.vschedule_date().value_or_empty();
suggest.stars = uint32(std::clamp( const auto price = CreditsAmountFromTL(data.vprice());
data.vstars_amount().v, suggest.priceWhole = price.whole();
uint64(), suggest.priceNano = price.nano();
uint64(kMaxSuggestStars))); suggest.ton = price.ton() ? 1 : 0;
} }
auto cloudDraft = std::make_unique<Draft>( auto cloudDraft = std::make_unique<Draft>(
textWithTags, textWithTags,

View file

@ -2514,7 +2514,7 @@ MediaGiftBox::MediaGiftBox(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
not_null<PeerData*> from, not_null<PeerData*> from,
GiftType type, GiftType type,
int count) int64 count)
: MediaGiftBox(parent, from, GiftCode{ .count = count, .type = type }) { : MediaGiftBox(parent, from, GiftCode{ .count = count, .type = type }) {
} }

View file

@ -136,6 +136,7 @@ struct GiveawayResults {
enum class GiftType : uchar { enum class GiftType : uchar {
Premium, // count - months Premium, // count - months
Credits, // count - credits Credits, // count - credits
Ton, // count - nano tons
StarGift, // count - stars StarGift, // count - stars
}; };
@ -155,7 +156,7 @@ struct GiftCode {
int starsUpgradedBySender = 0; int starsUpgradedBySender = 0;
int limitedCount = 0; int limitedCount = 0;
int limitedLeft = 0; int limitedLeft = 0;
int count = 0; int64 count = 0;
GiftType type = GiftType::Premium; GiftType type = GiftType::Premium;
bool viaGiveaway : 1 = false; bool viaGiveaway : 1 = false;
bool transferred : 1 = false; bool transferred : 1 = false;
@ -678,7 +679,7 @@ public:
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
not_null<PeerData*> from, not_null<PeerData*> from,
GiftType type, GiftType type,
int count); int64 count);
MediaGiftBox( MediaGiftBox(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
not_null<PeerData*> from, not_null<PeerData*> from,

View file

@ -2245,7 +2245,7 @@ void MessageReactions::scheduleSendPaid(
_paid->scheduledPrivacySet = true; _paid->scheduledPrivacySet = true;
} }
if (count > 0) { if (count > 0) {
_item->history()->session().credits().lock(StarsAmount(count)); _item->history()->session().credits().lock(CreditsAmount(count));
} }
_item->history()->owner().reactions().schedulePaid(_item); _item->history()->owner().reactions().schedulePaid(_item);
} }
@ -2259,7 +2259,7 @@ void MessageReactions::cancelScheduledPaid() {
if (_paid->scheduledFlag) { if (_paid->scheduledFlag) {
if (const auto amount = int(_paid->scheduled)) { if (const auto amount = int(_paid->scheduled)) {
_item->history()->session().credits().unlock( _item->history()->session().credits().unlock(
StarsAmount(amount)); CreditsAmount(amount));
} }
_paid->scheduled = 0; _paid->scheduled = 0;
_paid->scheduledFlag = 0; _paid->scheduledFlag = 0;
@ -2322,9 +2322,9 @@ void MessageReactions::finishPaidSending(
if (const auto amount = send.count) { if (const auto amount = send.count) {
const auto credits = &_item->history()->session().credits(); const auto credits = &_item->history()->session().credits();
if (success) { if (success) {
credits->withdrawLocked(StarsAmount(amount)); credits->withdrawLocked(CreditsAmount(amount));
} else { } else {
credits->unlock(StarsAmount(amount)); credits->unlock(CreditsAmount(amount));
} }
} }
} }

View file

@ -192,9 +192,18 @@ struct FullReplyTo {
struct SuggestPostOptions { struct SuggestPostOptions {
uint32 exists : 1 = 0; uint32 exists : 1 = 0;
uint32 stars : 31 = 0; uint32 priceWhole : 31 = 0;
uint32 priceNano : 31 = 0;
uint32 ton : 1 = 0;
TimeId date = 0; TimeId date = 0;
[[nodiscard]] CreditsAmount price() const {
return CreditsAmount(
priceWhole,
priceNano,
ton ? CreditsType::Ton : CreditsType::Stars);
}
explicit operator bool() const { explicit operator bool() const {
return exists != 0; return exists != 0;
} }

View file

@ -868,9 +868,8 @@ StarRefProgram ParseStarRefProgram(const MTPStarRefProgram *program) {
const auto &data = program->data(); const auto &data = program->data();
result.commission = data.vcommission_permille().v; result.commission = data.vcommission_permille().v;
result.durationMonths = data.vduration_months().value_or_empty(); result.durationMonths = data.vduration_months().value_or_empty();
result.revenuePerUser = data.vdaily_revenue_per_user() result.revenuePerUser = CreditsAmountFromTL(
? Data::FromTL(*data.vdaily_revenue_per_user()) data.vdaily_revenue_per_user());
: StarsAmount();
result.endDate = data.vend_date().value_or_empty(); result.endDate = data.vend_date().value_or_empty();
return result; return result;
} }

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "core/stars_amount.h" #include "core/credits_amount.h"
#include "data/components/credits.h" #include "data/components/credits.h"
#include "data/data_birthday.h" #include "data/data_birthday.h"
#include "data/data_peer.h" #include "data/data_peer.h"
@ -28,7 +28,7 @@ using DisallowedGiftTypes = base::flags<DisallowedGiftType>;
} // namespace Api } // namespace Api
struct StarRefProgram { struct StarRefProgram {
StarsAmount revenuePerUser; CreditsAmount revenuePerUser;
TimeId endDate = 0; TimeId endDate = 0;
ushort commission = 0; ushort commission = 0;
uint8 durationMonths = 0; uint8 durationMonths = 0;

View file

@ -1681,11 +1681,21 @@ ServiceAction ParseServiceAction(
content.transactionId = data.vcharge().data().vid().v; content.transactionId = data.vcharge().data().vid().v;
result.content = content; result.content = content;
}, [&](const MTPDmessageActionGiftStars &data) { }, [&](const MTPDmessageActionGiftStars &data) {
auto content = ActionGiftStars(); auto content = ActionGiftCredits();
content.cost = Ui::FillAmountAndCurrency( content.cost = Ui::FillAmountAndCurrency(
data.vamount().v, data.vamount().v,
qs(data.vcurrency())).toUtf8(); qs(data.vcurrency())).toUtf8();
content.credits = data.vstars().v; content.amount = CreditsAmount(data.vstars().v, CreditsType::Stars);
result.content = content;
}, [&](const MTPDmessageActionGiftTon &data) {
auto content = ActionGiftCredits();
content.cost = Ui::FillAmountAndCurrency(
data.vamount().v,
qs(data.vcurrency())).toUtf8();
content.amount = CreditsAmount(
data.vamount().v / uint64(1'000'000'000),
data.vamount().v % uint64(1'000'000'000),
CreditsType::Ton);
result.content = content; result.content = content;
}, [&](const MTPDmessageActionPrizeStars &data) { }, [&](const MTPDmessageActionPrizeStars &data) {
result.content = ActionPrizeStars{ result.content = ActionPrizeStars{
@ -1761,7 +1771,7 @@ ServiceAction ParseServiceAction(
result.content = ActionSuggestedPostApproval{ result.content = ActionSuggestedPostApproval{
.rejectComment = data.vreject_comment().value_or_empty(), .rejectComment = data.vreject_comment().value_or_empty(),
.scheduleDate = data.vschedule_date().value_or_empty(), .scheduleDate = data.vschedule_date().value_or_empty(),
.stars = int(data.vstars_amount().value_or_empty()), .price = CreditsAmountFromTL(data.vprice()),
.rejected = data.is_rejected(), .rejected = data.is_rejected(),
.balanceTooLow = data.is_balance_too_low(), .balanceTooLow = data.is_balance_too_low(),
}; };

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "scheme.h" #include "scheme.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/variant.h" #include "base/variant.h"
#include "core/credits_amount.h"
#include "data/data_peer_id.h" #include "data/data_peer_id.h"
#include <QtCore/QSize> #include <QtCore/QSize>
@ -658,9 +659,9 @@ struct ActionPaymentRefunded {
Utf8String transactionId; Utf8String transactionId;
}; };
struct ActionGiftStars { struct ActionGiftCredits {
Utf8String cost; Utf8String cost;
int credits = 0; CreditsAmount amount;
}; };
struct ActionPrizeStars { struct ActionPrizeStars {
@ -701,7 +702,7 @@ struct ActionTodoAppendTasks {
struct ActionSuggestedPostApproval { struct ActionSuggestedPostApproval {
Utf8String rejectComment; Utf8String rejectComment;
TimeId scheduleDate = 0; TimeId scheduleDate = 0;
int stars = 0; CreditsAmount price;
bool rejected = false; bool rejected = false;
bool balanceTooLow = false; bool balanceTooLow = false;
}; };
@ -749,7 +750,7 @@ struct ServiceAction {
ActionGiveawayResults, ActionGiveawayResults,
ActionBoostApply, ActionBoostApply,
ActionPaymentRefunded, ActionPaymentRefunded,
ActionGiftStars, ActionGiftCredits,
ActionPrizeStars, ActionPrizeStars,
ActionStarGift, ActionStarGift,
ActionPaidMessagesRefunded, ActionPaidMessagesRefunded,

View file

@ -1353,16 +1353,16 @@ auto HtmlWriter::Wrap::pushMessage(
+ " refunded back " + " refunded back "
+ amount; + amount;
return result; return result;
}, [&](const ActionGiftStars &data) { }, [&](const ActionGiftCredits &data) {
if (!data.credits || data.cost.isEmpty()) { if (!data.amount || data.cost.isEmpty()) {
return serviceFrom + " sent you a gift."; return serviceFrom + " sent you a gift.";
} }
return serviceFrom return serviceFrom
+ " sent you a gift for " + " sent you a gift for "
+ data.cost + data.cost
+ ": " + ": "
+ QString::number(data.credits).toUtf8() + QString::number(data.amount.value()).toUtf8()
+ " Telegram Stars."; + (data.amount.ton() ? " TON." : " Telegram Stars.");
}, [&](const ActionPrizeStars &data) { }, [&](const ActionPrizeStars &data) {
return "You won a prize in a giveaway organized by " return "You won a prize in a giveaway organized by "
+ peers.wrapPeerName(data.peerId) + peers.wrapPeerName(data.peerId)
@ -1450,8 +1450,10 @@ auto HtmlWriter::Wrap::pushMessage(
return serviceFrom return serviceFrom
+ (data.rejected ? " rejected " : " approved ") + (data.rejected ? " rejected " : " approved ")
+ "your suggested post" + "your suggested post"
+ (data.stars + (data.price
? ", for " + QString::number(data.stars).toUtf8() + " stars" ? (", for "
+ QString::number(data.price.value()).toUtf8()
+ (data.price.ton() ? " TON" : " stars"))
: "") : "")
+ (data.scheduleDate + (data.scheduleDate
? (", " ? (", "

View file

@ -644,14 +644,17 @@ QByteArray SerializeMessage(
pushBare("peer_name", wrapPeerName(data.peerId)); pushBare("peer_name", wrapPeerName(data.peerId));
push("peer_id", data.peerId); push("peer_id", data.peerId);
push("charge_id", data.transactionId); push("charge_id", data.transactionId);
}, [&](const ActionGiftStars &data) { }, [&](const ActionGiftCredits &data) {
pushActor(); pushActor();
pushAction("send_stars_gift"); pushAction(data.amount.ton()
? "send_ton_gift"
: "send_stars_gift");
if (!data.cost.isEmpty()) { if (!data.cost.isEmpty()) {
push("cost", data.cost); push("cost", data.cost);
} }
if (data.credits) { if (data.amount) {
push("stars", data.credits); push("amount_whole", data.amount.whole());
push("amount_nano", data.amount.nano());
} }
}, [&](const ActionPrizeStars &data) { }, [&](const ActionPrizeStars &data) {
pushActor(); pushActor();
@ -717,7 +720,9 @@ QByteArray SerializeMessage(
push("comment", data.rejectComment); push("comment", data.rejectComment);
} }
} else { } else {
push("stars_amount", NumberToString(data.stars)); push("price_amount_whole", NumberToString(data.price.whole()));
push("price_amount_nano", NumberToString(data.price.nano()));
push("price_currency", data.price.ton() ? "TON" : "Stars");
push("scheduled_date", data.scheduleDate); push("scheduled_date", data.scheduleDate);
} }
}, [](v::null_t) {}); }, [](v::null_t) {});

View file

@ -1949,7 +1949,7 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
AddComponents(HistoryMessageSuggestedPost::Bit()); AddComponents(HistoryMessageSuggestedPost::Bit());
} }
auto suggest = Get<HistoryMessageSuggestedPost>(); auto suggest = Get<HistoryMessageSuggestedPost>();
suggest->stars = edition.suggest.stars; suggest->price = edition.suggest.price;
suggest->date = edition.suggest.date; suggest->date = edition.suggest.date;
suggest->accepted = edition.suggest.accepted; suggest->accepted = edition.suggest.accepted;
suggest->rejected = edition.suggest.rejected; suggest->rejected = edition.suggest.rejected;
@ -4023,7 +4023,7 @@ void HistoryItem::createComponents(CreateConfig &&config) {
} }
if (const auto suggest = Get<HistoryMessageSuggestedPost>()) { if (const auto suggest = Get<HistoryMessageSuggestedPost>()) {
suggest->stars = config.suggest.stars; suggest->price = config.suggest.price;
suggest->date = config.suggest.date; suggest->date = config.suggest.date;
suggest->accepted = config.suggest.accepted; suggest->accepted = config.suggest.accepted;
suggest->rejected = config.suggest.rejected; suggest->rejected = config.suggest.rejected;
@ -4668,7 +4668,7 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) {
const auto &data = action.c_messageActionSuggestedPostApproval(); const auto &data = action.c_messageActionSuggestedPostApproval();
UpdateComponents(HistoryServiceSuggestDecision::Bit()); UpdateComponents(HistoryServiceSuggestDecision::Bit());
const auto decision = Get<HistoryServiceSuggestDecision>(); const auto decision = Get<HistoryServiceSuggestDecision>();
decision->stars = data.vstars_amount().value_or_empty(); decision->price = CreditsAmountFromTL(data.vprice());
decision->balanceTooLow = data.is_balance_too_low(); decision->balanceTooLow = data.is_balance_too_low();
decision->rejected = data.is_rejected(); decision->rejected = data.is_rejected();
decision->rejectComment = qs(data.vreject_comment().value_or_empty()); decision->rejectComment = qs(data.vreject_comment().value_or_empty());
@ -5735,6 +5735,42 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
return result; return result;
}; };
auto prepareGiftTon = [&](
const MTPDmessageActionGiftTon &action) {
auto result = PreparedServiceText();
const auto isSelf = (_from->id == _from->session().userPeerId());
const auto peer = isSelf ? _history->peer : _from;
const auto amount = action.vamount().v;
const auto currency = qs(action.vcurrency());
const auto cost = AmountAndStarCurrency(
&_history->session(),
amount,
currency);
const auto anonymous = _from->isServiceUser();
if (anonymous) {
result.text = tr::lng_action_gift_received_anonymous(
tr::now,
lt_cost,
cost,
Ui::Text::WithEntities);
} else {
result.links.push_back(peer->createOpenLink());
result.text = isSelf
? tr::lng_action_gift_sent(tr::now,
lt_cost,
cost,
Ui::Text::WithEntities)
: tr::lng_action_gift_received(
tr::now,
lt_user,
Ui::Text::Link(peer->shortName(), 1), // Link 1.
lt_cost,
cost,
Ui::Text::WithEntities);
}
return result;
};
auto prepareGiftPrize = [&]( auto prepareGiftPrize = [&](
const MTPDmessageActionPrizeStars &action) { const MTPDmessageActionPrizeStars &action) {
auto result = PreparedServiceText(); auto result = PreparedServiceText();
@ -6073,6 +6109,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
prepareBoostApply, prepareBoostApply,
preparePaymentRefunded, preparePaymentRefunded,
prepareGiftStars, prepareGiftStars,
prepareGiftTon,
prepareGiftPrize, prepareGiftPrize,
prepareStarGift, prepareStarGift,
prepareStarGiftUnique, prepareStarGiftUnique,
@ -6194,6 +6231,12 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
_from, _from,
Data::GiftType::Credits, Data::GiftType::Credits,
data.vstars().v); data.vstars().v);
}, [&](const MTPDmessageActionGiftTon &data) {
_media = std::make_unique<Data::MediaGiftBox>(
this,
_from,
Data::GiftType::Ton,
data.vamount().v);
}, [&](const MTPDmessageActionPrizeStars &data) { }, [&](const MTPDmessageActionPrizeStars &data) {
_media = std::make_unique<Data::MediaGiftBox>( _media = std::make_unique<Data::MediaGiftBox>(
this, this,

View file

@ -623,7 +623,7 @@ struct HistoryMessageFactcheck
struct HistoryMessageSuggestedPost struct HistoryMessageSuggestedPost
: RuntimeComponent<HistoryMessageSuggestedPost, HistoryItem> { : RuntimeComponent<HistoryMessageSuggestedPost, HistoryItem> {
int stars = 0; CreditsAmount price;
TimeId date = 0; TimeId date = 0;
mtpRequestId requestId = 0; mtpRequestId requestId = 0;
bool accepted = false; bool accepted = false;
@ -701,7 +701,7 @@ struct HistoryServiceTodoAppendTasks
struct HistoryServiceSuggestDecision struct HistoryServiceSuggestDecision
: RuntimeComponent<HistoryServiceSuggestDecision, HistoryItem> : RuntimeComponent<HistoryServiceSuggestDecision, HistoryItem>
, HistoryServiceDependentData { , HistoryServiceDependentData {
int stars = 0; CreditsAmount price;
TimeId date = 0; TimeId date = 0;
QString rejectComment; QString rejectComment;
bool rejected = false; bool rejected = false;

View file

@ -333,7 +333,7 @@ HistoryMessageSuggestInfo::HistoryMessageSuggestInfo(
return; return;
} }
const auto &fields = data->data(); const auto &fields = data->data();
stars = fields.vstars_amount().v; price = CreditsAmountFromTL(fields.vprice());
date = fields.vschedule_date().value_or_empty(); date = fields.vschedule_date().value_or_empty();
accepted = fields.is_accepted(); accepted = fields.is_accepted();
rejected = fields.is_rejected(); rejected = fields.is_rejected();
@ -350,7 +350,7 @@ HistoryMessageSuggestInfo::HistoryMessageSuggestInfo(
if (!options.exists) { if (!options.exists) {
return; return;
} }
stars = options.stars; price = options.price();
date = options.date; date = options.date;
exists = true; exists = true;
} }

View file

@ -154,7 +154,7 @@ struct HistoryMessageSuggestInfo {
explicit HistoryMessageSuggestInfo(const Api::SendOptions &options); explicit HistoryMessageSuggestInfo(const Api::SendOptions &options);
explicit HistoryMessageSuggestInfo(SuggestPostOptions options); explicit HistoryMessageSuggestInfo(SuggestPostOptions options);
int stars = 0; CreditsAmount price;
TimeId date = 0; TimeId date = 0;
bool accepted = false; bool accepted = false;
bool rejected = false; bool rejected = false;

View file

@ -78,7 +78,9 @@ void ChooseSuggestPriceBox(
wrap, wrap,
st::editTagField, st::editTagField,
tr::lng_paid_cost_placeholder(), tr::lng_paid_cost_placeholder(),
args.value.stars ? QString::number(args.value.stars) : QString(), (args.value.price()
? QString::number(args.value.price().value())
: QString()),
limit); limit);
const auto field = owned.data(); const auto field = owned.data();
wrap->widthValue() | rpl::start_with_next([=](int width) { wrap->widthValue() | rpl::start_with_next([=](int width) {
@ -137,14 +139,18 @@ void ChooseSuggestPriceBox(
Ui::AddDividerText(container, tr::lng_suggest_options_date_about()); Ui::AddDividerText(container, tr::lng_suggest_options_date_about());
AssertIsDebug()//tr::lng_suggest_options_offer AssertIsDebug()//tr::lng_suggest_options_offer
const auto save = [=] { const auto save = [=] {
const auto now = uint32(field->getLastText().toULongLong()); const auto now = field->getLastText().toDouble();
if (now > limit) { if (now > limit) {
field->showError(); field->showError();
return; return;
} }
const auto value = CreditsAmount(
int(std::floor(now)),
int(base::SafeRound((now - std::floor(now)) * 1'000'000'000.)));
args.done({ args.done({
.exists = true, .exists = true,
.stars = now, .priceWhole = uint32(value.whole()),
.priceNano = uint32(value.nano()),
.date = state->date.current(), .date = state->date.current(),
}); });
}; };
@ -234,15 +240,21 @@ void SuggestOptions::updateTexts() {
} }
TextWithEntities SuggestOptions::composeText() const { TextWithEntities SuggestOptions::composeText() const {
if (!_values.stars && !_values.date) { if (!_values.price() && !_values.date) {
return tr::lng_suggest_bar_text(tr::now, Ui::Text::WithEntities); return tr::lng_suggest_bar_text(tr::now, Ui::Text::WithEntities);
} else if (!_values.date && _values.price().ton()) {
return tr::lng_suggest_bar_priced(AssertIsDebug()
tr::now,
lt_amount,
TextWithEntities{ Lang::FormatCreditsAmountDecimal(_values.price()) + " TON" },
Ui::Text::WithEntities);
} else if (!_values.date) { } else if (!_values.date) {
return tr::lng_suggest_bar_priced( return tr::lng_suggest_bar_priced(
tr::now, tr::now,
lt_amount, lt_amount,
TextWithEntities{ QString::number(_values.stars) + " stars" }, TextWithEntities{ Lang::FormatCreditsAmountDecimal(_values.price()) + " stars" },
Ui::Text::WithEntities); Ui::Text::WithEntities);
} else if (!_values.stars) { } else if (!_values.price()) {
return tr::lng_suggest_bar_dated( return tr::lng_suggest_bar_dated(
tr::now, tr::now,
lt_date, lt_date,
@ -250,11 +262,21 @@ TextWithEntities SuggestOptions::composeText() const {
langDateTime(base::unixtime::parse(_values.date)), langDateTime(base::unixtime::parse(_values.date)),
}, },
Ui::Text::WithEntities); Ui::Text::WithEntities);
} else if (_values.price().ton()) {
return tr::lng_suggest_bar_priced_dated(
tr::now,
lt_amount,
TextWithEntities{ Lang::FormatCreditsAmountDecimal(_values.price()) + " TON," },
lt_date,
TextWithEntities{
langDateTime(base::unixtime::parse(_values.date)),
},
Ui::Text::WithEntities);
} }
return tr::lng_suggest_bar_priced_dated( return tr::lng_suggest_bar_priced_dated(
tr::now, tr::now,
lt_amount, lt_amount,
TextWithEntities{ QString::number(_values.stars) + " stars," }, TextWithEntities{ Lang::FormatCreditsAmountDecimal(_values.price()) + " stars," },
lt_date, lt_date,
TextWithEntities{ TextWithEntities{
langDateTime(base::unixtime::parse(_values.date)), langDateTime(base::unixtime::parse(_values.date)),

View file

@ -376,6 +376,10 @@ bool PremiumGift::gift() const {
return _data.slug.isEmpty() || !_data.channel; return _data.slug.isEmpty() || !_data.channel;
} }
bool PremiumGift::tonGift() const {
return (_data.type == Data::GiftType::Ton);
}
bool PremiumGift::starGift() const { bool PremiumGift::starGift() const {
return (_data.type == Data::GiftType::StarGift); return (_data.type == Data::GiftType::StarGift);
} }

View file

@ -52,6 +52,7 @@ public:
private: private:
[[nodiscard]] bool incomingGift() const; [[nodiscard]] bool incomingGift() const;
[[nodiscard]] bool outgoingGift() const; [[nodiscard]] bool outgoingGift() const;
[[nodiscard]] bool tonGift() const;
[[nodiscard]] bool starGift() const; [[nodiscard]] bool starGift() const;
[[nodiscard]] bool starGiftUpgrade() const; [[nodiscard]] bool starGiftUpgrade() const;
[[nodiscard]] bool gift() const; [[nodiscard]] bool gift() const;

View file

@ -76,7 +76,7 @@ struct Changes {
if (wasSuggest->date != nowSuggest->date) { if (wasSuggest->date != nowSuggest->date) {
result.date = true; result.date = true;
} }
if (wasSuggest->stars != nowSuggest->stars) { if (wasSuggest->price != nowSuggest->price) {
result.price = true; result.price = true;
} }
const auto wasText = original->originalText(); const auto wasText = original->originalText();
@ -178,7 +178,7 @@ auto GenerateSuggestDecisionMedia(
fadedFg)); fadedFg));
} }
} else { } else {
const auto stars = decision->stars; const auto price = decision->price;
pushText( pushText(
TextWithEntities( TextWithEntities(
).append(Emoji(kAgreement)).append(' ').append( ).append(Emoji(kAgreement)).append(' ').append(
@ -206,12 +206,16 @@ auto GenerateSuggestDecisionMedia(
date.time(), date.time(),
QLocale::ShortFormat))), QLocale::ShortFormat))),
Ui::Text::WithEntities)), Ui::Text::WithEntities)),
(stars (price
? st::chatSuggestInfoMiddleMargin ? st::chatSuggestInfoMiddleMargin
: st::chatSuggestInfoLastMargin)); : st::chatSuggestInfoLastMargin));
if (stars) { if (price) {
const auto amount = Ui::Text::Bold( const auto amount = Ui::Text::Bold(price.ton()
tr::lng_prize_credits_amount(tr::now, lt_count, stars)); ? (Lang::FormatCreditsAmountDecimal(price) + u" TON"_q)
: tr::lng_prize_credits_amount(
tr::now,
lt_count_decimal,
price.value()));
pushText( pushText(
TextWithEntities( TextWithEntities(
).append(Emoji(kMoney)).append(' ').append( ).append(Emoji(kMoney)).append(' ').append(
@ -320,12 +324,14 @@ auto GenerateSuggestRequestMedia(
((changes && changes->price) ((changes && changes->price)
? tr::lng_suggest_change_price_label ? tr::lng_suggest_change_price_label
: tr::lng_suggest_action_price_label)(tr::now), : tr::lng_suggest_action_price_label)(tr::now),
Ui::Text::Bold(suggest->stars Ui::Text::Bold(!suggest->price
? tr::lng_prize_credits_amount( ? tr::lng_suggest_action_price_free(tr::now)
: suggest->price.ton() AssertIsDebug()
? (Lang::FormatCreditsAmountDecimal(suggest->price) + u" TON"_q)
: tr::lng_prize_credits_amount(
tr::now, tr::now,
lt_count, lt_count,
suggest->stars) suggest->price.value())),
: tr::lng_suggest_action_price_free(tr::now)),
}); });
entries.push_back({ entries.push_back({
((changes && changes->date) ((changes && changes->date)

View file

@ -135,8 +135,8 @@ void InnerWidget::fill() {
return _state.overallRevenue; return _state.overallRevenue;
}) })
); );
auto valueToString = [](StarsAmount v) { auto valueToString = [](CreditsAmount v) {
return Lang::FormatStarsAmountDecimal(v); return Lang::FormatCreditsAmountDecimal(v);
}; };
if (data.revenueGraph.chart) { if (data.revenueGraph.chart) {
@ -161,7 +161,7 @@ void InnerWidget::fill() {
Ui::AddSkip(container, st::channelEarnOverviewTitleSkip); Ui::AddSkip(container, st::channelEarnOverviewTitleSkip);
const auto addOverview = [&]( const auto addOverview = [&](
rpl::producer<StarsAmount> value, rpl::producer<CreditsAmount> value,
const tr::phrase<> &text) { const tr::phrase<> &text) {
const auto line = container->add( const auto line = container->add(
Ui::CreateSkipWidget(container, 0), Ui::CreateSkipWidget(container, 0),
@ -177,8 +177,10 @@ void InnerWidget::fill() {
line, line,
std::move( std::move(
value value
) | rpl::map([=](StarsAmount v) { ) | rpl::map([=](CreditsAmount v) {
return v ? ToUsd(v, multiplier, kMinorLength) : QString(); return v
? ToUsd(v, multiplier, kMinorLength)
: QString();
}), }),
st::channelEarnOverviewSubMinorLabel); st::channelEarnOverviewSubMinorLabel);
rpl::combine( rpl::combine(
@ -254,7 +256,7 @@ void InnerWidget::fill() {
(peer()->isSelf() (peer()->isSelf()
? rpl::duplicate(overallBalanceValue) | rpl::type_erased() ? rpl::duplicate(overallBalanceValue) | rpl::type_erased()
: rpl::duplicate(availableBalanceValue) : rpl::duplicate(availableBalanceValue)
) | rpl::map([=](StarsAmount v) { ) | rpl::map([=](CreditsAmount v) {
return v ? ToUsd(v, multiplier, kMinorLength) : QString(); return v ? ToUsd(v, multiplier, kMinorLength) : QString();
})); }));
container->resizeToWidth(container->width()); container->resizeToWidth(container->width());

View file

@ -603,7 +603,7 @@ object_ptr<Ui::BoxContent> JoinStarRefBox(
const auto layout = box->verticalLayout(); const auto layout = box->verticalLayout();
const auto session = &initialRecipient->session(); const auto session = &initialRecipient->session();
auto text = Ui::Text::Colorized(Ui::CreditsEmoji(session)); auto text = Ui::Text::Colorized(Ui::CreditsEmoji(session));
text.append(Lang::FormatStarsAmountRounded(average)); text.append(Lang::FormatCreditsAmountRounded(average));
layout->add( layout->add(
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
box, box,

View file

@ -50,11 +50,11 @@ QString ToUsd(
Data::EarnInt value, Data::EarnInt value,
float64 rate, float64 rate,
int afterFloat) { int afterFloat) {
return ToUsd(StarsAmount(value), rate, afterFloat); return ToUsd(CreditsAmount(value), rate, afterFloat);
} }
QString ToUsd( QString ToUsd(
StarsAmount value, CreditsAmount value,
float64 rate, float64 rate,
int afterFloat) { int afterFloat) {
constexpr auto kApproximately = QChar(0x2248); constexpr auto kApproximately = QChar(0x2248);

View file

@ -18,7 +18,7 @@ namespace Info::ChannelEarn {
float64 rate, float64 rate,
int afterFloat); int afterFloat);
[[nodiscard]] QString ToUsd( [[nodiscard]] QString ToUsd(
StarsAmount value, CreditsAmount value,
float64 rate, float64 rate,
int afterFloat); int afterFloat);

View file

@ -258,9 +258,9 @@ void InnerWidget::load() {
} }
const auto &data = d.vstatus().data(); const auto &data = d.vstatus().data();
auto &e = _state.creditsEarn; auto &e = _state.creditsEarn;
e.currentBalance = Data::FromTL(data.vcurrent_balance()); e.currentBalance = CreditsAmountFromTL(data.vcurrent_balance());
e.availableBalance = Data::FromTL(data.vavailable_balance()); e.availableBalance = CreditsAmountFromTL(data.vavailable_balance());
e.overallRevenue = Data::FromTL(data.voverall_revenue()); e.overallRevenue = CreditsAmountFromTL(data.voverall_revenue());
e.isWithdrawalEnabled = data.is_withdrawal_enabled(); e.isWithdrawalEnabled = data.is_withdrawal_enabled();
e.nextWithdrawalAt = data.vnext_withdrawal_at() e.nextWithdrawalAt = data.vnext_withdrawal_at()
? base::unixtime::parse( ? base::unixtime::parse(
@ -395,7 +395,7 @@ void InnerWidget::fill() {
//constexpr auto kApproximately = QChar(0x2248); //constexpr auto kApproximately = QChar(0x2248);
const auto multiplier = data.usdRate; const auto multiplier = data.usdRate;
const auto creditsToUsdMap = [=](StarsAmount c) { const auto creditsToUsdMap = [=](CreditsAmount c) {
const auto creditsMultiplier = _state.creditsEarn.usdRate const auto creditsMultiplier = _state.creditsEarn.usdRate
* Data::kEarnMultiplier; * Data::kEarnMultiplier;
return c ? ToUsd(c, creditsMultiplier, 0) : QString(); return c ? ToUsd(c, creditsMultiplier, 0) : QString();
@ -707,7 +707,7 @@ void InnerWidget::fill() {
const auto addOverview = [&]( const auto addOverview = [&](
rpl::producer<EarnInt> currencyValue, rpl::producer<EarnInt> currencyValue,
rpl::producer<StarsAmount> creditsValue, rpl::producer<CreditsAmount> creditsValue,
const tr::phrase<> &text, const tr::phrase<> &text,
bool showCurrency, bool showCurrency,
bool showCredits) { bool showCredits) {
@ -741,8 +741,10 @@ void InnerWidget::fill() {
const auto creditsLabel = Ui::CreateChild<Ui::FlatLabel>( const auto creditsLabel = Ui::CreateChild<Ui::FlatLabel>(
line, line,
rpl::duplicate(creditsValue) | rpl::map([](StarsAmount value) { rpl::duplicate(
return Lang::FormatStarsAmountDecimal(value); creditsValue
) | rpl::map([](CreditsAmount value) {
return Lang::FormatCreditsAmountDecimal(value);
}), }),
st::channelEarnOverviewMajorLabel); st::channelEarnOverviewMajorLabel);
const auto icon = Ui::CreateSingleStarWidget( const auto icon = Ui::CreateSingleStarWidget(
@ -761,7 +763,7 @@ void InnerWidget::fill() {
int available, int available,
const QSize &size, const QSize &size,
const QSize &creditsSize, const QSize &creditsSize,
StarsAmount credits) { CreditsAmount credits) {
const auto skip = st::channelEarnOverviewSubMinorLabelPos.x(); const auto skip = st::channelEarnOverviewSubMinorLabelPos.x();
line->resize(line->width(), size.height()); line->resize(line->width(), size.height());
minorLabel->moveToLeft( minorLabel->moveToLeft(

View file

@ -940,19 +940,20 @@ rpl::producer<uint64> AddCurrencyAction(
return state->balance.value(); return state->balance.value();
} }
rpl::producer<StarsAmount> AddCreditsAction( rpl::producer<CreditsAmount> AddCreditsAction(
not_null<UserData*> user, not_null<UserData*> user,
not_null<Ui::VerticalLayout*> wrap, not_null<Ui::VerticalLayout*> wrap,
not_null<Controller*> controller) { not_null<Controller*> controller) {
struct State final { struct State final {
rpl::variable<StarsAmount> balance; rpl::variable<CreditsAmount> balance;
}; };
const auto state = wrap->lifetime().make_state<State>(); const auto state = wrap->lifetime().make_state<State>();
const auto parentController = controller->parentController(); const auto parentController = controller->parentController();
const auto wrapButton = AddActionButton( const auto wrapButton = AddActionButton(
wrap, wrap,
tr::lng_manage_peer_bot_balance_credits(), tr::lng_manage_peer_bot_balance_credits(),
state->balance.value() | rpl::map(rpl::mappers::_1 > StarsAmount(0)), state->balance.value(
) | rpl::map(rpl::mappers::_1 > CreditsAmount(0)),
[=] { parentController->showSection(Info::BotEarn::Make(user)); }, [=] { parentController->showSection(Info::BotEarn::Make(user)); },
nullptr); nullptr);
{ {
@ -992,7 +993,7 @@ rpl::producer<StarsAmount> AddCreditsAction(
) | rpl::start_with_next([=, &st]( ) | rpl::start_with_next([=, &st](
int width, int width,
const QString &button, const QString &button,
StarsAmount balance) { CreditsAmount balance) {
const auto available = width const auto available = width
- rect::m::sum::h(st.padding) - rect::m::sum::h(st.padding)
- st.style.font->width(button) - st.style.font->width(button)
@ -1000,7 +1001,7 @@ rpl::producer<StarsAmount> AddCreditsAction(
name->setMarkedText( name->setMarkedText(
base::duplicate(icon) base::duplicate(icon)
.append(QChar(' ')) .append(QChar(' '))
.append(Lang::FormatStarsAmountDecimal(balance)), .append(Lang::FormatCreditsAmountDecimal(balance)),
Core::TextContext({ Core::TextContext({
.session = &user->session(), .session = &user->session(),
.repaint = [=] { name->update(); }, .repaint = [=] { name->update(); },
@ -2404,7 +2405,7 @@ void ActionsFiller::addBalanceActions(not_null<UserData*> user) {
std::move(currencyBalance), std::move(currencyBalance),
std::move(creditsBalance) std::move(creditsBalance)
) | rpl::map((rpl::mappers::_1 > 0) ) | rpl::map((rpl::mappers::_1 > 0)
|| (rpl::mappers::_2 > StarsAmount(0)))); || (rpl::mappers::_2 > CreditsAmount(0))));
} }
void ActionsFiller::addInviteToGroupAction(not_null<UserData*> user) { void ActionsFiller::addInviteToGroupAction(not_null<UserData*> user) {

View file

@ -927,7 +927,7 @@ void CreditsRow::init() {
st::semiboldTextStyle, st::semiboldTextStyle,
TextWithEntities() TextWithEntities()
.append(_entry.in ? QChar('+') : kMinus) .append(_entry.in ? QChar('+') : kMinus)
.append(Lang::FormatStarsAmountDecimal(_entry.credits.abs())) .append(Lang::FormatCreditsAmountDecimal(_entry.credits.abs()))
.append(QChar(' ')) .append(QChar(' '))
.append(manager.creditsEmoji()), .append(manager.creditsEmoji()),
kMarkupTextOptions, kMarkupTextOptions,

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "lang/lang_tag.h" #include "lang/lang_tag.h"
#include "core/stars_amount.h" #include "core/credits_amount.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/text/text.h" #include "ui/text/text.h"
#include "base/qt/qt_common_adapters.h" #include "base/qt/qt_common_adapters.h"
@ -952,18 +952,18 @@ QString FormatExactCountDecimal(float64 number) {
return QLocale().toString(number, 'f', QLocale::FloatingPointShortest); return QLocale().toString(number, 'f', QLocale::FloatingPointShortest);
} }
ShortenedCount FormatStarsAmountToShort(StarsAmount amount) { ShortenedCount FormatCreditsAmountToShort(CreditsAmount amount) {
const auto attempt = FormatCountToShort(amount.whole()); const auto attempt = FormatCountToShort(amount.whole());
return attempt.shortened ? attempt : ShortenedCount{ return attempt.shortened ? attempt : ShortenedCount{
.string = FormatStarsAmountDecimal(amount), .string = FormatCreditsAmountDecimal(amount),
}; };
} }
QString FormatStarsAmountDecimal(StarsAmount amount) { QString FormatCreditsAmountDecimal(CreditsAmount amount) {
return FormatExactCountDecimal(amount.value()); return FormatExactCountDecimal(amount.value());
} }
QString FormatStarsAmountRounded(StarsAmount amount) { QString FormatCreditsAmountRounded(CreditsAmount amount) {
const auto value = amount.value(); const auto value = amount.value();
return FormatExactCountDecimal(base::SafeRound(value * 100.) / 100.); return FormatExactCountDecimal(base::SafeRound(value * 100.) / 100.);
} }

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
class StarsAmount; class CreditsAmount;
enum lngtag_count : int; enum lngtag_count : int;
@ -29,9 +29,10 @@ struct ShortenedCount {
[[nodiscard]] ShortenedCount FormatCountToShort(int64 number); [[nodiscard]] ShortenedCount FormatCountToShort(int64 number);
[[nodiscard]] QString FormatCountDecimal(int64 number); [[nodiscard]] QString FormatCountDecimal(int64 number);
[[nodiscard]] QString FormatExactCountDecimal(float64 number); [[nodiscard]] QString FormatExactCountDecimal(float64 number);
[[nodiscard]] ShortenedCount FormatStarsAmountToShort(StarsAmount amount); [[nodiscard]] ShortenedCount FormatCreditsAmountToShort(
[[nodiscard]] QString FormatStarsAmountDecimal(StarsAmount amount); CreditsAmount amount);
[[nodiscard]] QString FormatStarsAmountRounded(StarsAmount amount); [[nodiscard]] QString FormatCreditsAmountDecimal(CreditsAmount amount);
[[nodiscard]] QString FormatCreditsAmountRounded(CreditsAmount amount);
struct PluralResult { struct PluralResult {
int keyShift = 0; int keyShift = 0;

View file

@ -192,7 +192,8 @@ messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction; messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction; messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction; messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
messageActionSuggestedPostApproval#af42ae29 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int stars_amount:flags.4?long = MessageAction; messageActionSuggestedPostApproval#ee7a1596 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int price:flags.4?StarsAmount = MessageAction;
messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog; dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -1925,6 +1926,7 @@ payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<Connected
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots; payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount; starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
starsTonAmount#74aee3e0 amount:long = StarsAmount;
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers; messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers; messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
@ -1994,7 +1996,7 @@ todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:fla
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion; todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
suggestedPost#95ee6a6d flags:# accepted:flags.1?true rejected:flags.2?true stars_amount:long schedule_date:flags.0?int = SuggestedPost; suggestedPost#e8e37e5 flags:# accepted:flags.1?true rejected:flags.2?true price:flags.3?StarsAmount schedule_date:flags.0?int = SuggestedPost;
---functions--- ---functions---
@ -2571,8 +2573,8 @@ payments.applyGiftCode#f6e26854 slug:string = Updates;
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo; payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates; payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>; payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>;
payments.getStarsStatus#104fcfa7 peer:InputPeer = payments.StarsStatus; payments.getStarsStatus#4ea9b3bf flags:# ton:flags.0?true peer:InputPeer = payments.StarsStatus;
payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus; payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true ton:flags.4?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus;
payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult; payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult;
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates; payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true peer:InputPeer = payments.StarsRevenueStats; payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true peer:InputPeer = payments.StarsRevenueStats;

View file

@ -631,7 +631,7 @@ void Form::processReceipt(const MTPDpayments_paymentReceiptStars &data) {
ImageLocation()) ImageLocation())
: nullptr, : nullptr,
.peerId = peerFromUser(data.vbot_id().v), .peerId = peerFromUser(data.vbot_id().v),
.credits = StarsAmount(data.vtotal_amount().v), .credits = CreditsAmount(data.vtotal_amount().v),
.date = data.vdate().v, .date = data.vdate().v,
}; };
_updates.fire(CreditsReceiptReady{ .data = receiptData }); _updates.fire(CreditsReceiptReady{ .data = receiptData });

View file

@ -211,7 +211,7 @@ struct CreditsReceiptData {
QString description; QString description;
PhotoData *photo = nullptr; PhotoData *photo = nullptr;
PeerId peerId = PeerId(0); PeerId peerId = PeerId(0);
StarsAmount credits; CreditsAmount credits;
TimeId date = 0; TimeId date = 0;
}; };

View file

@ -33,7 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Settings { namespace Settings {
[[nodiscard]] not_null<Ui::RpWidget*> AddBalanceWidget( [[nodiscard]] not_null<Ui::RpWidget*> AddBalanceWidget(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
rpl::producer<StarsAmount> balanceValue, rpl::producer<CreditsAmount> balanceValue,
bool rightAlign, bool rightAlign,
rpl::producer<float64> opacityValue = nullptr); rpl::producer<float64> opacityValue = nullptr);
} // namespace Settings } // namespace Settings

View file

@ -41,7 +41,7 @@ struct PaidReactionBoxArgs {
QString channel; QString channel;
Fn<rpl::producer<TextWithContext>(rpl::producer<int> amount)> submit; Fn<rpl::producer<TextWithContext>(rpl::producer<int> amount)> submit;
rpl::producer<StarsAmount> balanceValue; rpl::producer<CreditsAmount> balanceValue;
Fn<void(int, uint64)> send; Fn<void(int, uint64)> send;
}; };

View file

@ -405,7 +405,7 @@ void Credits::setupContent() {
const auto balanceAmount = Ui::CreateChild<Ui::FlatLabel>( const auto balanceAmount = Ui::CreateChild<Ui::FlatLabel>(
balanceLine, balanceLine,
_controller->session().credits().balanceValue( _controller->session().credits().balanceValue(
) | rpl::map(Lang::FormatStarsAmountDecimal), ) | rpl::map(Lang::FormatCreditsAmountDecimal),
st::creditsSettingsBigBalance); st::creditsSettingsBigBalance);
balanceAmount->sizeValue() | rpl::start_with_next([=] { balanceAmount->sizeValue() | rpl::start_with_next([=] {
balanceLine->resize( balanceLine->resize(
@ -718,7 +718,7 @@ Fn<void()> BuyStarsHandler::handler(
const auto options = _api const auto options = _api
? _api->options() ? _api->options()
: Data::CreditTopupOptions(); : Data::CreditTopupOptions();
const auto amount = StarsAmount(); const auto amount = CreditsAmount();
const auto weak = Ui::MakeWeak(box); const auto weak = Ui::MakeWeak(box);
FillCreditOptions(show, inner, self, amount, [=] { FillCreditOptions(show, inner, self, amount, [=] {
if (const auto strong = weak.data()) { if (const auto strong = weak.data()) {

View file

@ -141,13 +141,13 @@ class Balance final
public: public:
using Ui::RpWidget::RpWidget; using Ui::RpWidget::RpWidget;
void setBalance(StarsAmount balance) { void setBalance(CreditsAmount balance) {
_balance = balance; _balance = balance;
_tooltip = Lang::FormatStarsAmountDecimal(balance); _tooltip = Lang::FormatCreditsAmountDecimal(balance);
} }
void enterEventHook(QEnterEvent *e) override { void enterEventHook(QEnterEvent *e) override {
if (_balance >= StarsAmount(10'000)) { if (_balance >= CreditsAmount(10'000)) {
Ui::Tooltip::Show(1000, this); Ui::Tooltip::Show(1000, this);
} }
} }
@ -170,7 +170,7 @@ public:
private: private:
QString _tooltip; QString _tooltip;
StarsAmount _balance; CreditsAmount _balance;
}; };
@ -506,7 +506,7 @@ void FillCreditOptions(
std::shared_ptr<Main::SessionShow> show, std::shared_ptr<Main::SessionShow> show,
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer, not_null<PeerData*> peer,
StarsAmount minimumCredits, CreditsAmount minimumCredits,
Fn<void()> paid, Fn<void()> paid,
rpl::producer<QString> subtitle, rpl::producer<QString> subtitle,
std::vector<Data::CreditTopupOption> preloadedTopupOptions) { std::vector<Data::CreditTopupOption> preloadedTopupOptions) {
@ -552,12 +552,12 @@ void FillCreditOptions(
- int(singleStarWidth * 1.5); - int(singleStarWidth * 1.5);
const auto buttonHeight = st.height + rect::m::sum::v(st.padding); const auto buttonHeight = st.height + rect::m::sum::v(st.padding);
const auto minCredits = (!options.empty() const auto minCredits = (!options.empty()
&& (minimumCredits > StarsAmount(options.back().credits))) && (minimumCredits > CreditsAmount(options.back().credits)))
? StarsAmount() ? CreditsAmount()
: minimumCredits; : minimumCredits;
for (auto i = 0; i < options.size(); i++) { for (auto i = 0; i < options.size(); i++) {
const auto &option = options[i]; const auto &option = options[i];
if (StarsAmount(option.credits) < minCredits) { if (CreditsAmount(option.credits) < minCredits) {
continue; continue;
} }
const auto button = [&] { const auto button = [&] {
@ -684,7 +684,7 @@ void FillCreditOptions(
not_null<Ui::RpWidget*> AddBalanceWidget( not_null<Ui::RpWidget*> AddBalanceWidget(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
rpl::producer<StarsAmount> balanceValue, rpl::producer<CreditsAmount> balanceValue,
bool rightAlign, bool rightAlign,
rpl::producer<float64> opacityValue) { rpl::producer<float64> opacityValue) {
struct State final { struct State final {
@ -720,10 +720,10 @@ not_null<Ui::RpWidget*> AddBalanceWidget(
}; };
std::move( std::move(
balanceValue balanceValue
) | rpl::start_with_next([=](StarsAmount value) { ) | rpl::start_with_next([=](CreditsAmount value) {
state->count.setText( state->count.setText(
st::semiboldTextStyle, st::semiboldTextStyle,
Lang::FormatStarsAmountToShort(value).string); Lang::FormatCreditsAmountToShort(value).string);
balance->setBalance(value); balance->setBalance(value);
resize(); resize();
}, balance->lifetime()); }, balance->lifetime());
@ -1468,7 +1468,7 @@ void GenericCreditsEntryBox(
: (e.gift && !creditsHistoryStarGift) : (e.gift && !creditsHistoryStarGift)
? QString() ? QString()
: QString(kMinus)) : QString(kMinus))
.append(Lang::FormatStarsAmountDecimal(e.credits.abs())) .append(Lang::FormatCreditsAmountDecimal(e.credits.abs()))
.append(QChar(' ')) .append(QChar(' '))
.append(owner->customEmojiManager().creditsEmoji()); .append(owner->customEmojiManager().creditsEmoji());
text->setMarkedText( text->setMarkedText(
@ -2069,7 +2069,7 @@ void GiftedCreditsBox(
? tr::lng_credits_box_history_entry_gift_name ? tr::lng_credits_box_history_entry_gift_name
: tr::lng_credits_box_history_entry_gift_sent)(tr::now), : tr::lng_credits_box_history_entry_gift_sent)(tr::now),
.date = base::unixtime::parse(date), .date = base::unixtime::parse(date),
.credits = StarsAmount(count), .credits = CreditsAmount(count),
.bareMsgId = uint64(), .bareMsgId = uint64(),
.barePeerId = (anonymous ? uint64() : peer->id.value), .barePeerId = (anonymous ? uint64() : peer->id.value),
.peerType = (anonymous ? PeerType::Fragment : PeerType::Peer), .peerType = (anonymous ? PeerType::Fragment : PeerType::Peer),
@ -2092,7 +2092,7 @@ void CreditsPrizeBox(
.title = QString(), .title = QString(),
.description = TextWithEntities(), .description = TextWithEntities(),
.date = base::unixtime::parse(date), .date = base::unixtime::parse(date),
.credits = StarsAmount(data.count), .credits = CreditsAmount(data.count),
.barePeerId = data.channel .barePeerId = data.channel
? data.channel->id.value ? data.channel->id.value
: 0, : 0,
@ -2115,7 +2115,7 @@ void GlobalStarGiftBox(
box, box,
show, show,
Data::CreditsHistoryEntry{ Data::CreditsHistoryEntry{
.credits = StarsAmount(data.stars), .credits = CreditsAmount(data.stars),
.bareGiftStickerId = data.document->id, .bareGiftStickerId = data.document->id,
.bareGiftOwnerId = ownerId, .bareGiftOwnerId = ownerId,
.bareGiftResaleRecipientId = ((resaleRecipientId != selfId) .bareGiftResaleRecipientId = ((resaleRecipientId != selfId)
@ -2142,7 +2142,7 @@ Data::CreditsHistoryEntry SavedStarGiftEntry(
return { return {
.description = data.message, .description = data.message,
.date = base::unixtime::parse(data.date), .date = base::unixtime::parse(data.date),
.credits = StarsAmount(data.info.stars), .credits = CreditsAmount(data.info.stars),
.bareMsgId = uint64(data.manageId.userMessageId().bare), .bareMsgId = uint64(data.manageId.userMessageId().bare),
.barePeerId = data.fromId.value, .barePeerId = data.fromId.value,
.bareGiftStickerId = data.info.document->id, .bareGiftStickerId = data.info.document->id,
@ -2221,7 +2221,7 @@ void StarGiftViewBox(
.id = data.slug, .id = data.slug,
.description = data.message, .description = data.message,
.date = base::unixtime::parse(item->date()), .date = base::unixtime::parse(item->date()),
.credits = StarsAmount(data.count), .credits = CreditsAmount(data.count),
.bareMsgId = uint64(item->id.bare), .bareMsgId = uint64(item->id.bare),
.barePeerId = fromId.value, .barePeerId = fromId.value,
.bareGiftStickerId = data.document ? data.document->id : 0, .bareGiftStickerId = data.document ? data.document->id : 0,
@ -2272,7 +2272,7 @@ void ShowRefundInfoBox(
auto info = Data::CreditsHistoryEntry(); auto info = Data::CreditsHistoryEntry();
info.id = refund->transactionId; info.id = refund->transactionId;
info.date = base::unixtime::parse(item->date()); info.date = base::unixtime::parse(item->date());
info.credits = StarsAmount(refund->amount); info.credits = CreditsAmount(refund->amount);
info.barePeerId = refund->peer->id.value; info.barePeerId = refund->peer->id.value;
info.peerType = Data::CreditsHistoryEntry::PeerType::Peer; info.peerType = Data::CreditsHistoryEntry::PeerType::Peer;
info.refunded = true; info.refunded = true;
@ -2370,7 +2370,7 @@ void SmallBalanceBox(
Fn<void()> paid) { Fn<void()> paid) {
Expects(show->session().credits().loaded()); Expects(show->session().credits().loaded());
auto credits = StarsAmount(wholeCredits); auto credits = CreditsAmount(wholeCredits);
box->setWidth(st::boxWideWidth); box->setWidth(st::boxWideWidth);
box->addButton(tr::lng_close(), [=] { box->closeBox(); }); box->addButton(tr::lng_close(), [=] { box->closeBox(); });
@ -2399,8 +2399,8 @@ void SmallBalanceBox(
}); });
auto needed = show->session().credits().balanceValue( auto needed = show->session().credits().balanceValue(
) | rpl::map([=](StarsAmount balance) { ) | rpl::map([=](CreditsAmount balance) {
return (balance < credits) ? (credits - balance) : StarsAmount(); return (balance < credits) ? (credits - balance) : CreditsAmount();
}); });
const auto content = [&]() -> Ui::Premium::TopBarAbstract* { const auto content = [&]() -> Ui::Premium::TopBarAbstract* {
return box->setPinnedToTopContent(object_ptr<Ui::Premium::TopBar>( return box->setPinnedToTopContent(object_ptr<Ui::Premium::TopBar>(
@ -2412,8 +2412,8 @@ void SmallBalanceBox(
rpl::duplicate( rpl::duplicate(
needed needed
) | rpl::filter( ) | rpl::filter(
rpl::mappers::_1 > StarsAmount(0) rpl::mappers::_1 > CreditsAmount(0)
) | rpl::map([](StarsAmount amount) { ) | rpl::map([](CreditsAmount amount) {
return amount.value(); return amount.value();
})), })),
.about = (v::is<SmallBalanceSubscription>(source) .about = (v::is<SmallBalanceSubscription>(source)
@ -2507,7 +2507,7 @@ void AddWithdrawalWidget(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
rpl::producer<QString> secondButtonUrl, rpl::producer<QString> secondButtonUrl,
rpl::producer<StarsAmount> availableBalanceValue, rpl::producer<CreditsAmount> availableBalanceValue,
rpl::producer<QDateTime> dateValue, rpl::producer<QDateTime> dateValue,
bool withdrawalEnabled, bool withdrawalEnabled,
rpl::producer<QString> usdValue) { rpl::producer<QString> usdValue) {
@ -2522,8 +2522,8 @@ void AddWithdrawalWidget(
labels, labels,
rpl::duplicate( rpl::duplicate(
availableBalanceValue availableBalanceValue
) | rpl::map([](StarsAmount v) { ) | rpl::map([](CreditsAmount v) {
return Lang::FormatStarsAmountDecimal(v); return Lang::FormatCreditsAmountDecimal(v);
}), }),
st::channelEarnBalanceMajorLabel); st::channelEarnBalanceMajorLabel);
const auto icon = Ui::CreateSingleStarWidget( const auto icon = Ui::CreateSingleStarWidget(
@ -2622,7 +2622,7 @@ void AddWithdrawalWidget(
st::settingsPremiumIconStar, st::settingsPremiumIconStar,
{ 0, -st::moderateBoxExpandInnerSkip, 0, 0 }, { 0, -st::moderateBoxExpandInnerSkip, 0, 0 },
true)); true));
using Balance = rpl::variable<StarsAmount>; using Balance = rpl::variable<CreditsAmount>;
const auto currentBalance = input->lifetime().make_state<Balance>( const auto currentBalance = input->lifetime().make_state<Balance>(
rpl::duplicate(availableBalanceValue)); rpl::duplicate(availableBalanceValue));
const auto process = [=] { const auto process = [=] {
@ -2834,7 +2834,7 @@ void MaybeRequestBalanceIncrease(
state->lifetime.destroy(); state->lifetime.destroy();
const auto balance = session->credits().balance(); const auto balance = session->credits().balance();
if (StarsAmount(credits) <= balance) { if (CreditsAmount(credits) <= balance) {
if (const auto onstack = done) { if (const auto onstack = done) {
onstack(SmallBalanceResult::Already); onstack(SmallBalanceResult::Already);
} }

View file

@ -72,14 +72,14 @@ void FillCreditOptions(
std::shared_ptr<Main::SessionShow> show, std::shared_ptr<Main::SessionShow> show,
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer, not_null<PeerData*> peer,
StarsAmount minCredits, CreditsAmount minCredits,
Fn<void()> paid, Fn<void()> paid,
rpl::producer<QString> subtitle, rpl::producer<QString> subtitle,
std::vector<Data::CreditTopupOption> preloadedTopupOptions); std::vector<Data::CreditTopupOption> preloadedTopupOptions);
[[nodiscard]] not_null<Ui::RpWidget*> AddBalanceWidget( [[nodiscard]] not_null<Ui::RpWidget*> AddBalanceWidget(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
rpl::producer<StarsAmount> balanceValue, rpl::producer<CreditsAmount> balanceValue,
bool rightAlign, bool rightAlign,
rpl::producer<float64> opacityValue = nullptr); rpl::producer<float64> opacityValue = nullptr);
@ -88,7 +88,7 @@ void AddWithdrawalWidget(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
rpl::producer<QString> secondButtonUrl, rpl::producer<QString> secondButtonUrl,
rpl::producer<StarsAmount> availableBalanceValue, rpl::producer<CreditsAmount> availableBalanceValue,
rpl::producer<QDateTime> dateValue, rpl::producer<QDateTime> dateValue,
bool withdrawalEnabled, bool withdrawalEnabled,
rpl::producer<QString> usdValue); rpl::producer<QString> usdValue);

View file

@ -738,9 +738,9 @@ void SetupPremium(
container, container,
tr::lng_settings_credits(), tr::lng_settings_credits(),
controller->session().credits().balanceValue( controller->session().credits().balanceValue(
) | rpl::map([=](StarsAmount c) { ) | rpl::map([=](CreditsAmount c) {
return c return c
? Lang::FormatStarsAmountToShort(c).string ? Lang::FormatCreditsAmountToShort(c).string
: QString(); : QString();
}), }),
st::settingsButton), st::settingsButton),

View file

@ -134,7 +134,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/palette.h" #include "styles/palette.h"
#include "styles/style_basic.h" #include "styles/style_basic.h"
#include "core/stars_amount.h" #include "core/credits_amount.h"
#include "core/utils.h" #include "core/utils.h"
#include "logs.h" #include "logs.h"
#include "config.h" #include "config.h"

View file

@ -136,6 +136,33 @@ auto EmptyMessageDraftSources()
return cWorkingDir() + u"tdata/tdld/"_q; return cWorkingDir() + u"tdata/tdld/"_q;
} }
[[nodiscard]] std::pair<quint64, quint64> SerializeSuggest(
SuggestPostOptions options) {
return {
((quint64(options.exists) << 63)
| (quint64(quint32(options.date)))),
((quint64(options.ton) << 63)
| (quint64(options.priceWhole) << 32)
| (quint64(options.priceNano))),
};
}
[[nodiscard]] SuggestPostOptions DeserializeSuggest(
std::pair<quint64, quint64> suggest) {
const auto exists = (suggest.first >> 63) ? 1 : 0;
const auto date = TimeId(uint32(suggest.first & 0xFFFF'FFFFULL));
const auto ton = (suggest.second >> 63) ? 1 : 0;
const auto priceWhole = uint32((suggest.second >> 32) & 0x7FFF'FFFFULL);
const auto priceNano = uint32(suggest.second & 0xFFFF'FFFFULL);
return {
.exists = uint32(exists),
.priceWhole = priceWhole,
.priceNano = priceNano,
.ton = uint32(ton),
.date = date,
};
}
} // namespace } // namespace
Account::Account(not_null<Main::Account*> owner, const QString &dataName) Account::Account(not_null<Main::Account*> owner, const QString &dataName)
@ -1276,7 +1303,7 @@ void Account::writeDrafts(not_null<History*> history) {
+ Serialize::stringSize(text.text) + Serialize::stringSize(text.text)
+ TextUtilities::SerializeTagsSize(text.tags) + TextUtilities::SerializeTagsSize(text.tags)
+ sizeof(qint64) + sizeof(qint64) // messageId + sizeof(qint64) + sizeof(qint64) // messageId
+ sizeof(quint64) // suggest + (sizeof(quint64) * 2) // suggest
+ Serialize::stringSize(webpage.url) + Serialize::stringSize(webpage.url)
+ sizeof(qint32) // webpage.forceLargeMedia + sizeof(qint32) // webpage.forceLargeMedia
+ sizeof(qint32) // webpage.forceSmallMedia + sizeof(qint32) // webpage.forceSmallMedia
@ -1303,15 +1330,15 @@ void Account::writeDrafts(not_null<History*> history) {
const TextWithTags &text, const TextWithTags &text,
const Data::WebPageDraft &webpage, const Data::WebPageDraft &webpage,
auto&&) { // cursor auto&&) { // cursor
const auto serialized = SerializeSuggest(suggest);
data.stream data.stream
<< key.serialize() << key.serialize()
<< text.text << text.text
<< TextUtilities::SerializeTags(text.tags) << TextUtilities::SerializeTags(text.tags)
<< qint64(reply.messageId.peer.value) << qint64(reply.messageId.peer.value)
<< qint64(reply.messageId.msg.bare) << qint64(reply.messageId.msg.bare)
<< quint64(quint64(quint32(suggest.date)) << serialized.first
| (quint64(suggest.stars) << 32) << serialized.second
| (quint64(suggest.exists) << 63))
<< webpage.url << webpage.url
<< qint32(webpage.forceLargeMedia ? 1 : 0) << qint32(webpage.forceLargeMedia ? 1 : 0)
<< qint32(webpage.forceSmallMedia ? 1 : 0) << qint32(webpage.forceSmallMedia ? 1 : 0)
@ -1536,7 +1563,7 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
QByteArray textTagsSerialized; QByteArray textTagsSerialized;
qint64 keyValue = 0; qint64 keyValue = 0;
qint64 messageIdPeer = 0, messageIdMsg = 0; qint64 messageIdPeer = 0, messageIdMsg = 0;
quint64 suggestSerialized = 0; std::pair<quint64, quint64> suggestSerialized;
qint32 keyValueOld = 0; qint32 keyValueOld = 0;
QString webpageUrl; QString webpageUrl;
qint32 webpageForceLargeMedia = 0; qint32 webpageForceLargeMedia = 0;
@ -1572,7 +1599,9 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
>> messageIdPeer >> messageIdPeer
>> messageIdMsg; >> messageIdMsg;
if (withSuggest) { if (withSuggest) {
draft.stream >> suggestSerialized; draft.stream
>> suggestSerialized.first
>> suggestSerialized.second;
} }
draft.stream draft.stream
>> webpageUrl >> webpageUrl
@ -1597,13 +1626,7 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
MsgId(messageIdMsg)), MsgId(messageIdMsg)),
.topicRootId = key.topicRootId(), .topicRootId = key.topicRootId(),
}, },
SuggestPostOptions{ DeserializeSuggest(suggestSerialized),
.exists = uint32(suggestSerialized >> 63),
.stars = uint32(
(suggestSerialized & ~(1ULL << 63)) >> 32),
.date = TimeId(
uint32(suggestSerialized & 0xFFFF'FFFFULL)),
},
MessageCursor(), MessageCursor(),
Data::WebPageDraft{ Data::WebPageDraft{
.url = webpageUrl, .url = webpageUrl,

View file

@ -164,11 +164,11 @@ not_null<RpWidget*> CreateSingleStarWidget(
not_null<MaskedInputField*> AddInputFieldForCredits( not_null<MaskedInputField*> AddInputFieldForCredits(
not_null<VerticalLayout*> container, not_null<VerticalLayout*> container,
rpl::producer<StarsAmount> value) { rpl::producer<CreditsAmount> value) {
const auto &st = st::botEarnInputField; const auto &st = st::botEarnInputField;
const auto inputContainer = container->add( const auto inputContainer = container->add(
CreateSkipWidget(container, st.heightMin)); CreateSkipWidget(container, st.heightMin));
const auto currentValue = rpl::variable<StarsAmount>( const auto currentValue = rpl::variable<CreditsAmount>(
rpl::duplicate(value)); rpl::duplicate(value));
const auto input = CreateChild<NumberInput>( const auto input = CreateChild<NumberInput>(
inputContainer, inputContainer,
@ -178,7 +178,7 @@ not_null<MaskedInputField*> AddInputFieldForCredits(
currentValue.current().whole()); currentValue.current().whole());
rpl::duplicate( rpl::duplicate(
value value
) | rpl::start_with_next([=](StarsAmount v) { ) | rpl::start_with_next([=](CreditsAmount v) {
input->changeLimit(v.whole()); input->changeLimit(v.whole());
input->setText(QString::number(v.whole())); input->setText(QString::number(v.whole()));
}, input->lifetime()); }, input->lifetime());

View file

@ -44,7 +44,7 @@ using PaintRoundImageCallback = Fn<void(
[[nodiscard]] not_null<Ui::MaskedInputField*> AddInputFieldForCredits( [[nodiscard]] not_null<Ui::MaskedInputField*> AddInputFieldForCredits(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
rpl::producer<StarsAmount> value); rpl::producer<CreditsAmount> value);
PaintRoundImageCallback GenerateCreditsPaintUserpicCallback( PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
const Data::CreditsHistoryEntry &entry); const Data::CreditsHistoryEntry &entry);

View file

@ -34,7 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/flat_map.h" #include "base/flat_map.h"
#include "base/flat_set.h" #include "base/flat_set.h"
#include "core/stars_amount.h" #include "core/credits_amount.h"
#include "ui/arc_angles.h" #include "ui/arc_angles.h"
#include "ui/text/text.h" #include "ui/text/text.h"