mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-08-22 02:07:24 +00:00
Allow settings gift price in TON.
This commit is contained in:
parent
985324ac12
commit
aa499eab61
@ -3559,6 +3559,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_send_button_self" = "Buy a Gift for {cost}";
|
||||
"lng_gift_buy_resale_title" = "Buy {name}";
|
||||
"lng_gift_buy_resale_button" = "Buy for {cost}";
|
||||
"lng_gift_buy_resale_equals" = "Equals to {cost}";
|
||||
"lng_gift_buy_resale_only_ton" = "The seller only accepts TON as payment";
|
||||
"lng_gift_buy_resale_pay_stars" = "Pay in Stars";
|
||||
"lng_gift_buy_resale_pay_ton" = "Pay in TON";
|
||||
"lng_gift_buy_resale_confirm" = "Do you want to buy {name} for {price} and gift it to {user}?";
|
||||
"lng_gift_buy_resale_confirm_self" = "Do you want to buy {name} for {price}?";
|
||||
"lng_gift_buy_price_change_title" = "Price change!";
|
||||
@ -3704,12 +3708,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_sell_unlist_title" = "Unlist {name}";
|
||||
"lng_gift_sell_unlist_sure" = "Are you sure you want to unlist your gift?";
|
||||
"lng_gift_sell_title" = "Price in Stars";
|
||||
"lng_gift_sell_placeholder" = "Enter price in Stars";
|
||||
"lng_gift_sell_about" = "You will receive {percent} of the selected amount.";
|
||||
"lng_gift_sell_amount#one" = "You will receive **{count}** Star.";
|
||||
"lng_gift_sell_amount#other" = "You will receive **{count}** Stars.";
|
||||
"lng_gift_sell_min_price#one" = "Minimum price is {count} Star.";
|
||||
"lng_gift_sell_min_price#other" = "Minimum price is {count} Stars.";
|
||||
"lng_gift_sell_only_ton" = "Only Accept TON";
|
||||
"lng_gift_sell_only_ton_about" = "If the buyer pays you in TON, there's no risk of refunds, unlike Stars payments.";
|
||||
"lng_gift_sell_amount_ton#one" = "You will receive **{count}** TON.";
|
||||
"lng_gift_sell_amount_ton#other" = "You will receive **{count}** TON.";
|
||||
"lng_gift_sell_min_price_ton#one" = "Minimum price is {count} TON.";
|
||||
"lng_gift_sell_min_price_ton#other" = "Minimum price is {count} TON.";
|
||||
"lng_gift_sell_title_ton" = "Price in TON";
|
||||
"lng_gift_sell_put" = "Put for Sale";
|
||||
"lng_gift_sell_update" = "Update the Price";
|
||||
"lng_gift_sell_toast" = "{name} is now for sale!";
|
||||
|
@ -895,9 +895,10 @@ std::optional<Data::StarGift> FromTL(
|
||||
? peerFromMTP(*data.vowner_id())
|
||||
: PeerId()),
|
||||
.releasedBy = releasedBy,
|
||||
.number = data.vnum().v,
|
||||
.nanoTonForResale = FindTonForResale(data.vresell_amount()),
|
||||
.starsForResale = FindStarsForResale(data.vresell_amount()),
|
||||
.tonForResale = FindTonForResale(data.vresell_amount()),
|
||||
.number = data.vnum().v,
|
||||
.onlyAcceptTon = data.is_resale_ton_only(),
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
}),
|
||||
|
@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/view/controls/history_view_suggest_options.h"
|
||||
#include "history/view/media/history_view_media_generic.h"
|
||||
#include "history/view/media/history_view_unique_gift.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
@ -71,6 +72,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/controls/emoji_button.h"
|
||||
#include "ui/controls/ton_common.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
@ -233,6 +235,37 @@ struct SessionResalePrices {
|
||||
crl::time lastReceived = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] CreditsAmount StarsFromTon(
|
||||
not_null<Main::Session*> session,
|
||||
CreditsAmount ton) {
|
||||
const auto appConfig = &session->appConfig();
|
||||
const auto starsRate = appConfig->starsWithdrawRate() / 100.;
|
||||
const auto tonRate = appConfig->currencyWithdrawRate();
|
||||
if (!starsRate) {
|
||||
return {};
|
||||
}
|
||||
const auto count = (ton.value() * tonRate) / starsRate;
|
||||
return CreditsAmount(int(base::SafeRound(count)));
|
||||
}
|
||||
|
||||
[[nodiscard]] CreditsAmount TonFromStars(
|
||||
not_null<Main::Session*> session,
|
||||
CreditsAmount stars) {
|
||||
const auto appConfig = &session->appConfig();
|
||||
const auto starsRate = appConfig->starsWithdrawRate() / 100.;
|
||||
const auto tonRate = appConfig->currencyWithdrawRate();
|
||||
if (!tonRate) {
|
||||
return {};
|
||||
}
|
||||
const auto count = (stars.value() * starsRate) / tonRate;
|
||||
const auto whole = int(std::floor(count));
|
||||
const auto cents = int(base::SafeRound((count - whole) * 100));
|
||||
return CreditsAmount(
|
||||
whole,
|
||||
cents * (Ui::kNanosInOne / 100),
|
||||
CreditsType::Ton);
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<SessionResalePrices*> ResalePrices(
|
||||
not_null<Main::Session*> session) {
|
||||
static auto result = base::flat_map<
|
||||
@ -4403,19 +4436,6 @@ void ShowUniqueGiftWearBox(
|
||||
session,
|
||||
st::creditsBoxButtonLabel,
|
||||
&st::giftBox.button.textFg);
|
||||
|
||||
rpl::combine(
|
||||
box->widthValue(),
|
||||
button->widthValue()
|
||||
) | rpl::start_with_next([=](int outer, int inner) {
|
||||
const auto padding = st::giftBox.buttonPadding;
|
||||
const auto wanted = outer - padding.left() - padding.right();
|
||||
if (inner != wanted) {
|
||||
button->resizeToWidth(wanted);
|
||||
button->moveToLeft(padding.left(), padding.top());
|
||||
}
|
||||
}, box->lifetime());
|
||||
|
||||
AddUniqueCloseButton(box, {});
|
||||
}));
|
||||
}
|
||||
@ -4473,12 +4493,14 @@ void UpdateGiftSellPrice(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> unique,
|
||||
Data::SavedStarGiftId savedId,
|
||||
int price) {
|
||||
CreditsAmount price) {
|
||||
const auto was = unique->starsForResale;
|
||||
const auto session = &show->session();
|
||||
session->api().request(MTPpayments_UpdateStarGiftPrice(
|
||||
Api::InputSavedStarGiftId(savedId, unique),
|
||||
MTP_starsAmount(MTP_long(price), MTP_int(0))
|
||||
(price
|
||||
? StarsAmountToTL(price)
|
||||
: MTP_starsAmount(MTP_long(0), MTP_int(0)))
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
session->api().applyUpdates(result);
|
||||
show->showToast((!price
|
||||
@ -4489,8 +4511,26 @@ void UpdateGiftSellPrice(
|
||||
tr::now,
|
||||
lt_name,
|
||||
Data::UniqueGiftName(*unique)));
|
||||
|
||||
unique->starsForResale = price;
|
||||
const auto setStars = [&](CreditsAmount amount) {
|
||||
unique->starsForResale = amount.whole();
|
||||
};
|
||||
const auto setTon = [&](CreditsAmount amount) {
|
||||
unique->nanoTonForResale = amount.whole() * Ui::kNanosInOne
|
||||
+ amount.nano();
|
||||
};
|
||||
if (!price) {
|
||||
setStars({});
|
||||
setTon({});
|
||||
unique->onlyAcceptTon = false;
|
||||
} else if (price.ton()) {
|
||||
setStars(StarsFromTon(session, price));
|
||||
setTon(price);
|
||||
unique->onlyAcceptTon = true;
|
||||
} else {
|
||||
setStars(price);
|
||||
setTon(TonFromStars(session, price));
|
||||
unique->onlyAcceptTon = false;
|
||||
}
|
||||
session->data().notifyGiftUpdate({
|
||||
.id = savedId,
|
||||
.slug = unique->slug,
|
||||
@ -4517,80 +4557,103 @@ void UniqueGiftSellBox(
|
||||
Data::SavedStarGiftId savedId,
|
||||
int price,
|
||||
Settings::GiftWearBoxStyleOverride st) {
|
||||
box->setTitle(tr::lng_gift_sell_title());
|
||||
const auto session = &show->session();
|
||||
const auto &appConfig = session->appConfig();
|
||||
const auto starsMin = appConfig.giftResaleStarsMin();
|
||||
const auto nanoTonMin = appConfig.giftResaleNanoTonMin();
|
||||
const auto starsThousandths = appConfig.giftResaleStarsThousandths();
|
||||
const auto nanoTonThousandths = appConfig.giftResaleNanoTonThousandths();
|
||||
|
||||
struct State {
|
||||
rpl::variable<bool> onlyTon;
|
||||
rpl::variable<CreditsAmount> price;
|
||||
Fn<std::optional<CreditsAmount>()> computePrice;
|
||||
rpl::event_stream<> errors;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
state->onlyTon = unique->onlyAcceptTon;
|
||||
const auto priceNow = unique->onlyAcceptTon
|
||||
? CreditsAmount(
|
||||
unique->nanoTonForResale / Ui::kNanosInOne,
|
||||
unique->nanoTonForResale % Ui::kNanosInOne,
|
||||
CreditsType::Ton)
|
||||
: CreditsAmount(unique->starsForResale);
|
||||
state->price = priceNow
|
||||
? priceNow
|
||||
: price
|
||||
? CreditsAmount(price)
|
||||
: CreditsAmount(starsMin);
|
||||
|
||||
box->setTitle(rpl::conditional(
|
||||
state->onlyTon.value(),
|
||||
tr::lng_gift_sell_title_ton(),
|
||||
tr::lng_gift_sell_title()));
|
||||
box->setStyle(st.box ? *st.box : st::upgradeGiftBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
|
||||
box->addTopButton(st.close ? *st.close : st::boxTitleClose, [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
const auto priceNow = unique->starsForResale;
|
||||
const auto name = Data::UniqueGiftName(*unique);
|
||||
const auto slug = unique->slug;
|
||||
|
||||
const auto session = &show->session();
|
||||
AddSubsectionTitle(
|
||||
box->verticalLayout(),
|
||||
tr::lng_gift_sell_placeholder(),
|
||||
(st::boxRowPadding - QMargins(
|
||||
st::defaultSubsectionTitlePadding.left(),
|
||||
0,
|
||||
st::defaultSubsectionTitlePadding.right(),
|
||||
st::defaultSubsectionTitlePadding.bottom())));
|
||||
const auto &appConfig = session->appConfig();
|
||||
const auto limit = appConfig.giftResalePriceMax();
|
||||
const auto minimal = appConfig.giftResalePriceMin();
|
||||
const auto thousandths = appConfig.giftResaleReceiveThousandths();
|
||||
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||
box,
|
||||
st::editTagField.heightMin));
|
||||
auto owned = object_ptr<Ui::NumberInput>(
|
||||
wrap,
|
||||
st::editTagField,
|
||||
rpl::single(QString()),
|
||||
QString::number(priceNow ? priceNow : price ? price : minimal),
|
||||
limit);
|
||||
const auto field = owned.data();
|
||||
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
field->move(0, 0);
|
||||
field->resize(width, field->height());
|
||||
wrap->resize(width, field->height());
|
||||
}, wrap->lifetime());
|
||||
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(field);
|
||||
st::paidStarIcon.paint(p, 0, st::paidStarIconTop, field->width());
|
||||
}, field->lifetime());
|
||||
field->selectAll();
|
||||
box->setFocusCallback([=] {
|
||||
field->setFocusFast();
|
||||
const auto container = box->verticalLayout();
|
||||
auto priceInput = HistoryView::AddStarsTonPriceInput(container, {
|
||||
.session = session,
|
||||
.showTon = state->onlyTon.value(),
|
||||
.price = state->price.current(),
|
||||
.starsMin = starsMin,
|
||||
.starsMax = appConfig.giftResaleStarsMax(),
|
||||
.nanoTonMin = nanoTonMin,
|
||||
.nanoTonMax = appConfig.giftResaleNanoTonMax(),
|
||||
});
|
||||
state->price = std::move(priceInput.result);
|
||||
state->computePrice = std::move(priceInput.computeResult);
|
||||
box->setFocusCallback(std::move(priceInput.focusCallback));
|
||||
|
||||
const auto errors = box->lifetime().make_state<
|
||||
rpl::event_stream<>
|
||||
>();
|
||||
auto goods = rpl::merge(
|
||||
rpl::single(rpl::empty) | rpl::map_to(true),
|
||||
base::qt_signal_producer(
|
||||
field,
|
||||
&Ui::NumberInput::changed
|
||||
) | rpl::map_to(true),
|
||||
errors->events() | rpl::map_to(false)
|
||||
std::move(priceInput.updates) | rpl::map_to(true),
|
||||
state->errors.events() | rpl::map_to(false)
|
||||
) | rpl::start_spawning(box->lifetime());
|
||||
auto text = rpl::duplicate(goods) | rpl::map([=](bool good) {
|
||||
const auto value = field->getLastText().toInt();
|
||||
const auto receive = (int64(value) * thousandths) / 1000;
|
||||
return !good
|
||||
? tr::lng_gift_sell_min_price(
|
||||
tr::now,
|
||||
lt_count,
|
||||
minimal,
|
||||
Ui::Text::RichLangValue)
|
||||
: (value >= minimal)
|
||||
? tr::lng_gift_sell_amount(
|
||||
tr::now,
|
||||
lt_count,
|
||||
receive,
|
||||
Ui::Text::RichLangValue)
|
||||
const auto value = state->computePrice();
|
||||
const auto amount = value ? value->value() : 0.;
|
||||
const auto tonMin = nanoTonMin / float64(Ui::kNanosInOne);
|
||||
const auto enough = value
|
||||
&& (amount >= (value->ton() ? tonMin : starsMin));
|
||||
const auto receive = !value
|
||||
? 0
|
||||
: value->ton()
|
||||
? ((amount * nanoTonThousandths) / 1000.)
|
||||
: ((int64(amount) * starsThousandths) / 1000);
|
||||
const auto thousandths = state->onlyTon.current()
|
||||
? nanoTonThousandths
|
||||
: starsThousandths;
|
||||
return (!good || !value)
|
||||
? (state->onlyTon.current()
|
||||
? tr::lng_gift_sell_min_price_ton(
|
||||
tr::now,
|
||||
lt_count,
|
||||
nanoTonMin / float64(Ui::kNanosInOne),
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_gift_sell_min_price(
|
||||
tr::now,
|
||||
lt_count,
|
||||
starsMin,
|
||||
Ui::Text::RichLangValue))
|
||||
: enough
|
||||
? (value->ton()
|
||||
? tr::lng_gift_sell_amount_ton(
|
||||
tr::now,
|
||||
lt_count,
|
||||
receive,
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_gift_sell_amount(
|
||||
tr::now,
|
||||
lt_count,
|
||||
receive,
|
||||
Ui::Text::RichLangValue))
|
||||
: tr::lng_gift_sell_about(
|
||||
tr::now,
|
||||
lt_percent,
|
||||
@ -4603,37 +4666,50 @@ void UniqueGiftSellBox(
|
||||
box->verticalLayout()->resizeToWidth(box->width());
|
||||
}),
|
||||
st::boxLabel));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
box->addRow(object_ptr<Ui::PlainShadow>(box));
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto onlyTon = box->addRow(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
box,
|
||||
tr::lng_gift_sell_only_ton(tr::now),
|
||||
state->onlyTon.current(),
|
||||
st::defaultCheckbox));
|
||||
state->onlyTon = onlyTon->checkedValue();
|
||||
|
||||
Ui::AddSkip(container);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_gift_sell_only_ton_about(Ui::Text::RichLangValue),
|
||||
st::boxDividerLabel));
|
||||
Ui::AddSkip(container);
|
||||
|
||||
rpl::duplicate(goods) | rpl::start_with_next([=](bool good) {
|
||||
details->setTextColorOverride(
|
||||
good ? st::windowSubTextFg->c : st::boxTextFgError->c);
|
||||
}, details->lifetime());
|
||||
|
||||
QObject::connect(field, &NumberInput::submitted, [=] {
|
||||
const auto count = field->getLastText().toInt();
|
||||
if (count < minimal) {
|
||||
field->showError();
|
||||
errors->fire({});
|
||||
const auto submit = [=] {
|
||||
const auto value = state->computePrice();
|
||||
if (!value) {
|
||||
state->errors.fire({});
|
||||
return;
|
||||
}
|
||||
box->closeBox();
|
||||
UpdateGiftSellPrice(show, unique, savedId, count);
|
||||
});
|
||||
const auto button = box->addButton(priceNow
|
||||
UpdateGiftSellPrice(show, unique, savedId, *value);
|
||||
};
|
||||
std::move(
|
||||
priceInput.submits
|
||||
) | rpl::start_with_next(submit, details->lifetime());
|
||||
auto submitText = priceNow
|
||||
? tr::lng_gift_sell_update()
|
||||
: tr::lng_gift_sell_put(), [=] { field->submitted({}); });
|
||||
rpl::combine(
|
||||
box->widthValue(),
|
||||
button->widthValue()
|
||||
) | rpl::start_with_next([=](int outer, int inner) {
|
||||
const auto padding = st::giftBox.buttonPadding;
|
||||
const auto wanted = outer - padding.left() - padding.right();
|
||||
if (inner != wanted) {
|
||||
button->resizeToWidth(wanted);
|
||||
button->moveToLeft(padding.left(), padding.top());
|
||||
}
|
||||
}, box->lifetime());
|
||||
: tr::lng_gift_sell_put();
|
||||
box->addButton(std::move(submitText), submit);
|
||||
}
|
||||
|
||||
void ShowUniqueGiftSellBox(
|
||||
@ -4874,17 +4950,6 @@ void UpgradeBox(
|
||||
st::creditsBoxButtonLabel,
|
||||
&st::giftBox.button.textFg);
|
||||
}
|
||||
rpl::combine(
|
||||
box->widthValue(),
|
||||
button->widthValue()
|
||||
) | rpl::start_with_next([=](int outer, int inner) {
|
||||
const auto padding = st::giftBox.buttonPadding;
|
||||
const auto wanted = outer - padding.left() - padding.right();
|
||||
if (inner != wanted) {
|
||||
button->resizeToWidth(wanted);
|
||||
button->moveToLeft(padding.left(), padding.top());
|
||||
}
|
||||
}, box->lifetime());
|
||||
|
||||
AddUniqueCloseButton(box, {});
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ void UpdateGiftSellPrice(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> unique,
|
||||
Data::SavedStarGiftId savedId,
|
||||
int price);
|
||||
CreditsAmount price);
|
||||
void ShowUniqueGiftSellBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> unique,
|
||||
|
@ -1533,6 +1533,18 @@ bool ResolveStarsSettings(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResolveTonSettings(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
controller->showSettings(::Settings::CurrencyId());
|
||||
controller->window().activate();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
|
||||
@ -1637,6 +1649,10 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
|
||||
u"^stars/?(^\\?.*)?(#|$)"_q,
|
||||
ResolveStarsSettings
|
||||
},
|
||||
{
|
||||
u"^ton/?(^\\?.*)?(#|$)"_q,
|
||||
ResolveTonSettings
|
||||
},
|
||||
{
|
||||
u"^([^\\?]+)(\\?|#|$)"_q,
|
||||
HandleUnknown
|
||||
|
@ -45,10 +45,11 @@ struct UniqueGift {
|
||||
QString ownerName;
|
||||
PeerId ownerId = 0;
|
||||
PeerData *releasedBy = nullptr;
|
||||
int number = 0;
|
||||
int starsForTransfer = -1;
|
||||
int64 nanoTonForResale = -1;
|
||||
int starsForResale = -1;
|
||||
int64 tonForResale = -1;
|
||||
int starsForTransfer = -1;
|
||||
int number = 0;
|
||||
bool onlyAcceptTon = false;
|
||||
TimeId exportAt = 0;
|
||||
TimeId canTransferAt = 0;
|
||||
TimeId canResellAt = 0;
|
||||
|
@ -48,6 +48,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] rpl::producer<CreditsAmount> StarsPriceValue(
|
||||
rpl::producer<CreditsAmount> full) {
|
||||
return rpl::single(
|
||||
CreditsAmount()
|
||||
) | rpl::then(std::move(
|
||||
full
|
||||
) | rpl::filter([=](CreditsAmount amount) {
|
||||
return amount.stars();
|
||||
}));
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<CreditsAmount> TonPriceValue(
|
||||
rpl::producer<CreditsAmount> full) {
|
||||
return rpl::single(
|
||||
CreditsAmount()
|
||||
) | rpl::then(std::move(
|
||||
full
|
||||
) | rpl::filter([=](CreditsAmount amount) {
|
||||
return amount.ton();
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ChooseSuggestTimeBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
@ -112,6 +137,214 @@ void AddApproximateUsd(
|
||||
usd->widthValue() | rpl::start_with_next(move, usd->lifetime());
|
||||
}
|
||||
|
||||
StarsTonPriceInput AddStarsTonPriceInput(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
StarsTonPriceArgs &&args) {
|
||||
struct State {
|
||||
rpl::variable<bool> ton;
|
||||
rpl::variable<CreditsAmount> price;
|
||||
rpl::event_stream<> updates;
|
||||
rpl::event_stream<> submits;
|
||||
};
|
||||
const auto state = container->lifetime().make_state<State>();
|
||||
state->ton = std::move(args.showTon);
|
||||
state->price = args.price;
|
||||
|
||||
const auto session = args.session;
|
||||
const auto added = st::boxRowPadding - st::defaultSubsectionTitlePadding;
|
||||
const auto manager = &session->data().customEmojiManager();
|
||||
const auto makeIcon = [&](
|
||||
not_null<QWidget*> parent,
|
||||
TextWithEntities text) {
|
||||
return Ui::CreateChild<Ui::FlatLabel>(
|
||||
parent,
|
||||
rpl::single(text),
|
||||
st::defaultFlatLabel,
|
||||
st::defaultPopupMenu,
|
||||
Core::TextContext({ .session = session }));
|
||||
};
|
||||
|
||||
const auto starsWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto starsInner = starsWrap->entity();
|
||||
|
||||
Ui::AddSubsectionTitle(
|
||||
starsInner,
|
||||
tr::lng_suggest_options_stars_price(),
|
||||
QMargins(
|
||||
added.left(),
|
||||
0,
|
||||
added.right(),
|
||||
-st::defaultSubsectionTitlePadding.bottom()));
|
||||
|
||||
const auto starsFieldWrap = starsInner->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
starsInner,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
auto ownedStarsField = object_ptr<Ui::NumberInput>(
|
||||
starsFieldWrap,
|
||||
st::editTagField,
|
||||
rpl::single(u"0"_q),
|
||||
((args.price && args.price.stars())
|
||||
? QString::number(args.price.whole())
|
||||
: QString()),
|
||||
args.starsMax);
|
||||
const auto starsField = ownedStarsField.data();
|
||||
const auto starsIcon = makeIcon(starsField, manager->creditsEmoji());
|
||||
|
||||
starsFieldWrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
starsIcon->move(st::starsFieldIconPosition);
|
||||
starsField->move(0, 0);
|
||||
starsField->resize(width, starsField->height());
|
||||
starsFieldWrap->resize(width, starsField->height());
|
||||
}, starsFieldWrap->lifetime());
|
||||
|
||||
AddApproximateUsd(
|
||||
starsField,
|
||||
session,
|
||||
StarsPriceValue(state->price.value()));
|
||||
|
||||
Ui::AddSkip(starsInner);
|
||||
Ui::AddSkip(starsInner);
|
||||
if (args.starsAbout) {
|
||||
Ui::AddDividerText(starsInner, std::move(args.starsAbout));
|
||||
}
|
||||
|
||||
const auto tonWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto tonInner = tonWrap->entity();
|
||||
|
||||
Ui::AddSubsectionTitle(
|
||||
tonInner,
|
||||
tr::lng_suggest_options_ton_price(),
|
||||
QMargins(
|
||||
added.left(),
|
||||
0,
|
||||
added.right(),
|
||||
-st::defaultSubsectionTitlePadding.bottom()));
|
||||
|
||||
const auto tonFieldWrap = tonInner->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
tonInner,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
auto ownedTonField = object_ptr<Ui::InputField>::fromRaw(
|
||||
Ui::CreateTonAmountInput(
|
||||
tonFieldWrap,
|
||||
rpl::single('0' + Ui::TonAmountSeparator() + '0'),
|
||||
((args.price && args.price.ton())
|
||||
? (args.price.whole() * Ui::kNanosInOne + args.price.nano())
|
||||
: 0)));
|
||||
const auto tonField = ownedTonField.data();
|
||||
const auto tonIcon = makeIcon(tonField, Ui::Text::SingleCustomEmoji(
|
||||
manager->registerInternalEmoji(
|
||||
Ui::Earn::IconCurrencyColored(
|
||||
st::tonFieldIconSize,
|
||||
st::currencyFg->c),
|
||||
st::channelEarnCurrencyCommonMargins,
|
||||
false)));
|
||||
|
||||
tonFieldWrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
tonIcon->move(st::tonFieldIconPosition);
|
||||
tonField->move(0, 0);
|
||||
tonField->resize(width, tonField->height());
|
||||
tonFieldWrap->resize(width, tonField->height());
|
||||
}, tonFieldWrap->lifetime());
|
||||
|
||||
AddApproximateUsd(
|
||||
tonField,
|
||||
session,
|
||||
TonPriceValue(state->price.value()));
|
||||
|
||||
Ui::AddSkip(tonInner);
|
||||
Ui::AddSkip(tonInner);
|
||||
if (args.tonAbout) {
|
||||
Ui::AddDividerText(tonInner, std::move(args.tonAbout));
|
||||
}
|
||||
|
||||
tonWrap->toggleOn(state->ton.value(), anim::type::instant);
|
||||
starsWrap->toggleOn(
|
||||
state->ton.value() | rpl::map(!rpl::mappers::_1),
|
||||
anim::type::instant);
|
||||
|
||||
auto computeResult = [=]() -> std::optional<CreditsAmount> {
|
||||
auto nanos = int64();
|
||||
const auto ton = uint32(state->ton.current() ? 1 : 0);
|
||||
if (ton) {
|
||||
const auto text = tonField->getLastText();
|
||||
const auto now = Ui::ParseTonAmountString(text);
|
||||
if (now
|
||||
&& *now
|
||||
&& ((*now < args.nanoTonMin) || (*now > args.nanoTonMax))) {
|
||||
tonField->showError();
|
||||
return {};
|
||||
}
|
||||
nanos = now.value_or(0);
|
||||
} else {
|
||||
const auto now = starsField->getLastText().toLongLong();
|
||||
if (now && (now < args.starsMin || now > args.starsMax)) {
|
||||
starsField->showError();
|
||||
return {};
|
||||
}
|
||||
nanos = now * Ui::kNanosInOne;
|
||||
}
|
||||
return CreditsAmount(
|
||||
nanos / Ui::kNanosInOne,
|
||||
nanos % Ui::kNanosInOne,
|
||||
ton ? CreditsType::Ton : CreditsType::Stars);
|
||||
};
|
||||
|
||||
const auto updatePrice = [=] {
|
||||
if (auto result = computeResult()) {
|
||||
state->price = *result;
|
||||
}
|
||||
state->updates.fire({});
|
||||
};
|
||||
QObject::connect(
|
||||
starsField,
|
||||
&Ui::NumberInput::changed,
|
||||
starsField,
|
||||
updatePrice);
|
||||
tonField->changes(
|
||||
) | rpl::start_with_next(updatePrice, tonField->lifetime());
|
||||
|
||||
state->ton.changes(
|
||||
) | rpl::start_with_next(updatePrice, container->lifetime());
|
||||
|
||||
QObject::connect(
|
||||
starsField,
|
||||
&Ui::NumberInput::submitted,
|
||||
container,
|
||||
[=] { state->submits.fire({}); });
|
||||
tonField->submits(
|
||||
) | rpl::to_empty | rpl::start_to_stream(
|
||||
state->submits,
|
||||
tonField->lifetime());
|
||||
|
||||
auto focusCallback = [=] {
|
||||
if (state->ton.current()) {
|
||||
tonField->selectAll();
|
||||
tonField->setFocusFast();
|
||||
} else {
|
||||
starsField->selectAll();
|
||||
starsField->setFocusFast();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
.focusCallback = std::move(focusCallback),
|
||||
.computeResult = std::move(computeResult),
|
||||
.submits = state->submits.events(),
|
||||
.updates = state->updates.events(),
|
||||
.result = state->price.value(),
|
||||
};
|
||||
}
|
||||
|
||||
void ChooseSuggestPriceBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
SuggestPriceBoxArgs &&args) {
|
||||
@ -135,42 +368,17 @@ void ChooseSuggestPriceBox(
|
||||
state->date = args.value.date;
|
||||
state->ton = (args.value.ton != 0);
|
||||
state->price = args.value.price();
|
||||
const auto updatePrice = [=] {
|
||||
if (const auto price = state->computePrice()) {
|
||||
state->price = *price;
|
||||
}
|
||||
};
|
||||
|
||||
const auto starsPrice = [=] {
|
||||
return rpl::single(
|
||||
CreditsAmount()
|
||||
) | rpl::then(state->price.value(
|
||||
) | rpl::filter([=](CreditsAmount amount) {
|
||||
return amount.stars();
|
||||
}));
|
||||
};
|
||||
const auto tonPrice = [=] {
|
||||
return rpl::single(
|
||||
CreditsAmount(0, 0, CreditsType::Ton)
|
||||
) | rpl::then(state->price.value(
|
||||
) | rpl::filter([=](CreditsAmount amount) {
|
||||
return amount.ton();
|
||||
}));
|
||||
};
|
||||
|
||||
const auto peer = args.peer;
|
||||
const auto admin = peer->amMonoforumAdmin();
|
||||
const auto broadcast = peer->monoforumBroadcast();
|
||||
const auto usePeer = broadcast ? broadcast : peer;
|
||||
const auto session = &peer->session();
|
||||
const auto &appConfig = session->appConfig();
|
||||
if (!admin) {
|
||||
session->credits().load();
|
||||
session->credits().tonLoad();
|
||||
}
|
||||
const auto starsMin = session->appConfig().suggestedPostStarsMin();
|
||||
const auto starsMax = session->appConfig().suggestedPostStarsMax();
|
||||
const auto nanoTonMin = session->appConfig().suggestedPostNanoTonMin();
|
||||
const auto nanoTonMax = session->appConfig().suggestedPostNanoTonMax();
|
||||
const auto container = box->verticalLayout();
|
||||
|
||||
box->setStyle(st::suggestPriceBox);
|
||||
@ -267,7 +475,6 @@ void ChooseSuggestPriceBox(
|
||||
state->buttons[i].active = true;
|
||||
state->buttons[1 - i].active = false;
|
||||
buttons->update();
|
||||
updatePrice();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -299,64 +506,11 @@ void ChooseSuggestPriceBox(
|
||||
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto added = st::boxRowPadding - st::defaultSubsectionTitlePadding;
|
||||
const auto manager = &session->data().customEmojiManager();
|
||||
const auto makeIcon = [&](
|
||||
not_null<QWidget*> parent,
|
||||
TextWithEntities text) {
|
||||
return Ui::CreateChild<Ui::FlatLabel>(
|
||||
parent,
|
||||
rpl::single(text),
|
||||
st::defaultFlatLabel,
|
||||
st::defaultPopupMenu,
|
||||
Core::TextContext({ .session = session }));
|
||||
const auto computePrice = [session](CreditsAmount amount) {
|
||||
return PriceAfterCommission(session, amount).value();
|
||||
};
|
||||
|
||||
const auto starsWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto starsInner = starsWrap->entity();
|
||||
|
||||
Ui::AddSubsectionTitle(
|
||||
starsInner,
|
||||
tr::lng_suggest_options_stars_price(),
|
||||
QMargins(added.left(), 0, added.right(), -st::defaultSubsectionTitlePadding.bottom()));
|
||||
|
||||
const auto starsFieldWrap = starsInner->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
box,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
auto ownedStarsField = object_ptr<Ui::NumberInput>(
|
||||
starsFieldWrap,
|
||||
st::editTagField,
|
||||
rpl::single(u"0"_q),
|
||||
((args.value.exists && args.value.priceWhole && !args.value.ton)
|
||||
? QString::number(args.value.priceWhole)
|
||||
: QString()),
|
||||
starsMax);
|
||||
const auto starsField = ownedStarsField.data();
|
||||
const auto starsIcon = makeIcon(starsField, manager->creditsEmoji());
|
||||
|
||||
QObject::connect(starsField, &Ui::NumberInput::changed, updatePrice);
|
||||
|
||||
starsFieldWrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
starsIcon->move(st::starsFieldIconPosition);
|
||||
starsField->move(0, 0);
|
||||
starsField->resize(width, starsField->height());
|
||||
starsFieldWrap->resize(width, starsField->height());
|
||||
}, starsFieldWrap->lifetime());
|
||||
|
||||
AddApproximateUsd(starsField, session, starsPrice());
|
||||
|
||||
Ui::AddSkip(starsInner);
|
||||
Ui::AddSkip(starsInner);
|
||||
const auto computePrice = [peer = args.peer](CreditsAmount amount) {
|
||||
return PriceAfterCommission(&peer->session(), amount).value();
|
||||
};
|
||||
const auto formatCommission = [peer = args.peer](CreditsAmount amount) {
|
||||
return FormatAfterCommissionPercent(&peer->session(), amount);
|
||||
const auto formatCommission = [session](CreditsAmount amount) {
|
||||
return FormatAfterCommissionPercent(session, amount);
|
||||
};
|
||||
const auto youGet = [=](rpl::producer<CreditsAmount> price, bool stars) {
|
||||
return (stars
|
||||
@ -367,109 +521,34 @@ void ChooseSuggestPriceBox(
|
||||
lt_percent,
|
||||
rpl::duplicate(price) | rpl::map(formatCommission));
|
||||
};
|
||||
Ui::AddDividerText(starsInner, admin
|
||||
auto starsAbout = admin
|
||||
? rpl::combine(
|
||||
youGet(starsPrice(), true),
|
||||
youGet(StarsPriceValue(state->price.value()), true),
|
||||
tr::lng_suggest_options_stars_warning(Ui::Text::RichLangValue)
|
||||
) | rpl::map([=](const QString &t1, const TextWithEntities &t2) {
|
||||
return TextWithEntities{ t1 }.append("\n\n").append(t2);
|
||||
})
|
||||
: tr::lng_suggest_options_stars_price_about(Ui::Text::WithEntities));
|
||||
|
||||
const auto tonWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto tonInner = tonWrap->entity();
|
||||
|
||||
Ui::AddSubsectionTitle(
|
||||
tonInner,
|
||||
tr::lng_suggest_options_ton_price(),
|
||||
QMargins(added.left(), 0, added.right(), -st::defaultSubsectionTitlePadding.bottom()));
|
||||
|
||||
const auto tonFieldWrap = tonInner->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
box,
|
||||
st::editTagField.heightMin),
|
||||
st::boxRowPadding);
|
||||
auto ownedTonField = object_ptr<Ui::InputField>::fromRaw(
|
||||
Ui::CreateTonAmountInput(
|
||||
tonFieldWrap,
|
||||
rpl::single('0' + Ui::TonAmountSeparator() + '0'),
|
||||
((args.value.price() && args.value.ton)
|
||||
? (int64(args.value.priceWhole) * Ui::kNanosInOne
|
||||
+ int64(args.value.priceNano))
|
||||
: 0)));
|
||||
const auto tonField = ownedTonField.data();
|
||||
const auto tonIcon = makeIcon(tonField, Ui::Text::SingleCustomEmoji(
|
||||
manager->registerInternalEmoji(
|
||||
Ui::Earn::IconCurrencyColored(
|
||||
st::tonFieldIconSize,
|
||||
st::currencyFg->c),
|
||||
st::channelEarnCurrencyCommonMargins,
|
||||
false)));
|
||||
|
||||
tonField->changes(
|
||||
) | rpl::start_with_next(updatePrice, tonField->lifetime());
|
||||
|
||||
tonFieldWrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
tonIcon->move(st::tonFieldIconPosition);
|
||||
tonField->move(0, 0);
|
||||
tonField->resize(width, tonField->height());
|
||||
tonFieldWrap->resize(width, tonField->height());
|
||||
}, tonFieldWrap->lifetime());
|
||||
|
||||
AddApproximateUsd(tonField, session, tonPrice());
|
||||
|
||||
Ui::AddSkip(tonInner);
|
||||
Ui::AddSkip(tonInner);
|
||||
Ui::AddDividerText(
|
||||
tonInner,
|
||||
(admin
|
||||
? youGet(tonPrice(), false)
|
||||
: tr::lng_suggest_options_ton_price_about()));
|
||||
|
||||
tonWrap->toggleOn(state->ton.value(), anim::type::instant);
|
||||
starsWrap->toggleOn(
|
||||
state->ton.value() | rpl::map(!rpl::mappers::_1),
|
||||
anim::type::instant);
|
||||
|
||||
state->computePrice = [=]() -> std::optional<CreditsAmount> {
|
||||
auto nanos = int64();
|
||||
const auto ton = uint32(state->ton.current() ? 1 : 0);
|
||||
if (ton) {
|
||||
const auto text = tonField->getLastText();
|
||||
const auto now = Ui::ParseTonAmountString(text);
|
||||
if (now
|
||||
&& *now
|
||||
&& ((*now < nanoTonMin) || (*now > nanoTonMax))) {
|
||||
tonField->showError();
|
||||
return {};
|
||||
}
|
||||
nanos = now.value_or(0);
|
||||
} else {
|
||||
const auto now = starsField->getLastText().toLongLong();
|
||||
if (now && (now < starsMin || now > starsMax)) {
|
||||
starsField->showError();
|
||||
return {};
|
||||
}
|
||||
nanos = now * Ui::kNanosInOne;
|
||||
}
|
||||
return CreditsAmount(
|
||||
nanos / Ui::kNanosInOne,
|
||||
nanos % Ui::kNanosInOne,
|
||||
ton ? CreditsType::Ton : CreditsType::Stars);
|
||||
};
|
||||
|
||||
box->setFocusCallback([=] {
|
||||
if (state->ton.current()) {
|
||||
tonField->selectAll();
|
||||
tonField->setFocusFast();
|
||||
} else {
|
||||
starsField->selectAll();
|
||||
starsField->setFocusFast();
|
||||
}
|
||||
: tr::lng_suggest_options_stars_price_about(Ui::Text::WithEntities);
|
||||
auto tonAbout = admin
|
||||
? youGet(
|
||||
TonPriceValue(state->price.value()),
|
||||
false
|
||||
) | Ui::Text::ToWithEntities()
|
||||
: tr::lng_suggest_options_ton_price_about(Ui::Text::WithEntities);
|
||||
auto priceInput = AddStarsTonPriceInput(container, {
|
||||
.session = session,
|
||||
.showTon = state->ton.value(),
|
||||
.price = args.value.price(),
|
||||
.starsMin = appConfig.suggestedPostStarsMin(),
|
||||
.starsMax = appConfig.suggestedPostStarsMax(),
|
||||
.nanoTonMin = appConfig.suggestedPostNanoTonMin(),
|
||||
.nanoTonMax = appConfig.suggestedPostNanoTonMax(),
|
||||
.starsAbout = std::move(starsAbout),
|
||||
.tonAbout = std::move(tonAbout),
|
||||
});
|
||||
state->price = std::move(priceInput.result);
|
||||
state->computePrice = std::move(priceInput.computeResult);
|
||||
box->setFocusCallback(std::move(priceInput.focusCallback));
|
||||
|
||||
Ui::AddSkip(container);
|
||||
|
||||
@ -568,17 +647,13 @@ void ChooseSuggestPriceBox(
|
||||
}
|
||||
}, box->lifetime());
|
||||
|
||||
QObject::connect(
|
||||
starsField,
|
||||
&Ui::NumberInput::submitted,
|
||||
box,
|
||||
state->save);
|
||||
tonField->submits(
|
||||
) | rpl::start_with_next(state->save, tonField->lifetime());
|
||||
std::move(
|
||||
priceInput.submits
|
||||
) | rpl::start_with_next(state->save, box->lifetime());
|
||||
|
||||
const auto button = box->addButton(rpl::single(QString()), state->save);
|
||||
const auto coloredTonIcon = Ui::Text::SingleCustomEmoji(
|
||||
manager->registerInternalEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
Ui::Earn::IconCurrencyColored(
|
||||
st::tonFieldIconSize,
|
||||
st::currencyFg->c),
|
||||
|
@ -15,6 +15,7 @@ class Show;
|
||||
|
||||
namespace Ui {
|
||||
class GenericBox;
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
@ -43,6 +44,30 @@ void ChooseSuggestTimeBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
SuggestTimeBoxArgs &&args);
|
||||
|
||||
struct StarsTonPriceInput {
|
||||
Fn<void()> focusCallback;
|
||||
Fn<std::optional<CreditsAmount>()> computeResult;
|
||||
rpl::producer<> submits;
|
||||
rpl::producer<> updates;
|
||||
rpl::producer<CreditsAmount> result;
|
||||
};
|
||||
|
||||
struct StarsTonPriceArgs {
|
||||
not_null<Main::Session*> session;
|
||||
rpl::producer<bool> showTon;
|
||||
CreditsAmount price;
|
||||
int starsMin = 0;
|
||||
int starsMax = 0;
|
||||
int64 nanoTonMin = 0;
|
||||
int64 nanoTonMax = 0;
|
||||
rpl::producer<TextWithEntities> starsAbout;
|
||||
rpl::producer<TextWithEntities> tonAbout;
|
||||
};
|
||||
|
||||
[[nodiscard]] StarsTonPriceInput AddStarsTonPriceInput(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
StarsTonPriceArgs &&args);
|
||||
|
||||
struct SuggestPriceBoxArgs {
|
||||
not_null<PeerData*> peer;
|
||||
bool updating = false;
|
||||
|
@ -96,6 +96,13 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
_mediaLifetime.destroy();
|
||||
unsubscribe();
|
||||
|
||||
const auto format = [=](int64 number) {
|
||||
const auto onlyK = (number < 100'000'000);
|
||||
return (number >= 1'000'000)
|
||||
? Lang::FormatCountToShort(number, onlyK).string
|
||||
: Lang::FormatCountDecimal(number);
|
||||
};
|
||||
|
||||
_descriptor = descriptor;
|
||||
_resalePrice = resalePrice;
|
||||
const auto resale = (_resalePrice > 0);
|
||||
@ -156,19 +163,18 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
? (unique
|
||||
? _delegate->monostar()
|
||||
: _delegate->star()).append(' ').append(
|
||||
Lang::FormatCountDecimal(unique
|
||||
format(unique
|
||||
? unique->starsForResale
|
||||
: data.info.starsResellMin)
|
||||
).append(data.info.resellCount > 1 ? "+" : "")
|
||||
: (_small && unique && unique->starsForResale)
|
||||
? _delegate->monostar().append(' ').append(
|
||||
Lang::FormatCountDecimal(unique->starsForResale))
|
||||
format(unique->starsForResale))
|
||||
: unique
|
||||
? tr::lng_gift_transfer_button(
|
||||
tr::now,
|
||||
Ui::Text::WithEntities)
|
||||
: _delegate->star().append(
|
||||
' ' + Lang::FormatCountDecimal(data.info.stars))),
|
||||
: _delegate->star().append(' ' + format(data.info.stars))),
|
||||
kMarkupTextOptions,
|
||||
_delegate->textContext());
|
||||
if (!_stars) {
|
||||
|
@ -917,7 +917,7 @@ int NonZeroPartToInt(QString value) {
|
||||
: (value.isEmpty() ? 0 : value.toInt());
|
||||
}
|
||||
|
||||
ShortenedCount FormatCountToShort(int64 number) {
|
||||
ShortenedCount FormatCountToShort(int64 number, bool onlyK) {
|
||||
auto result = ShortenedCount{ number };
|
||||
const auto abs = std::abs(number);
|
||||
const auto shorten = [&](int64 divider, char multiplier) {
|
||||
@ -934,7 +934,7 @@ ShortenedCount FormatCountToShort(int64 number) {
|
||||
result.number = rounded * divider;
|
||||
result.shortened = true;
|
||||
};
|
||||
if (abs >= 1'000'000) {
|
||||
if (!onlyK && abs >= 1'000'000) {
|
||||
shorten(1'000'000, 'M');
|
||||
} else if (abs >= 10'000) {
|
||||
shorten(1'000, 'K');
|
||||
|
@ -26,7 +26,9 @@ struct ShortenedCount {
|
||||
QString string;
|
||||
bool shortened = false;
|
||||
};
|
||||
[[nodiscard]] ShortenedCount FormatCountToShort(int64 number);
|
||||
[[nodiscard]] ShortenedCount FormatCountToShort(
|
||||
int64 number,
|
||||
bool onlyK = false);
|
||||
[[nodiscard]] QString FormatCountDecimal(int64 number);
|
||||
[[nodiscard]] QString FormatExactCountDecimal(float64 number);
|
||||
[[nodiscard]] ShortenedCount FormatCreditsAmountToShort(
|
||||
|
@ -148,18 +148,32 @@ bool AppConfig::confcallPrioritizeVP8() const {
|
||||
return get<bool>(u"confcall_use_vp8"_q, false);
|
||||
}
|
||||
|
||||
int AppConfig::giftResalePriceMax() const {
|
||||
return get<int>(u"stars_stargift_resale_amount_max"_q, 35000);
|
||||
}
|
||||
|
||||
int AppConfig::giftResalePriceMin() const {
|
||||
int AppConfig::giftResaleStarsMin() const {
|
||||
return get<int>(u"stars_stargift_resale_amount_min"_q, 125);
|
||||
}
|
||||
|
||||
int AppConfig::giftResaleReceiveThousandths() const {
|
||||
int AppConfig::giftResaleStarsMax() const {
|
||||
return get<int>(u"stars_stargift_resale_amount_max"_q, 35000);
|
||||
}
|
||||
|
||||
int AppConfig::giftResaleStarsThousandths() const {
|
||||
return get<int>(u"stars_stargift_resale_commission_permille"_q, 800);
|
||||
}
|
||||
|
||||
int64 AppConfig::giftResaleNanoTonMin() const {
|
||||
return get<int64>(u"ton_stargift_resale_amount_min"_q, 250'000'000LL);
|
||||
}
|
||||
|
||||
int64 AppConfig::giftResaleNanoTonMax() const {
|
||||
return get<int64>(
|
||||
u"ton_stargift_resale_amount_max"_q,
|
||||
1'000'000'000'000'000LL);
|
||||
}
|
||||
|
||||
int AppConfig::giftResaleNanoTonThousandths() const {
|
||||
return get<int>(u"ton_stargift_resale_commission_permille"_q, 800);
|
||||
}
|
||||
|
||||
int AppConfig::pollOptionsLimit() const {
|
||||
return get<int>(u"poll_answers_max"_q, 12);
|
||||
}
|
||||
|
@ -85,9 +85,12 @@ public:
|
||||
[[nodiscard]] int confcallSizeLimit() const;
|
||||
[[nodiscard]] bool confcallPrioritizeVP8() const;
|
||||
|
||||
[[nodiscard]] int giftResalePriceMax() const;
|
||||
[[nodiscard]] int giftResalePriceMin() const;
|
||||
[[nodiscard]] int giftResaleReceiveThousandths() const;
|
||||
[[nodiscard]] int giftResaleStarsMin() const;
|
||||
[[nodiscard]] int giftResaleStarsMax() const;
|
||||
[[nodiscard]] int giftResaleStarsThousandths() const;
|
||||
[[nodiscard]] int64 giftResaleNanoTonMin() const;
|
||||
[[nodiscard]] int64 giftResaleNanoTonMax() const;
|
||||
[[nodiscard]] int giftResaleNanoTonThousandths() const;
|
||||
|
||||
[[nodiscard]] int pollOptionsLimit() const;
|
||||
[[nodiscard]] int todoListItemsLimit() const;
|
||||
|
@ -1081,7 +1081,7 @@ void FillUniqueGiftMenu(
|
||||
const auto name = UniqueGiftName(*unique);
|
||||
const auto confirm = [=](Fn<void()> close) {
|
||||
close();
|
||||
Ui::UpdateGiftSellPrice(show, unique, savedId, 0);
|
||||
Ui::UpdateGiftSellPrice(show, unique, savedId, {});
|
||||
};
|
||||
show->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_gift_sell_unlist_sure(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user