mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 05:07:10 +02:00
Support gifting premium for stars.
This commit is contained in:
parent
bd70a05861
commit
7d2878d81c
26 changed files with 321 additions and 112 deletions
BIN
Telegram/Resources/icons/payments/premium_emoji.png
Normal file
BIN
Telegram/Resources/icons/payments/premium_emoji.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 370 B |
BIN
Telegram/Resources/icons/payments/premium_emoji@2x.png
Normal file
BIN
Telegram/Resources/icons/payments/premium_emoji@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 712 B |
BIN
Telegram/Resources/icons/payments/premium_emoji@3x.png
Normal file
BIN
Telegram/Resources/icons/payments/premium_emoji@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 926 B |
|
@ -3329,6 +3329,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
|
||||
"lng_gift_premium_features" = "See Features >";
|
||||
"lng_gift_premium_label" = "Premium";
|
||||
"lng_gift_premium_by_stars" = "or {amount}";
|
||||
"lng_gift_stars_subtitle" = "Gift Stars";
|
||||
"lng_gift_stars_about" = "Give {name} gifts that can be kept on your profile or converted to Stars. {link}";
|
||||
"lng_gift_stars_link" = "What are Stars >";
|
||||
|
@ -3340,6 +3341,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_gift_send_title" = "Send a Gift";
|
||||
"lng_gift_send_message" = "Enter Message";
|
||||
"lng_gift_send_anonymous" = "Hide My Name";
|
||||
"lng_gift_send_pay_with_stars" = "Pay with {amount}";
|
||||
"lng_gift_send_stars_balance" = "Your balance is {amount}. {link}";
|
||||
"lng_gift_send_stars_balance_link" = "Get More Stars >";
|
||||
"lng_gift_send_anonymous_self" = "Hide my name and message from visitors to my profile.";
|
||||
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
|
||||
"lng_gift_send_anonymous_about_paid" = "You can hide your name from visitors to {user}'s profile. {recipient} will still see your name.";
|
||||
|
|
|
@ -479,6 +479,9 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::request() {
|
|||
for (const auto &tlOption : result.v) {
|
||||
const auto &data = tlOption.data();
|
||||
tlMapOptions[data.vusers().v].push_back(tlOption);
|
||||
if (qs(data.vcurrency()) == Ui::kCreditsCurrency) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto token = Token{ data.vusers().v, data.vmonths().v };
|
||||
_stores[token] = Store{
|
||||
|
|
|
@ -30,11 +30,6 @@ ShortInfoBox {
|
|||
labeledOneLine: FlatLabel;
|
||||
}
|
||||
|
||||
boxStarIconEmoji: IconEmoji {
|
||||
icon: icon{{ "payments/small_star", windowFg }};
|
||||
padding: margins(0px, -2px, 0px, 0px);
|
||||
}
|
||||
|
||||
countryRowHeight: 36px;
|
||||
countryRowNameFont: semiboldFont;
|
||||
countryRowNameFg: boxTextFg;
|
||||
|
|
|
@ -512,7 +512,7 @@ TextWithEntities CreditsEmoji(not_null<Main::Session*> session) {
|
|||
|
||||
TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
|
||||
return Ui::Text::IconEmoji(
|
||||
&st::boxStarIconEmoji,
|
||||
&st::starIconEmoji,
|
||||
QString(QChar(0x2B50)));
|
||||
}
|
||||
|
||||
|
@ -570,7 +570,7 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
|
|||
}, st, textFg);
|
||||
}
|
||||
|
||||
void SendStarGift(
|
||||
void SendStarsForm(
|
||||
not_null<Main::Session*> session,
|
||||
std::shared_ptr<Payments::CreditsFormData> data,
|
||||
Fn<void(std::optional<QString>)> done) {
|
||||
|
|
|
@ -52,7 +52,7 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
|
|||
const style::FlatLabel &st,
|
||||
const style::color *textFg = nullptr);
|
||||
|
||||
void SendStarGift(
|
||||
void SendStarsForm(
|
||||
not_null<Main::Session*> session,
|
||||
std::shared_ptr<Payments::CreditsFormData> data,
|
||||
Fn<void(std::optional<QString>)> done);
|
||||
|
|
|
@ -128,6 +128,7 @@ struct GiftDetails {
|
|||
uint64 randomId = 0;
|
||||
bool anonymous = false;
|
||||
bool upgraded = false;
|
||||
bool byStars = false;
|
||||
};
|
||||
|
||||
class PreviewDelegate final : public DefaultElementDelegate {
|
||||
|
@ -497,7 +498,14 @@ void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
|
|||
std::move(details) | rpl::start_with_next([=](GiftDetails details) {
|
||||
const auto &descriptor = details.descriptor;
|
||||
const auto cost = v::match(descriptor, [&](GiftTypePremium data) {
|
||||
return FillAmountAndCurrency(data.cost, data.currency, true);
|
||||
const auto stars = (details.byStars && data.stars)
|
||||
? data.stars
|
||||
: (data.currency == kCreditsCurrency)
|
||||
? data.cost
|
||||
: 0;
|
||||
return stars
|
||||
? tr::lng_gift_stars_title(tr::now, lt_count, stars)
|
||||
: FillAmountAndCurrency(data.cost, data.currency, true);
|
||||
}, [&](GiftTypeStars data) {
|
||||
const auto stars = data.info.stars
|
||||
+ (details.upgraded ? data.info.starsToUpgrade : 0);
|
||||
|
@ -1118,16 +1126,35 @@ void SendGift(
|
|||
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
|
||||
const GiftDetails &details,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
const auto processNonPanelPaymentFormFactory
|
||||
= Payments::ProcessNonPanelPaymentFormFactory(window, done);
|
||||
v::match(details.descriptor, [&](const GiftTypePremium &gift) {
|
||||
auto invoice = api->invoice(1, gift.months);
|
||||
invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{
|
||||
.users = { peer->asUser() },
|
||||
.message = details.text,
|
||||
};
|
||||
Payments::CheckoutProcess::Start(std::move(invoice), done);
|
||||
if (details.byStars && gift.stars) {
|
||||
auto invoice = Payments::InvoicePremiumGiftCode{
|
||||
.purpose = Payments::InvoicePremiumGiftCodeUsers{
|
||||
.users = { peer->asUser() },
|
||||
.message = details.text,
|
||||
},
|
||||
.currency = Ui::kCreditsCurrency,
|
||||
.randomId = details.randomId,
|
||||
.amount = uint64(gift.stars),
|
||||
.storeQuantity = 1,
|
||||
.users = 1,
|
||||
.months = gift.months,
|
||||
};
|
||||
Payments::CheckoutProcess::Start(
|
||||
std::move(invoice),
|
||||
done,
|
||||
processNonPanelPaymentFormFactory);
|
||||
} else {
|
||||
auto invoice = api->invoice(1, gift.months);
|
||||
invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{
|
||||
.users = { peer->asUser() },
|
||||
.message = details.text,
|
||||
};
|
||||
Payments::CheckoutProcess::Start(std::move(invoice), done);
|
||||
}
|
||||
}, [&](const GiftTypeStars &gift) {
|
||||
const auto processNonPanelPaymentFormFactory
|
||||
= Payments::ProcessNonPanelPaymentFormFactory(window, done);
|
||||
Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{
|
||||
.giftId = gift.info.id,
|
||||
.randomId = details.randomId,
|
||||
|
@ -1459,9 +1486,14 @@ void SendGiftBox(
|
|||
auto cost = state->details.value(
|
||||
) | rpl::map([session](const GiftDetails &details) {
|
||||
return v::match(details.descriptor, [&](const GiftTypePremium &data) {
|
||||
if (data.currency == kCreditsCurrency) {
|
||||
const auto stars = (details.byStars && data.stars)
|
||||
? data.stars
|
||||
: (data.currency == kCreditsCurrency)
|
||||
? data.cost
|
||||
: 0;
|
||||
if (stars) {
|
||||
return CreditsEmojiSmall(session).append(
|
||||
Lang::FormatCountDecimal(std::abs(data.cost)));
|
||||
Lang::FormatCountDecimal(std::abs(stars)));
|
||||
}
|
||||
return TextWithEntities{
|
||||
FillAmountAndCurrency(data.cost, data.currency),
|
||||
|
@ -1580,10 +1612,56 @@ void SendGiftBox(
|
|||
}, container->lifetime());
|
||||
AddSkip(container);
|
||||
}
|
||||
v::match(descriptor, [&](const GiftTypePremium &) {
|
||||
v::match(descriptor, [&](const GiftTypePremium &data) {
|
||||
AddDividerText(messageInner, tr::lng_gift_send_premium_about(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName())));
|
||||
|
||||
if (const auto byStars = data.stars) {
|
||||
const auto star = Ui::Text::IconEmoji(&st::starIconEmojiColored);
|
||||
AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
container,
|
||||
tr::lng_gift_send_pay_with_stars(
|
||||
lt_amount,
|
||||
rpl::single(base::duplicate(star).append(Lang::FormatCountDecimal(byStars))),
|
||||
Ui::Text::WithEntities),
|
||||
st::settingsButtonNoIcon)
|
||||
)->toggleOn(rpl::single(false))->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
auto now = state->details.current();
|
||||
now.byStars = toggled;
|
||||
state->details = std::move(now);
|
||||
}, container->lifetime());
|
||||
AddSkip(container);
|
||||
|
||||
const auto balance = AddDividerText(
|
||||
container,
|
||||
tr::lng_gift_send_stars_balance(
|
||||
lt_amount,
|
||||
peer->session().credits().balanceValue(
|
||||
) | rpl::map([=](StarsAmount amount) {
|
||||
return base::duplicate(star).append(
|
||||
Lang::FormatStarsAmountDecimal(amount));
|
||||
}),
|
||||
lt_link,
|
||||
tr::lng_gift_send_stars_balance_link(
|
||||
) | Ui::Text::ToLink(),
|
||||
Ui::Text::WithEntities));
|
||||
struct State {
|
||||
Settings::BuyStarsHandler buyStars;
|
||||
rpl::variable<bool> loading;
|
||||
};
|
||||
const auto state = balance->lifetime().make_state<State>();
|
||||
state->loading = state->buyStars.loadingValue();
|
||||
balance->setClickHandlerFilter([=](const auto &...) {
|
||||
if (!state->loading.current()) {
|
||||
state->buyStars.handler(window->uiShow())();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}, [&](const GiftTypeStars &) {
|
||||
AddDividerText(container, peer->isSelf()
|
||||
? tr::lng_gift_send_anonymous_self()
|
||||
|
@ -1618,9 +1696,13 @@ void SendGiftBox(
|
|||
const auto weak = MakeWeak(box);
|
||||
const auto done = [=](Payments::CheckoutResult result) {
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
if (details.byStars
|
||||
|| v::is<GiftTypeStars>(details.descriptor)) {
|
||||
window->session().credits().load(true);
|
||||
}
|
||||
const auto copy = state->media;
|
||||
window->showPeerHistory(peer);
|
||||
ShowSentToast(window, descriptor, details);
|
||||
ShowSentToast(window, details.descriptor, details);
|
||||
}
|
||||
if (const auto strong = weak.data()) {
|
||||
box->closeBox();
|
||||
|
@ -1853,6 +1935,8 @@ void GiftBox(
|
|||
box->setCustomCornersFilling(RectPart::FullTop);
|
||||
box->addButton(tr::lng_create_group_back(), [=] { box->closeBox(); });
|
||||
|
||||
window->session().credits().load();
|
||||
|
||||
FillBg(box);
|
||||
|
||||
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||
|
|
|
@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
|
||||
|
@ -1282,7 +1283,7 @@ void SelectTextInFieldWithMargins(
|
|||
}
|
||||
|
||||
TextWithEntities PaidSendButtonText(tr::now_t, int stars) {
|
||||
return Ui::Text::IconEmoji(&st::boxStarIconEmoji).append(
|
||||
return Ui::Text::IconEmoji(&st::starIconEmoji).append(
|
||||
Lang::FormatCountToShort(stars).string);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,14 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
|||
{ u"first_chat"_q , Command::ChatFirst },
|
||||
{ u"last_chat"_q , Command::ChatLast },
|
||||
{ u"self_chat"_q , Command::ChatSelf },
|
||||
{ u"pinned_chat1"_q , Command::ChatPinned1 },
|
||||
{ u"pinned_chat2"_q , Command::ChatPinned2 },
|
||||
{ u"pinned_chat3"_q , Command::ChatPinned3 },
|
||||
{ u"pinned_chat4"_q , Command::ChatPinned4 },
|
||||
{ u"pinned_chat5"_q , Command::ChatPinned5 },
|
||||
{ u"pinned_chat6"_q , Command::ChatPinned6 },
|
||||
{ u"pinned_chat7"_q , Command::ChatPinned7 },
|
||||
{ u"pinned_chat8"_q , Command::ChatPinned8 },
|
||||
|
||||
{ u"previous_folder"_q , Command::FolderPrevious },
|
||||
{ u"next_folder"_q , Command::FolderNext },
|
||||
|
|
|
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_credits.h" // giftBoxByStarsStyle
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
@ -1027,6 +1028,14 @@ TextWithEntities CustomEmojiManager::creditsEmoji(QMargins padding) {
|
|||
false));
|
||||
}
|
||||
|
||||
TextWithEntities CustomEmojiManager::ministarEmoji(QMargins padding) {
|
||||
return Ui::Text::SingleCustomEmoji(
|
||||
registerInternalEmoji(
|
||||
Ui::GenerateStars(st::giftBoxByStarsStyle.font->height, 1),
|
||||
padding,
|
||||
false));
|
||||
}
|
||||
|
||||
QString CustomEmojiManager::registerInternalEmoji(
|
||||
QImage emoji,
|
||||
QMargins padding,
|
||||
|
|
|
@ -100,6 +100,7 @@ public:
|
|||
[[nodiscard]] uint64 coloredSetId() const;
|
||||
|
||||
[[nodiscard]] TextWithEntities creditsEmoji(QMargins padding = {});
|
||||
[[nodiscard]] TextWithEntities ministarEmoji(QMargins padding = {});
|
||||
|
||||
private:
|
||||
static constexpr auto kSizeCount = int(SizeTag::kCount);
|
||||
|
|
|
@ -85,13 +85,11 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
|||
unsubscribe();
|
||||
v::match(descriptor, [&](const GiftTypePremium &data) {
|
||||
const auto months = data.months;
|
||||
const auto years = (months % 12) ? 0 : months / 12;
|
||||
_text = Ui::Text::String(st::giftBoxGiftHeight / 4);
|
||||
_text.setMarkedText(
|
||||
st::defaultTextStyle,
|
||||
Ui::Text::Bold(years
|
||||
? tr::lng_years(tr::now, lt_count, years)
|
||||
: tr::lng_months(tr::now, lt_count, months)
|
||||
Ui::Text::Bold(
|
||||
tr::lng_months(tr::now, lt_count, months)
|
||||
).append('\n').append(
|
||||
tr::lng_gift_premium_label(tr::now)
|
||||
));
|
||||
|
@ -101,6 +99,18 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
|||
data.cost,
|
||||
data.currency,
|
||||
true));
|
||||
if (const auto stars = data.stars) {
|
||||
const auto starsText = QString::number(stars);
|
||||
_byStars.setMarkedText(
|
||||
st::giftBoxByStarsStyle,
|
||||
tr::lng_gift_premium_by_stars(
|
||||
tr::now,
|
||||
lt_amount,
|
||||
_delegate->ministar().append(' ' + starsText),
|
||||
Ui::Text::WithEntities),
|
||||
kMarkupTextOptions,
|
||||
_delegate->textContext());
|
||||
}
|
||||
_userpic = nullptr;
|
||||
if (!_stars) {
|
||||
_stars.emplace(this, true, starsType);
|
||||
|
@ -170,7 +180,9 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
|||
QSize(buttonw, buttonh)
|
||||
).marginsAdded(st::giftBoxButtonPadding);
|
||||
const auto skipy = _delegate->buttonSize().height()
|
||||
- st::giftBoxButtonBottom
|
||||
- (_byStars.isEmpty()
|
||||
? st::giftBoxButtonBottom
|
||||
: st::giftBoxButtonBottomByStars)
|
||||
- inner.height();
|
||||
const auto skipx = (width() - inner.width()) / 2;
|
||||
const auto outer = (width() - 2 * skipx);
|
||||
|
@ -355,7 +367,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
|||
? st::giftBoxSmallStickerTop
|
||||
: _text.isEmpty()
|
||||
? st::giftBoxStickerStarTop
|
||||
: st::giftBoxStickerTop),
|
||||
: _byStars.isEmpty()
|
||||
? st::giftBoxStickerTop
|
||||
: st::giftBoxStickerTopByStars),
|
||||
size.width(),
|
||||
size.height()),
|
||||
frame);
|
||||
|
@ -367,7 +381,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
|||
? st::giftBoxSmallStickerTop
|
||||
: _text.isEmpty()
|
||||
? st::giftBoxStickerStarTop
|
||||
: st::giftBoxStickerTop));
|
||||
: _byStars.isEmpty()
|
||||
? st::giftBoxStickerTop
|
||||
: st::giftBoxStickerTopByStars));
|
||||
_delegate->hiddenMark()->paint(
|
||||
p,
|
||||
frame,
|
||||
|
@ -473,8 +489,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
|||
if (!_text.isEmpty()) {
|
||||
p.setPen(st::windowFg);
|
||||
_text.draw(p, {
|
||||
.position = (position
|
||||
+ QPoint(0, st::giftBoxPremiumTextTop)),
|
||||
.position = (position + QPoint(0, _byStars.isEmpty()
|
||||
? st::giftBoxPremiumTextTop
|
||||
: st::giftBoxPremiumTextTopByStars)),
|
||||
.availableWidth = singlew,
|
||||
.align = style::al_top,
|
||||
});
|
||||
|
@ -492,6 +509,17 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
|||
+ QPoint(padding.left(), padding.top())),
|
||||
.availableWidth = _price.maxWidth(),
|
||||
});
|
||||
|
||||
if (!_byStars.isEmpty()) {
|
||||
p.setPen(st::creditsFg);
|
||||
_byStars.draw(p, {
|
||||
.position = QPoint(
|
||||
position.x(),
|
||||
_button.y() + _button.height() + st::giftBoxByStarsSkip),
|
||||
.availableWidth = singlew,
|
||||
.align = style::al_top,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,6 +543,12 @@ TextWithEntities Delegate::star() {
|
|||
return owner->customEmojiManager().creditsEmoji();
|
||||
}
|
||||
|
||||
TextWithEntities Delegate::ministar() {
|
||||
const auto owner = &_window->session().data();
|
||||
const auto top = st::giftBoxByStarsStarTop;
|
||||
return owner->customEmojiManager().ministarEmoji({ 0, top, 0, 0 });
|
||||
}
|
||||
|
||||
std::any Delegate::textContext() {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &_window->session(),
|
||||
|
|
|
@ -102,6 +102,7 @@ enum class GiftButtonMode {
|
|||
class GiftButtonDelegate {
|
||||
public:
|
||||
[[nodiscard]] virtual TextWithEntities star() = 0;
|
||||
[[nodiscard]] virtual TextWithEntities ministar() = 0;
|
||||
[[nodiscard]] virtual std::any textContext() = 0;
|
||||
[[nodiscard]] virtual QSize buttonSize() = 0;
|
||||
[[nodiscard]] virtual QMargins buttonExtend() = 0;
|
||||
|
@ -144,6 +145,7 @@ private:
|
|||
GiftDescriptor _descriptor;
|
||||
Ui::Text::String _text;
|
||||
Ui::Text::String _price;
|
||||
Ui::Text::String _byStars;
|
||||
std::shared_ptr<Ui::DynamicImage> _userpic;
|
||||
QImage _uniqueBackgroundCache;
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> _uniquePatternEmoji;
|
||||
|
@ -170,6 +172,7 @@ public:
|
|||
~Delegate();
|
||||
|
||||
TextWithEntities star() override;
|
||||
TextWithEntities ministar() override;
|
||||
std::any textContext() override;
|
||||
QSize buttonSize() override;
|
||||
QMargins buttonExtend() override;
|
||||
|
|
|
@ -66,7 +66,6 @@ void CheckoutProcess::Start(
|
|||
Mode mode,
|
||||
Fn<void(CheckoutResult)> reactivate,
|
||||
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
|
||||
const auto hasNonPanelPaymentFormProcess = !!nonPanelPaymentFormProcess;
|
||||
auto &processes = LookupSessionProcesses(&item->history()->session());
|
||||
const auto media = item->media();
|
||||
const auto invoice = media ? media->invoice() : nullptr;
|
||||
|
@ -87,9 +86,7 @@ void CheckoutProcess::Start(
|
|||
i->second->setReactivateCallback(std::move(reactivate));
|
||||
i->second->setNonPanelPaymentFormProcess(
|
||||
std::move(nonPanelPaymentFormProcess));
|
||||
if (!hasNonPanelPaymentFormProcess) {
|
||||
i->second->requestActivate();
|
||||
}
|
||||
i->second->requestActivate();
|
||||
return;
|
||||
}
|
||||
const auto j = processes.byItem.emplace(
|
||||
|
@ -100,9 +97,7 @@ void CheckoutProcess::Start(
|
|||
std::move(reactivate),
|
||||
std::move(nonPanelPaymentFormProcess),
|
||||
PrivateTag{})).first;
|
||||
if (!hasNonPanelPaymentFormProcess) {
|
||||
j->second->requestActivate();
|
||||
}
|
||||
j->second->requestActivate();
|
||||
}
|
||||
|
||||
void CheckoutProcess::Start(
|
||||
|
@ -110,16 +105,13 @@ void CheckoutProcess::Start(
|
|||
const QString &slug,
|
||||
Fn<void(CheckoutResult)> reactivate,
|
||||
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
|
||||
const auto hasNonPanelPaymentFormProcess = !!nonPanelPaymentFormProcess;
|
||||
auto &processes = LookupSessionProcesses(session);
|
||||
const auto i = processes.bySlug.find(slug);
|
||||
if (i != end(processes.bySlug)) {
|
||||
i->second->setReactivateCallback(std::move(reactivate));
|
||||
i->second->setNonPanelPaymentFormProcess(
|
||||
std::move(nonPanelPaymentFormProcess));
|
||||
if (!hasNonPanelPaymentFormProcess) {
|
||||
i->second->requestActivate();
|
||||
}
|
||||
i->second->requestActivate();
|
||||
return;
|
||||
}
|
||||
const auto j = processes.bySlug.emplace(
|
||||
|
@ -130,20 +122,21 @@ void CheckoutProcess::Start(
|
|||
std::move(reactivate),
|
||||
std::move(nonPanelPaymentFormProcess),
|
||||
PrivateTag{})).first;
|
||||
if (!hasNonPanelPaymentFormProcess) {
|
||||
j->second->requestActivate();
|
||||
}
|
||||
j->second->requestActivate();
|
||||
}
|
||||
|
||||
void CheckoutProcess::Start(
|
||||
InvoicePremiumGiftCode giftCodeInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate) {
|
||||
Fn<void(CheckoutResult)> reactivate,
|
||||
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
|
||||
const auto randomId = giftCodeInvoice.randomId;
|
||||
auto id = InvoiceId{ std::move(giftCodeInvoice) };
|
||||
auto &processes = LookupSessionProcesses(SessionFromId(id));
|
||||
const auto i = processes.byRandomId.find(randomId);
|
||||
if (i != end(processes.byRandomId)) {
|
||||
i->second->setReactivateCallback(std::move(reactivate));
|
||||
i->second->setNonPanelPaymentFormProcess(
|
||||
std::move(nonPanelPaymentFormProcess));
|
||||
i->second->requestActivate();
|
||||
return;
|
||||
}
|
||||
|
@ -153,7 +146,7 @@ void CheckoutProcess::Start(
|
|||
std::move(id),
|
||||
Mode::Payment,
|
||||
std::move(reactivate),
|
||||
nullptr,
|
||||
std::move(nonPanelPaymentFormProcess),
|
||||
PrivateTag{})).first;
|
||||
j->second->requestActivate();
|
||||
}
|
||||
|
@ -372,7 +365,9 @@ void CheckoutProcess::setNonPanelPaymentFormProcess(
|
|||
}
|
||||
|
||||
void CheckoutProcess::requestActivate() {
|
||||
_panel->requestActivate();
|
||||
if (!_nonPanelPaymentFormProcess) {
|
||||
_panel->requestActivate();
|
||||
}
|
||||
}
|
||||
|
||||
not_null<Ui::PanelDelegate*> CheckoutProcess::panelDelegate() {
|
||||
|
|
|
@ -88,7 +88,8 @@ public:
|
|||
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess);
|
||||
static void Start(
|
||||
InvoicePremiumGiftCode giftCodeInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate);
|
||||
Fn<void(CheckoutResult)> reactivate,
|
||||
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess = nullptr);
|
||||
static void Start(
|
||||
InvoiceCredits creditsInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate);
|
||||
|
|
|
@ -219,6 +219,13 @@ MTPinputStorePaymentPurpose InvoiceCreditsGiveawayToTL(
|
|||
MTP_int(invoice.users));
|
||||
}
|
||||
|
||||
bool IsPremiumForStarsInvoice(const InvoiceId &id) {
|
||||
const auto giftCode = std::get_if<InvoicePremiumGiftCode>(&id.value);
|
||||
return giftCode
|
||||
&& !giftCode->creditsAmount
|
||||
&& (giftCode->currency == ::Ui::kCreditsCurrency);
|
||||
}
|
||||
|
||||
Form::Form(InvoiceId id, bool receipt)
|
||||
: _id(id)
|
||||
, _session(SessionFromId(id))
|
||||
|
@ -412,12 +419,29 @@ MTPInputInvoice Form::inputInvoice() const {
|
|||
MTP_long(giftCode.amount));
|
||||
const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
|
||||
&giftCode.purpose);
|
||||
if (users) {
|
||||
auto message = (users && !users->message.empty())
|
||||
? MTP_textWithEntities(
|
||||
MTP_string(users->message.text),
|
||||
Api::EntitiesToMTP(
|
||||
&users->users.front()->session(),
|
||||
users->message.entities,
|
||||
Api::ConvertOption::SkipLocal))
|
||||
: std::optional<MTPTextWithEntities>();
|
||||
if (users
|
||||
&& users->users.size() == 1
|
||||
&& giftCode.currency == ::Ui::kCreditsCurrency) {
|
||||
using Flag = MTPDinputInvoicePremiumGiftStars::Flag;
|
||||
return MTP_inputInvoicePremiumGiftStars(
|
||||
MTP_flags(message ? Flag::f_message : Flag()),
|
||||
users->users.front()->inputUser,
|
||||
MTP_int(giftCode.months),
|
||||
message.value_or(MTPTextWithEntities()));
|
||||
} else if (users) {
|
||||
using Flag = MTPDinputStorePaymentPremiumGiftCode::Flag;
|
||||
return MTP_inputInvoicePremiumGiftCode(
|
||||
MTP_inputStorePaymentPremiumGiftCode(
|
||||
MTP_flags((users->boostPeer ? Flag::f_boost_peer : Flag())
|
||||
| (users->message.empty() ? Flag(0) : Flag::f_message)),
|
||||
| (message ? Flag::f_message : Flag())),
|
||||
MTP_vector_from_range(ranges::views::all(
|
||||
users->users
|
||||
) | ranges::views::transform([](not_null<UserData*> user) {
|
||||
|
@ -426,12 +450,7 @@ MTPInputInvoice Form::inputInvoice() const {
|
|||
users->boostPeer ? users->boostPeer->input : MTPInputPeer(),
|
||||
MTP_string(giftCode.currency),
|
||||
MTP_long(giftCode.amount),
|
||||
MTP_textWithEntities(
|
||||
MTP_string(users->message.text),
|
||||
Api::EntitiesToMTP(
|
||||
&users->users.front()->session(),
|
||||
users->message.entities,
|
||||
Api::ConvertOption::SkipLocal))),
|
||||
message.value_or(MTPTextWithEntities())),
|
||||
option);
|
||||
} else {
|
||||
return MTP_inputInvoicePremiumGiftCode(
|
||||
|
|
|
@ -287,6 +287,8 @@ struct FormUpdate : std::variant<
|
|||
[[nodiscard]] MTPinputStorePaymentPurpose InvoiceCreditsGiveawayToTL(
|
||||
const InvoicePremiumGiftCode &invoice);
|
||||
|
||||
[[nodiscard]] bool IsPremiumForStarsInvoice(const InvoiceId &id);
|
||||
|
||||
class Form final : public base::has_weak_ptr {
|
||||
public:
|
||||
Form(InvoiceId id, bool receipt);
|
||||
|
|
|
@ -57,7 +57,8 @@ void ProcessCreditsPayment(
|
|||
onstack(CheckoutResult::Cancelled);
|
||||
}
|
||||
return;
|
||||
} else if (form->starGiftForm) {
|
||||
} else if (form->starGiftForm
|
||||
|| IsPremiumForStarsInvoice(form->id)) {
|
||||
const auto done = [=](std::optional<QString> error) {
|
||||
const auto onstack = maybeReturnToBot;
|
||||
if (error) {
|
||||
|
@ -86,7 +87,7 @@ void ProcessCreditsPayment(
|
|||
onstack(CheckoutResult::Paid);
|
||||
}
|
||||
};
|
||||
Ui::SendStarGift(&show->session(), form, done);
|
||||
Ui::SendStarsForm(&show->session(), form, done);
|
||||
return;
|
||||
}
|
||||
const auto unsuccessful = std::make_shared<bool>(true);
|
||||
|
|
|
@ -429,8 +429,7 @@ void Credits::setupContent() {
|
|||
Ui::AddSkip(content);
|
||||
|
||||
struct State final {
|
||||
rpl::variable<bool> confirmButtonBusy = false;
|
||||
std::optional<Api::CreditsTopupOptions> api;
|
||||
BuyStarsHandler buyStars;
|
||||
};
|
||||
const auto state = content->lifetime().make_state<State>();
|
||||
|
||||
|
@ -438,60 +437,21 @@ void Credits::setupContent() {
|
|||
object_ptr<Ui::RoundButton>(
|
||||
content,
|
||||
rpl::conditional(
|
||||
state->confirmButtonBusy.value(),
|
||||
state->buyStars.loadingValue(),
|
||||
rpl::single(QString()),
|
||||
tr::lng_credits_buy_button()),
|
||||
st::creditsSettingsBigBalanceButton),
|
||||
st::boxRowPadding);
|
||||
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
const auto show = _controller->uiShow();
|
||||
const auto optionsBox = [=](not_null<Ui::GenericBox*> box) {
|
||||
box->setStyle(st::giveawayGiftCodeBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setTitle(tr::lng_credits_summary_options_subtitle());
|
||||
const auto inner = box->verticalLayout();
|
||||
const auto self = show->session().user();
|
||||
const auto options = state->api
|
||||
? state->api->options()
|
||||
: Data::CreditTopupOptions();
|
||||
const auto amount = StarsAmount();
|
||||
FillCreditOptions(show, inner, self, amount, paid, nullptr, options);
|
||||
|
||||
const auto button = box->addButton(tr::lng_close(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
const auto buttonWidth = st::boxWideWidth
|
||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
};
|
||||
button->setClickedCallback([=] {
|
||||
if (state->api && !state->api->options().empty()) {
|
||||
state->confirmButtonBusy = false;
|
||||
show->show(Box(optionsBox));
|
||||
} else {
|
||||
state->confirmButtonBusy = true;
|
||||
state->api.emplace(show->session().user());
|
||||
state->api->request(
|
||||
) | rpl::start_with_error_done([=](const QString &error) {
|
||||
state->confirmButtonBusy = false;
|
||||
show->showToast(error);
|
||||
}, [=] {
|
||||
state->confirmButtonBusy = false;
|
||||
show->show(Box(optionsBox));
|
||||
}, content->lifetime());
|
||||
}
|
||||
});
|
||||
button->setClickedCallback(state->buyStars.handler(show, paid));
|
||||
{
|
||||
using namespace Info::Statistics;
|
||||
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||
button,
|
||||
button->height() / 2);
|
||||
AddChildToWidgetCenter(button, loadingAnimation);
|
||||
loadingAnimation->showOn(state->confirmButtonBusy.value());
|
||||
loadingAnimation->showOn(state->buyStars.loadingValue());
|
||||
}
|
||||
const auto paddings = rect::m::sum::h(st::boxRowPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
|
@ -699,4 +659,62 @@ Type CreditsId() {
|
|||
return Credits::Id();
|
||||
}
|
||||
|
||||
Fn<void()> BuyStarsHandler::handler(
|
||||
std::shared_ptr<::Main::SessionShow> show,
|
||||
Fn<void()> paid) {
|
||||
const auto optionsBox = [=](not_null<Ui::GenericBox*> box) {
|
||||
box->setStyle(st::giveawayGiftCodeBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setTitle(tr::lng_credits_summary_options_subtitle());
|
||||
const auto inner = box->verticalLayout();
|
||||
const auto self = show->session().user();
|
||||
const auto options = _api
|
||||
? _api->options()
|
||||
: Data::CreditTopupOptions();
|
||||
const auto amount = StarsAmount();
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
FillCreditOptions(show, inner, self, amount, [=] {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
if (const auto onstack = paid) {
|
||||
onstack();
|
||||
}
|
||||
}, nullptr, options);
|
||||
|
||||
const auto button = box->addButton(tr::lng_close(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
const auto buttonWidth = st::boxWideWidth
|
||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
};
|
||||
return crl::guard(this, [=] {
|
||||
if (_api && !_api->options().empty()) {
|
||||
_loading = false;
|
||||
show->show(Box(crl::guard(this, optionsBox)));
|
||||
} else {
|
||||
_loading = true;
|
||||
const auto user = show->session().user();
|
||||
_api = std::make_unique<Api::CreditsTopupOptions>(user);
|
||||
_api->request(
|
||||
) | rpl::start_with_error_done([=](const QString &error) {
|
||||
_loading = false;
|
||||
show->showToast(error);
|
||||
}, [=] {
|
||||
_loading = false;
|
||||
show->show(Box(crl::guard(this, optionsBox)));
|
||||
}, _lifetime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<bool> BuyStarsHandler::loadingValue() const {
|
||||
return _loading.value();
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -9,9 +9,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "settings/settings_type.h"
|
||||
|
||||
namespace Api {
|
||||
class CreditsTopupOptions;
|
||||
} // namespace Api
|
||||
|
||||
namespace Main {
|
||||
class SessionShow;
|
||||
} // namespace Main
|
||||
|
||||
namespace Settings {
|
||||
|
||||
[[nodiscard]] Type CreditsId();
|
||||
|
||||
} // namespace Settings
|
||||
class BuyStarsHandler final : public base::has_weak_ptr {
|
||||
public:
|
||||
[[nodiscard]] Fn<void()> handler(
|
||||
std::shared_ptr<::Main::SessionShow> show,
|
||||
Fn<void()> paid = nullptr);
|
||||
[[nodiscard]] rpl::producer<bool> loadingValue() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Api::CreditsTopupOptions> _api;
|
||||
rpl::variable<bool> _loading;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -569,8 +569,8 @@ void FillCreditOptions(
|
|||
if (const auto strong = weak.data()) {
|
||||
strong->window()->setFocus();
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
if (paid) {
|
||||
paid();
|
||||
if (const auto onstack = paid) {
|
||||
onstack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/ui_utility.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_credits.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
@ -54,7 +55,7 @@ void SendButton::setState(State state) {
|
|||
|| _state.starsToSend != state.starsToSend) {
|
||||
_starsToSendText.setMarkedText(
|
||||
_st.stars.style,
|
||||
Text::IconEmoji(&st::boxStarIconEmoji).append(
|
||||
Text::IconEmoji(&st::starIconEmoji).append(
|
||||
Lang::FormatCountToShort(state.starsToSend).string),
|
||||
kMarkupTextOptions);
|
||||
}
|
||||
|
|
|
@ -63,11 +63,12 @@ creditsBoxButtonLabel: FlatLabel(defaultFlatLabel) {
|
|||
}
|
||||
|
||||
starIconEmoji: IconEmoji {
|
||||
icon: icon{{ "payments/small_star", windowFg }};
|
||||
padding: margins(0px, -3px, 0px, 0px);
|
||||
icon: icon{{ "payments/premium_emoji", creditsBg1 }};
|
||||
padding: margins(4px, 1px, 4px, 0px);
|
||||
}
|
||||
starIconEmojiColored: IconEmoji(starIconEmoji) {
|
||||
useIconColor: true;
|
||||
}
|
||||
starIconSmall: icon{{ "payments/small_star", windowFg }};
|
||||
starIconSmallPadding: margins(0px, -3px, 0px, 0px);
|
||||
|
||||
creditsHistoryEntryTypeAds: icon {{ "folders/folders_channels", premiumButtonFg }};
|
||||
|
||||
|
@ -123,10 +124,17 @@ giftBoxGiftHeight: 164px;
|
|||
giftBoxGiftSmall: 108px;
|
||||
giftBoxGiftRadius: 12px;
|
||||
giftBoxGiftBadgeFont: font(10px semibold);
|
||||
giftBoxByStarsStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(10px);
|
||||
}
|
||||
giftBoxByStarsSkip: 2px;
|
||||
giftBoxByStarsStarTop: 3px;
|
||||
giftBoxPremiumIconSize: 64px;
|
||||
giftBoxPremiumIconTop: 10px;
|
||||
giftBoxPremiumTextTop: 84px;
|
||||
giftBoxPremiumTextTopByStars: 78px;
|
||||
giftBoxButtonBottom: 12px;
|
||||
giftBoxButtonBottomByStars: 18px;
|
||||
giftBoxButtonPadding: margins(8px, 4px, 8px, 4px);
|
||||
giftBoxPreviewStickerPadding: margins(10px, 12px, 10px, 16px);
|
||||
giftBoxPreviewTitlePadding: margins(12px, 4px, 12px, 4px);
|
||||
|
@ -135,6 +143,7 @@ giftBoxButtonMargin: margins(12px, 8px, 12px, 12px);
|
|||
giftBoxStickerTop: 0px;
|
||||
giftBoxStickerStarTop: 24px;
|
||||
giftBoxSmallStickerTop: 16px;
|
||||
giftBoxStickerTopByStars: -4px;
|
||||
giftBoxStickerSize: size(80px, 80px);
|
||||
giftBoxUserpicSize: 24px;
|
||||
giftBoxUserpicSkip: 2px;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit da0d634ec373d8ba0b8f66f3381a87e43edffdf2
|
||||
Subproject commit 76f25481470faee2e0a24aa7be8c6d58564addea
|
Loading…
Add table
Reference in a new issue