mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Allow sending premium / star gifts.
This commit is contained in:
parent
71deef61f5
commit
0d0d0ab994
13 changed files with 407 additions and 109 deletions
|
@ -1853,11 +1853,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_proximity_distance_km#other" = "{count} km";
|
"lng_action_proximity_distance_km#other" = "{count} km";
|
||||||
"lng_action_webview_data_done" = "Data from the \"{text}\" button was transferred to the bot.";
|
"lng_action_webview_data_done" = "Data from the \"{text}\" button was transferred to the bot.";
|
||||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
"lng_action_gift_sent" = "You sent a gift for {cost}";
|
||||||
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
|
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
|
||||||
"lng_action_gift_anonymous" = "Anonymous Gift";
|
"lng_action_gift_for_stars#one" = "{count} Star";
|
||||||
|
"lng_action_gift_for_stars#other" = "{count} Stars";
|
||||||
"lng_action_gift_got_subtitle" = "Gift from {user}";
|
"lng_action_gift_got_subtitle" = "Gift from {user}";
|
||||||
"lng_action_gift_got_stars_text" = "Display this gift on your page or convert it to {cost}.";
|
"lng_action_gift_got_stars_text#one" = "Display this gift on your page or convert it to **{count}** Star.";
|
||||||
|
"lng_action_gift_got_stars_text#other" = "Display this gift on your page or convert it to **{count}** Stars.";
|
||||||
|
"lng_action_gift_sent_subtitle" = "Gift for {user}";
|
||||||
|
"lng_action_gift_sent_text#one" = "{user} can display this gift on their page or convert it to {count} Star.";
|
||||||
|
"lng_action_gift_sent_text#other" = "{user} can display this gift on their page or convert it to {count} Stars.";
|
||||||
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
|
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
|
||||||
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
|
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
|
||||||
"lng_action_suggested_photo_button" = "View Photo";
|
"lng_action_suggested_photo_button" = "View Photo";
|
||||||
|
|
|
@ -7,12 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/gift_credits_box.h"
|
#include "boxes/gift_credits_box.h"
|
||||||
|
|
||||||
|
#include "base/random.h"
|
||||||
#include "api/api_credits.h"
|
#include "api/api_credits.h"
|
||||||
#include "api/api_premium.h"
|
#include "api/api_premium.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "boxes/send_credits_box.h"
|
#include "boxes/send_credits_box.h"
|
||||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
|
#include "core/local_url_handlers.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
@ -27,6 +30,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/session/session_show.h"
|
#include "main/session/session_show.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "payments/payments_form.h"
|
||||||
|
#include "payments/payments_checkout_process.h"
|
||||||
|
#include "payments/payments_non_panel_process.h"
|
||||||
#include "settings/settings_credits_graphics.h"
|
#include "settings/settings_credits_graphics.h"
|
||||||
#include "settings/settings_credits.h"
|
#include "settings/settings_credits.h"
|
||||||
#include "settings/settings_premium.h"
|
#include "settings/settings_premium.h"
|
||||||
|
@ -38,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/premium_stars_colored.h"
|
#include "ui/effects/premium_stars_colored.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/basic_click_handlers.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
|
@ -83,6 +90,11 @@ struct GiftTypePremium {
|
||||||
const GiftTypePremium &) = default;
|
const GiftTypePremium &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PremiumGiftsDescriptor {
|
||||||
|
std::vector<GiftTypePremium> list;
|
||||||
|
std::shared_ptr<Api::PremiumGiftCodeOptions> api;
|
||||||
|
};
|
||||||
|
|
||||||
struct GiftTypeStars {
|
struct GiftTypeStars {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
int64 stars = 0;
|
int64 stars = 0;
|
||||||
|
@ -102,6 +114,11 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
|
||||||
const GiftDescriptor&) = default;
|
const GiftDescriptor&) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GiftsDescriptor {
|
||||||
|
std::vector<GiftDescriptor> list;
|
||||||
|
std::shared_ptr<Api::PremiumGiftCodeOptions> api;
|
||||||
|
};
|
||||||
|
|
||||||
struct GiftDetails {
|
struct GiftDetails {
|
||||||
GiftDescriptor descriptor;
|
GiftDescriptor descriptor;
|
||||||
QString text;
|
QString text;
|
||||||
|
@ -174,6 +191,8 @@ auto GenerateGiftMedia(
|
||||||
Element *replacing,
|
Element *replacing,
|
||||||
const GiftDetails &data)
|
const GiftDetails &data)
|
||||||
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||||
|
Expects(v::is<GiftTypeStars>(data.descriptor));
|
||||||
|
|
||||||
return [=](Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
|
return [=](Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
|
||||||
const auto &descriptor = data.descriptor;
|
const auto &descriptor = data.descriptor;
|
||||||
auto pushText = [&](
|
auto pushText = [&](
|
||||||
|
@ -212,27 +231,15 @@ auto GenerateGiftMedia(
|
||||||
replacing,
|
replacing,
|
||||||
sticker,
|
sticker,
|
||||||
st::giftBoxPreviewStickerPadding));
|
st::giftBoxPreviewStickerPadding));
|
||||||
const auto title = data.anonymous
|
const auto title = tr::lng_action_gift_got_subtitle(
|
||||||
? tr::lng_action_gift_anonymous(tr::now)
|
tr::now,
|
||||||
: tr::lng_action_gift_got_subtitle(
|
lt_user,
|
||||||
tr::now,
|
parent->data()->history()->session().user()->shortName());
|
||||||
lt_user,
|
auto textFallback = tr::lng_action_gift_got_stars_text(
|
||||||
parent->data()->history()->session().user()->shortName());
|
tr::now,
|
||||||
auto textFallback = v::match(descriptor, [&](GiftTypePremium data) {
|
lt_count,
|
||||||
return TextWithEntities{
|
v::get<GiftTypeStars>(descriptor).stars,
|
||||||
u"Use all those premium features with joy!"_q
|
Ui::Text::RichLangValue);
|
||||||
};
|
|
||||||
}, [&](GiftTypeStars data) {
|
|
||||||
return tr::lng_action_gift_got_stars_text(
|
|
||||||
tr::now,
|
|
||||||
lt_cost,
|
|
||||||
tr::lng_gift_stars_title(
|
|
||||||
tr::now,
|
|
||||||
lt_count,
|
|
||||||
data.stars,
|
|
||||||
Ui::Text::Bold),
|
|
||||||
Ui::Text::WithEntities);
|
|
||||||
});
|
|
||||||
auto description = data.text.isEmpty()
|
auto description = data.text.isEmpty()
|
||||||
? std::move(textFallback)
|
? std::move(textFallback)
|
||||||
: TextWithEntities{ data.text };
|
: TextWithEntities{ data.text };
|
||||||
|
@ -284,14 +291,12 @@ void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
|
||||||
}, [&](GiftTypeStars data) {
|
}, [&](GiftTypeStars data) {
|
||||||
return tr::lng_gift_stars_title(tr::now, lt_count, data.stars);
|
return tr::lng_gift_stars_title(tr::now, lt_count, data.stars);
|
||||||
});
|
});
|
||||||
const auto text = details.anonymous
|
const auto text = tr::lng_action_gift_received(
|
||||||
? tr::lng_action_gift_received_anonymous(tr::now, lt_cost, cost)
|
tr::now,
|
||||||
: tr::lng_action_gift_received(
|
lt_user,
|
||||||
tr::now,
|
_history->session().user()->shortName(),
|
||||||
lt_user,
|
lt_cost,
|
||||||
_history->session().user()->shortName(),
|
cost);
|
||||||
lt_cost,
|
|
||||||
cost);
|
|
||||||
const auto item = _history->makeMessage({
|
const auto item = _history->makeMessage({
|
||||||
.id = _history->nextNonHistoryEntryId(),
|
.id = _history->nextNonHistoryEntryId(),
|
||||||
.flags = (MessageFlag::FakeAboutView
|
.flags = (MessageFlag::FakeAboutView
|
||||||
|
@ -355,11 +360,11 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
_item->draw(p, context);
|
_item->draw(p, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<std::vector<GiftTypePremium>> GiftsPremium(
|
[[nodiscard]] rpl::producer<PremiumGiftsDescriptor> GiftsPremium(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
not_null<PeerData*> peer) {
|
not_null<PeerData*> peer) {
|
||||||
struct Session {
|
struct Session {
|
||||||
std::vector<GiftTypePremium> last;
|
PremiumGiftsDescriptor last;
|
||||||
};
|
};
|
||||||
static auto Map = base::flat_map<not_null<Main::Session*>, Session>();
|
static auto Map = base::flat_map<not_null<Main::Session*>, Session>();
|
||||||
return [=](auto consumer) {
|
return [=](auto consumer) {
|
||||||
|
@ -370,12 +375,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
i = Map.emplace(session, Session()).first;
|
i = Map.emplace(session, Session()).first;
|
||||||
session->lifetime().add([=] { Map.remove(session); });
|
session->lifetime().add([=] { Map.remove(session); });
|
||||||
}
|
}
|
||||||
if (!i->second.last.empty()) {
|
if (!i->second.last.list.empty()) {
|
||||||
consumer.put_next_copy(i->second.last);
|
consumer.put_next_copy(i->second.last);
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Api;
|
using namespace Api;
|
||||||
const auto api = lifetime.make_state<PremiumGiftCodeOptions>(peer);
|
const auto api = std::make_shared<PremiumGiftCodeOptions>(peer);
|
||||||
api->request() | rpl::start_with_error_done([=](QString error) {
|
api->request() | rpl::start_with_error_done([=](QString error) {
|
||||||
consumer.put_next({});
|
consumer.put_next({});
|
||||||
}, [=] {
|
}, [=] {
|
||||||
|
@ -411,9 +416,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
ranges::sort(list, ranges::less(), &GiftTypePremium::months);
|
ranges::sort(list, ranges::less(), &GiftTypePremium::months);
|
||||||
auto &map = Map[session];
|
auto &map = Map[session];
|
||||||
if (map.last != list) {
|
if (map.last.list != list) {
|
||||||
map.last = list;
|
map.last = PremiumGiftsDescriptor{
|
||||||
consumer.put_next_copy(list);
|
std::move(list),
|
||||||
|
api,
|
||||||
|
};
|
||||||
|
consumer.put_next_copy(map.last);
|
||||||
}
|
}
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
|
|
||||||
|
@ -923,6 +931,9 @@ void SendGiftBox(
|
||||||
not_null<Window::SessionController*> window,
|
not_null<Window::SessionController*> window,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const GiftDescriptor &descriptor) {
|
const GiftDescriptor &descriptor) {
|
||||||
|
Expects(v::is<GiftTypeStars>(descriptor));
|
||||||
|
|
||||||
|
const auto gift = v::get<GiftTypeStars>(descriptor);
|
||||||
box->setStyle(st::giftBox);
|
box->setStyle(st::giftBox);
|
||||||
box->setWidth(st::boxWideWidth);
|
box->setWidth(st::boxWideWidth);
|
||||||
box->setTitle(tr::lng_gift_send_title());
|
box->setTitle(tr::lng_gift_send_title());
|
||||||
|
@ -949,21 +960,11 @@ void SendGiftBox(
|
||||||
Lang::FormatCountDecimal(std::abs(data.stars)));
|
Lang::FormatCountDecimal(std::abs(data.stars)));
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
const auto button = box->addButton(rpl::single(QString()), [=] {
|
|
||||||
box->closeBox();
|
|
||||||
});
|
|
||||||
SetButtonMarkedLabel(
|
|
||||||
button,
|
|
||||||
tr::lng_gift_send_button(
|
|
||||||
lt_cost,
|
|
||||||
std::move(cost),
|
|
||||||
Ui::Text::WithEntities),
|
|
||||||
session,
|
|
||||||
st::creditsBoxButtonLabel,
|
|
||||||
st::giftBox.button.textFg->c);
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<GiftDetails> details;
|
rpl::variable<GiftDetails> details;
|
||||||
|
uint64 randomId = 0;
|
||||||
|
bool submitting = false;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
state->details = GiftDetails{
|
state->details = GiftDetails{
|
||||||
|
@ -1010,6 +1011,33 @@ void SendGiftBox(
|
||||||
const auto buttonWidth = st::boxWideWidth
|
const auto buttonWidth = st::boxWideWidth
|
||||||
- st::giftBox.buttonPadding.left()
|
- st::giftBox.buttonPadding.left()
|
||||||
- st::giftBox.buttonPadding.right();
|
- st::giftBox.buttonPadding.right();
|
||||||
|
const auto button = box->addButton(rpl::single(QString()), [=] {
|
||||||
|
if (state->submitting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->submitting = true;
|
||||||
|
state->randomId = base::RandomValue<uint64>();
|
||||||
|
const auto details = state->details.current();
|
||||||
|
const auto done = [=](Payments::CheckoutResult result) {
|
||||||
|
box->closeBox();
|
||||||
|
};
|
||||||
|
Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{
|
||||||
|
.giftId = gift.id,
|
||||||
|
.randomId = state->randomId,
|
||||||
|
.message = { details.text },
|
||||||
|
.user = peer->asUser(),
|
||||||
|
.anonymous = details.anonymous,
|
||||||
|
}, done, Payments::ProcessNonPanelPaymentFormFactory(window, done));
|
||||||
|
});
|
||||||
|
SetButtonMarkedLabel(
|
||||||
|
button,
|
||||||
|
tr::lng_gift_send_button(
|
||||||
|
lt_cost,
|
||||||
|
std::move(cost),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
|
session,
|
||||||
|
st::creditsBoxButtonLabel,
|
||||||
|
st::giftBox.button.textFg->c);
|
||||||
button->resizeToWidth(buttonWidth);
|
button->resizeToWidth(buttonWidth);
|
||||||
button->widthValue() | rpl::start_with_next([=](int width) {
|
button->widthValue() | rpl::start_with_next([=](int width) {
|
||||||
if (width != buttonWidth) {
|
if (width != buttonWidth) {
|
||||||
|
@ -1018,10 +1046,23 @@ void SendGiftBox(
|
||||||
}, button->lifetime());
|
}, button->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendPremiumGift(
|
||||||
|
not_null<Window::SessionController*> window,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
|
||||||
|
const GiftTypePremium &gift,
|
||||||
|
Fn<void(Payments::CheckoutResult)> done) {
|
||||||
|
auto invoice = api->invoice(1, gift.months);
|
||||||
|
invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{
|
||||||
|
{ peer->asUser() }
|
||||||
|
};
|
||||||
|
Payments::CheckoutProcess::Start(std::move(invoice), done);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
|
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
|
||||||
not_null<Window::SessionController*> window,
|
not_null<Window::SessionController*> window,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
rpl::producer<std::vector<GiftDescriptor>> gifts) {
|
rpl::producer<GiftsDescriptor> gifts) {
|
||||||
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
||||||
const auto raw = result.data();
|
const auto raw = result.data();
|
||||||
|
|
||||||
|
@ -1062,7 +1103,7 @@ void SendGiftBox(
|
||||||
return _bg;
|
return _bg;
|
||||||
}
|
}
|
||||||
DocumentData *lookupSticker(
|
DocumentData *lookupSticker(
|
||||||
const GiftDescriptor &descriptor) {
|
const GiftDescriptor &descriptor) override {
|
||||||
const auto &session = _window->session();
|
const auto &session = _window->session();
|
||||||
auto &packs = session.giftBoxStickersPacks();
|
auto &packs = session.giftBoxStickersPacks();
|
||||||
packs.load();
|
packs.load();
|
||||||
|
@ -1086,6 +1127,7 @@ void SendGiftBox(
|
||||||
struct State {
|
struct State {
|
||||||
Delegate delegate;
|
Delegate delegate;
|
||||||
std::vector<std::unique_ptr<GiftButton>> buttons;
|
std::vector<std::unique_ptr<GiftButton>> buttons;
|
||||||
|
bool sending = false;
|
||||||
};
|
};
|
||||||
const auto state = raw->lifetime().make_state<State>(State{
|
const auto state = raw->lifetime().make_state<State>(State{
|
||||||
.delegate = Delegate(window, peer),
|
.delegate = Delegate(window, peer),
|
||||||
|
@ -1136,23 +1178,24 @@ void SendGiftBox(
|
||||||
state->delegate.setBackground(std::move(bg));
|
state->delegate.setBackground(std::move(bg));
|
||||||
std::move(
|
std::move(
|
||||||
gifts
|
gifts
|
||||||
) | rpl::start_with_next([=](const std::vector<GiftDescriptor> &gifts) {
|
) | rpl::start_with_next([=](const GiftsDescriptor &gifts) {
|
||||||
const auto width = st::boxWideWidth;
|
const auto width = st::boxWideWidth;
|
||||||
const auto padding = st::giftBoxPadding;
|
const auto padding = st::giftBoxPadding;
|
||||||
const auto available = width - padding.left() - padding.right();
|
const auto available = width - padding.left() - padding.right();
|
||||||
|
|
||||||
auto x = padding.left();
|
auto x = padding.left();
|
||||||
auto y = padding.top();
|
auto y = padding.top();
|
||||||
state->buttons.resize(gifts.size());
|
state->buttons.resize(gifts.list.size());
|
||||||
for (auto &button : state->buttons) {
|
for (auto &button : state->buttons) {
|
||||||
if (!button) {
|
if (!button) {
|
||||||
button = std::make_unique<GiftButton>(raw, &state->delegate);
|
button = std::make_unique<GiftButton>(raw, &state->delegate);
|
||||||
button->show();
|
button->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto i = 0, count = int(gifts.size()); i != count; ++i) {
|
const auto api = gifts.api;
|
||||||
|
for (auto i = 0, count = int(gifts.list.size()); i != count; ++i) {
|
||||||
const auto button = state->buttons[i].get();
|
const auto button = state->buttons[i].get();
|
||||||
const auto &descriptor = gifts[i];
|
const auto &descriptor = gifts.list[i];
|
||||||
button->setDescriptor(descriptor);
|
button->setDescriptor(descriptor);
|
||||||
|
|
||||||
const auto last = !((i + 1) % kGiftsPerRow);
|
const auto last = !((i + 1) % kGiftsPerRow);
|
||||||
|
@ -1167,16 +1210,36 @@ void SendGiftBox(
|
||||||
x += single.width() + st::giftBoxGiftSkip.x();
|
x += single.width() + st::giftBoxGiftSkip.x();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto premiumSent = [=](Payments::CheckoutResult result) {
|
||||||
|
state->sending = false;
|
||||||
|
if (result == Payments::CheckoutResult::Paid) {
|
||||||
|
window->showToast(u"Sent!"_q);
|
||||||
|
}
|
||||||
|
};
|
||||||
button->setClickedCallback([=] {
|
button->setClickedCallback([=] {
|
||||||
window->show(Box(SendGiftBox, window, peer, descriptor));
|
if (v::is<GiftTypePremium>(descriptor)) {
|
||||||
|
if (state->sending) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
state->sending = true;
|
||||||
|
}
|
||||||
|
SendPremiumGift(
|
||||||
|
window,
|
||||||
|
peer,
|
||||||
|
api,
|
||||||
|
v::get<GiftTypePremium>(descriptor),
|
||||||
|
premiumSent);
|
||||||
|
} else {
|
||||||
|
window->show(Box(SendGiftBox, window, peer, descriptor));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (gifts.size() % kGiftsPerRow) {
|
if (gifts.list.size() % kGiftsPerRow) {
|
||||||
y += padding.bottom() + single.height();
|
y += padding.bottom() + single.height();
|
||||||
} else {
|
} else {
|
||||||
y += padding.bottom() - st::giftBoxGiftSkip.y();
|
y += padding.bottom() - st::giftBoxGiftSkip.y();
|
||||||
}
|
}
|
||||||
raw->resize(raw->width(), gifts.empty() ? 0 : y);
|
raw->resize(raw->width(), gifts.list.empty() ? 0 : y);
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1228,16 +1291,20 @@ void AddBlock(
|
||||||
not_null<Window::SessionController*> window,
|
not_null<Window::SessionController*> window,
|
||||||
not_null<PeerData*> peer) {
|
not_null<PeerData*> peer) {
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<std::vector<GiftDescriptor>> gifts;
|
rpl::variable<PremiumGiftsDescriptor> gifts;
|
||||||
};
|
};
|
||||||
auto state = std::make_unique<State>();
|
auto state = std::make_unique<State>();
|
||||||
|
|
||||||
state->gifts = GiftsPremium(&window->session(), peer) | rpl::map([=](
|
state->gifts = GiftsPremium(&window->session(), peer);
|
||||||
const std::vector<GiftTypePremium> &gifts) {
|
;
|
||||||
return gifts | ranges::to<std::vector<GiftDescriptor>>;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto result = MakeGiftsList(window, peer, state->gifts.value());
|
auto result = MakeGiftsList(window, peer, state->gifts.value(
|
||||||
|
) | rpl::map([=](const PremiumGiftsDescriptor &gifts) {
|
||||||
|
return GiftsDescriptor{
|
||||||
|
gifts.list | ranges::to<std::vector<GiftDescriptor>>,
|
||||||
|
gifts.api,
|
||||||
|
};
|
||||||
|
}));
|
||||||
result->lifetime().add([state = std::move(state)] {});
|
result->lifetime().add([state = std::move(state)] {});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1267,7 +1334,9 @@ void AddBlock(
|
||||||
? (!gift.limited)
|
? (!gift.limited)
|
||||||
: (price && gift.stars != price);
|
: (price && gift.stars != price);
|
||||||
}), end(gifts));
|
}), end(gifts));
|
||||||
return gifts | ranges::to<std::vector<GiftDescriptor>>();
|
return GiftsDescriptor{
|
||||||
|
gifts | ranges::to<std::vector<GiftDescriptor>>(),
|
||||||
|
};
|
||||||
})));
|
})));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -440,4 +440,22 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
|
||||||
}, st, textFg);
|
}, st, textFg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendStarGift(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
std::shared_ptr<Payments::CreditsFormData> data,
|
||||||
|
Fn<void(std::optional<QString>)> done) {
|
||||||
|
session->api().request(MTPpayments_SendStarsForm(
|
||||||
|
MTP_long(data->formId),
|
||||||
|
data->inputInvoice
|
||||||
|
)).done([=](const MTPpayments_PaymentResult &result) {
|
||||||
|
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||||
|
session->api().applyUpdates(data.vupdates());
|
||||||
|
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
|
||||||
|
});
|
||||||
|
done(std::nullopt);
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
done(error.type());
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -52,4 +52,9 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
|
||||||
const style::FlatLabel &st,
|
const style::FlatLabel &st,
|
||||||
std::optional<QColor> textFg = {});
|
std::optional<QColor> textFg = {});
|
||||||
|
|
||||||
|
void SendStarGift(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
std::shared_ptr<Payments::CreditsFormData> data,
|
||||||
|
Fn<void(std::optional<QString>)> done);
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -130,16 +130,24 @@ struct GiveawayResults {
|
||||||
enum class GiftType : uchar {
|
enum class GiftType : uchar {
|
||||||
Premium, // count - months
|
Premium, // count - months
|
||||||
Credits, // count - credits
|
Credits, // count - credits
|
||||||
|
StarGift, // count - stars
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GiftCode {
|
struct GiftCode {
|
||||||
QString slug;
|
QString slug;
|
||||||
|
DocumentData *document = nullptr;
|
||||||
|
TextWithEntities message;
|
||||||
ChannelData *channel = nullptr;
|
ChannelData *channel = nullptr;
|
||||||
|
MsgId giveawayMsgId = 0;
|
||||||
|
int convertStars = 0;
|
||||||
|
int limitedCount = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int giveawayMsgId = 0;
|
|
||||||
GiftType type = GiftType::Premium;
|
GiftType type = GiftType::Premium;
|
||||||
bool viaGiveaway = false;
|
bool viaGiveaway : 1 = false;
|
||||||
bool unclaimed = false;
|
bool unclaimed : 1 = false;
|
||||||
|
bool anonymous : 1 = false;
|
||||||
|
bool converted : 1 = false;
|
||||||
|
bool saved : 1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Media {
|
class Media {
|
||||||
|
|
|
@ -4917,12 +4917,15 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
||||||
Ui::Text::WithEntities);
|
Ui::Text::WithEntities);
|
||||||
} else {
|
} else {
|
||||||
result.links.push_back(peer->createOpenLink());
|
result.links.push_back(peer->createOpenLink());
|
||||||
result.text = (isSelf
|
result.text = isSelf
|
||||||
? tr::lng_action_gift_received_me
|
? tr::lng_action_gift_sent(tr::now,
|
||||||
: tr::lng_action_gift_received)(
|
lt_cost,
|
||||||
|
cost,
|
||||||
|
Ui::Text::WithEntities)
|
||||||
|
: tr::lng_action_gift_received(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_user,
|
lt_user,
|
||||||
Ui::Text::Link(peer->name(), 1), // Link 1.
|
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||||
lt_cost,
|
lt_cost,
|
||||||
cost,
|
cost,
|
||||||
Ui::Text::WithEntities);
|
Ui::Text::WithEntities);
|
||||||
|
@ -5130,18 +5133,22 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
||||||
} else {
|
} else {
|
||||||
const auto isSelf = (_from->id == _from->session().userPeerId());
|
const auto isSelf = (_from->id == _from->session().userPeerId());
|
||||||
const auto peer = isSelf ? _history->peer : _from;
|
const auto peer = isSelf ? _history->peer : _from;
|
||||||
|
const auto cost = AmountAndStarCurrency(
|
||||||
|
&_history->session(),
|
||||||
|
action.vamount().value_or_empty(),
|
||||||
|
qs(action.vcurrency().value_or_empty()));
|
||||||
result.links.push_back(peer->createOpenLink());
|
result.links.push_back(peer->createOpenLink());
|
||||||
result.text = (isSelf
|
result.text = isSelf
|
||||||
? tr::lng_action_gift_received_me
|
? tr::lng_action_gift_sent(tr::now,
|
||||||
: tr::lng_action_gift_received)(
|
lt_cost,
|
||||||
|
cost,
|
||||||
|
Ui::Text::WithEntities)
|
||||||
|
: tr::lng_action_gift_received(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_user,
|
lt_user,
|
||||||
Ui::Text::Link(peer->name(), 1), // Link 1.
|
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||||
lt_cost,
|
lt_cost,
|
||||||
AmountAndStarCurrency(
|
cost,
|
||||||
&_history->session(),
|
|
||||||
action.vamount().value_or_empty(),
|
|
||||||
qs(action.vcurrency().value_or_empty())),
|
|
||||||
Ui::Text::WithEntities);
|
Ui::Text::WithEntities);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5238,18 +5245,22 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
||||||
_history->session().giftBoxStickersPacks().load();
|
_history->session().giftBoxStickersPacks().load();
|
||||||
const auto amount = action.vamount().v;
|
const auto amount = action.vamount().v;
|
||||||
const auto currency = qs(action.vcurrency());
|
const auto currency = qs(action.vcurrency());
|
||||||
|
const auto cost = AmountAndStarCurrency(
|
||||||
|
&_history->session(),
|
||||||
|
amount,
|
||||||
|
currency);
|
||||||
result.links.push_back(peer->createOpenLink());
|
result.links.push_back(peer->createOpenLink());
|
||||||
result.text = (isSelf
|
result.text = isSelf
|
||||||
? tr::lng_action_gift_received_me
|
? tr::lng_action_gift_sent(tr::now,
|
||||||
: tr::lng_action_gift_received)(
|
lt_cost,
|
||||||
|
cost,
|
||||||
|
Ui::Text::WithEntities)
|
||||||
|
: tr::lng_action_gift_received(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_user,
|
lt_user,
|
||||||
Ui::Text::Link(peer->name(), 1), // Link 1.
|
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||||
lt_cost,
|
lt_cost,
|
||||||
AmountAndStarCurrency(
|
cost,
|
||||||
&_history->session(),
|
|
||||||
amount,
|
|
||||||
currency),
|
|
||||||
Ui::Text::WithEntities);
|
Ui::Text::WithEntities);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -5273,6 +5284,34 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
||||||
auto prepareStarGift = [&](
|
auto prepareStarGift = [&](
|
||||||
const MTPDmessageActionStarGift &action) {
|
const MTPDmessageActionStarGift &action) {
|
||||||
auto result = PreparedServiceText();
|
auto result = PreparedServiceText();
|
||||||
|
const auto isSelf = _from->isSelf();
|
||||||
|
const auto peer = isSelf ? _history->peer : _from;
|
||||||
|
const auto stars = action.vgift().data().vstars().v;
|
||||||
|
const auto cost = TextWithEntities{
|
||||||
|
tr::lng_action_gift_for_stars(tr::now, lt_count, stars),
|
||||||
|
};
|
||||||
|
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;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5428,18 +5467,35 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
||||||
.slug = qs(data.vtransaction_id()),
|
.slug = qs(data.vtransaction_id()),
|
||||||
.channel = history()->owner().channel(
|
.channel = history()->owner().channel(
|
||||||
peerToChannel(peerFromMTP(data.vboost_peer()))),
|
peerToChannel(peerFromMTP(data.vboost_peer()))),
|
||||||
.count = int(data.vstars().v),
|
|
||||||
.giveawayMsgId = data.vgiveaway_msg_id().v,
|
.giveawayMsgId = data.vgiveaway_msg_id().v,
|
||||||
|
.count = int(data.vstars().v),
|
||||||
.type = Data::GiftType::Credits,
|
.type = Data::GiftType::Credits,
|
||||||
.viaGiveaway = true,
|
.viaGiveaway = true,
|
||||||
.unclaimed = data.is_unclaimed(),
|
.unclaimed = data.is_unclaimed(),
|
||||||
});
|
});
|
||||||
}, [&](const MTPDmessageActionStarGift &data) {
|
}, [&](const MTPDmessageActionStarGift &data) {
|
||||||
_media = std::make_unique<Data::MediaGiftBox>(
|
const auto &gift = data.vgift().data();
|
||||||
this,
|
const auto document = history()->owner().processDocument(
|
||||||
_from,
|
gift.vsticker());
|
||||||
Data::GiftType::Credits,
|
using Fields = Data::GiftCode;
|
||||||
data.vstars_amount().v);
|
_media = std::make_unique<Data::MediaGiftBox>(this, _from, Fields{
|
||||||
|
.document = document->sticker() ? document.get() : nullptr,
|
||||||
|
.message = (data.vmessage()
|
||||||
|
? TextWithEntities{
|
||||||
|
.text = qs(data.vmessage()->data().vtext()),
|
||||||
|
.entities = Api::EntitiesFromMTP(
|
||||||
|
&history()->session(),
|
||||||
|
data.vmessage()->data().ventities().v),
|
||||||
|
}
|
||||||
|
: TextWithEntities()),
|
||||||
|
.convertStars = int(data.vconvert_stars().v),
|
||||||
|
.limitedCount = gift.vavailability_total().value_or_empty(),
|
||||||
|
.count = int(gift.vstars().v),
|
||||||
|
.type = Data::GiftType::StarGift,
|
||||||
|
.anonymous = data.is_name_hidden(),
|
||||||
|
.converted = data.is_converted(),
|
||||||
|
.saved = data.is_saved(),
|
||||||
|
});
|
||||||
}, [](const auto &) {
|
}, [](const auto &) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,14 @@ QSize PremiumGift::size() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PremiumGift::title() {
|
QString PremiumGift::title() {
|
||||||
if (creditsPrize()) {
|
if (starGift()) {
|
||||||
|
return (outgoingGift()
|
||||||
|
? tr::lng_action_gift_sent_subtitle
|
||||||
|
: tr::lng_action_gift_got_subtitle)(
|
||||||
|
tr::now,
|
||||||
|
lt_user,
|
||||||
|
_parent->history()->peer->shortName());
|
||||||
|
} else if (creditsPrize()) {
|
||||||
return tr::lng_prize_title(tr::now);
|
return tr::lng_prize_title(tr::now);
|
||||||
} else if (const auto count = credits()) {
|
} else if (const auto count = credits()) {
|
||||||
return tr::lng_gift_stars_title(tr::now, lt_count, count);
|
return tr::lng_gift_stars_title(tr::now, lt_count, count);
|
||||||
|
@ -65,6 +72,23 @@ QString PremiumGift::title() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities PremiumGift::subtitle() {
|
TextWithEntities PremiumGift::subtitle() {
|
||||||
|
if (starGift()) {
|
||||||
|
return !_data.message.empty()
|
||||||
|
? _data.message
|
||||||
|
: outgoingGift()
|
||||||
|
? tr::lng_action_gift_sent_text(
|
||||||
|
tr::now,
|
||||||
|
lt_count,
|
||||||
|
_data.count,
|
||||||
|
lt_user,
|
||||||
|
Ui::Text::Bold(_parent->history()->peer->shortName()),
|
||||||
|
Ui::Text::RichLangValue)
|
||||||
|
: tr::lng_action_gift_got_stars_text(
|
||||||
|
tr::now,
|
||||||
|
lt_count,
|
||||||
|
_data.count,
|
||||||
|
Ui::Text::RichLangValue);
|
||||||
|
}
|
||||||
const auto isCreditsPrize = creditsPrize();
|
const auto isCreditsPrize = creditsPrize();
|
||||||
if (const auto count = credits(); count && !isCreditsPrize) {
|
if (const auto count = credits(); count && !isCreditsPrize) {
|
||||||
return outgoingGift()
|
return outgoingGift()
|
||||||
|
@ -111,7 +135,9 @@ TextWithEntities PremiumGift::subtitle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QString> PremiumGift::button() {
|
rpl::producer<QString> PremiumGift::button() {
|
||||||
return creditsPrize()
|
return (starGift() && outgoingGift())
|
||||||
|
? nullptr
|
||||||
|
: creditsPrize()
|
||||||
? tr::lng_view_button_giftcode()
|
? tr::lng_view_button_giftcode()
|
||||||
: (gift() && (outgoingGift() || !_data.unclaimed))
|
: (gift() && (outgoingGift() || !_data.unclaimed))
|
||||||
? tr::lng_sticker_premium_view()
|
? tr::lng_sticker_premium_view()
|
||||||
|
@ -119,6 +145,9 @@ rpl::producer<QString> PremiumGift::button() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickHandlerPtr PremiumGift::createViewLink() {
|
ClickHandlerPtr PremiumGift::createViewLink() {
|
||||||
|
if (starGift() && outgoingGift()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
const auto from = _gift->from();
|
const auto from = _gift->from();
|
||||||
const auto peer = _parent->history()->peer;
|
const auto peer = _parent->history()->peer;
|
||||||
const auto date = _parent->data()->date();
|
const auto date = _parent->data()->date();
|
||||||
|
@ -142,7 +171,7 @@ ClickHandlerPtr PremiumGift::createViewLink() {
|
||||||
.barePeerId = data.channel
|
.barePeerId = data.channel
|
||||||
? data.channel->id.value
|
? data.channel->id.value
|
||||||
: 0,
|
: 0,
|
||||||
.bareGiveawayMsgId = uint64(data.giveawayMsgId),
|
.bareGiveawayMsgId = uint64(data.giveawayMsgId.bare),
|
||||||
.peerType = Type::Peer,
|
.peerType = Type::Peer,
|
||||||
.in = true,
|
.in = true,
|
||||||
},
|
},
|
||||||
|
@ -223,6 +252,10 @@ bool PremiumGift::gift() const {
|
||||||
return _data.slug.isEmpty() || !_data.channel;
|
return _data.slug.isEmpty() || !_data.channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PremiumGift::starGift() const {
|
||||||
|
return _data.type == Data::GiftType::StarGift;
|
||||||
|
}
|
||||||
|
|
||||||
bool PremiumGift::creditsPrize() const {
|
bool PremiumGift::creditsPrize() const {
|
||||||
return _data.viaGiveaway
|
return _data.viaGiveaway
|
||||||
&& (_data.type == Data::GiftType::Credits)
|
&& (_data.type == Data::GiftType::Credits)
|
||||||
|
@ -236,6 +269,14 @@ int PremiumGift::credits() const {
|
||||||
void PremiumGift::ensureStickerCreated() const {
|
void PremiumGift::ensureStickerCreated() const {
|
||||||
if (_sticker) {
|
if (_sticker) {
|
||||||
return;
|
return;
|
||||||
|
} else if (const auto document = _data.document) {
|
||||||
|
if (const auto sticker = document->sticker()) {
|
||||||
|
const auto skipPremiumEffect = false;
|
||||||
|
_sticker.emplace(_parent, document, skipPremiumEffect, _parent);
|
||||||
|
_sticker->setDiceIndex(sticker->alt, 1);
|
||||||
|
_sticker->initSize(st::msgServiceGiftBoxStickerSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const auto &session = _parent->history()->session();
|
const auto &session = _parent->history()->session();
|
||||||
auto &packs = session.giftBoxStickersPacks();
|
auto &packs = session.giftBoxStickersPacks();
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] bool incomingGift() const;
|
[[nodiscard]] bool incomingGift() const;
|
||||||
[[nodiscard]] bool outgoingGift() const;
|
[[nodiscard]] bool outgoingGift() const;
|
||||||
|
[[nodiscard]] bool starGift() const;
|
||||||
[[nodiscard]] bool gift() const;
|
[[nodiscard]] bool gift() const;
|
||||||
[[nodiscard]] bool creditsPrize() const;
|
[[nodiscard]] bool creditsPrize() const;
|
||||||
[[nodiscard]] int credits() const;
|
[[nodiscard]] int credits() const;
|
||||||
|
|
|
@ -181,6 +181,30 @@ void CheckoutProcess::Start(
|
||||||
j->second->requestActivate();
|
j->second->requestActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::Start(
|
||||||
|
InvoiceStarGift giftInvoice,
|
||||||
|
Fn<void(CheckoutResult)> reactivate,
|
||||||
|
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess) {
|
||||||
|
const auto randomId = giftInvoice.randomId;
|
||||||
|
auto id = InvoiceId{ std::move(giftInvoice) };
|
||||||
|
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));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processes.byRandomId.emplace(
|
||||||
|
randomId,
|
||||||
|
std::make_unique<CheckoutProcess>(
|
||||||
|
std::move(id),
|
||||||
|
Mode::Payment,
|
||||||
|
std::move(reactivate),
|
||||||
|
std::move(nonPanelPaymentFormProcess),
|
||||||
|
PrivateTag{}));
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
||||||
not_null<const HistoryItem*> item) {
|
not_null<const HistoryItem*> item) {
|
||||||
const auto session = &item->history()->session();
|
const auto session = &item->history()->session();
|
||||||
|
|
|
@ -38,6 +38,7 @@ class Form;
|
||||||
struct FormUpdate;
|
struct FormUpdate;
|
||||||
struct Error;
|
struct Error;
|
||||||
struct InvoiceCredits;
|
struct InvoiceCredits;
|
||||||
|
struct InvoiceStarGift;
|
||||||
struct InvoiceId;
|
struct InvoiceId;
|
||||||
struct InvoicePremiumGiftCode;
|
struct InvoicePremiumGiftCode;
|
||||||
struct CreditsFormData;
|
struct CreditsFormData;
|
||||||
|
@ -91,6 +92,10 @@ public:
|
||||||
static void Start(
|
static void Start(
|
||||||
InvoiceCredits creditsInvoice,
|
InvoiceCredits creditsInvoice,
|
||||||
Fn<void(CheckoutResult)> reactivate);
|
Fn<void(CheckoutResult)> reactivate);
|
||||||
|
static void Start(
|
||||||
|
InvoiceStarGift giftInvoice,
|
||||||
|
Fn<void(CheckoutResult)> reactivate,
|
||||||
|
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess);
|
||||||
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||||
not_null<const HistoryItem*> item);
|
not_null<const HistoryItem*> item);
|
||||||
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/text/text_entity.h"
|
#include "ui/text/text_entity.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "api/api_text_entities.h"
|
||||||
#include "core/core_cloud_password.h"
|
#include "core/core_cloud_password.h"
|
||||||
#include "window/themes/window_theme.h"
|
#include "window/themes/window_theme.h"
|
||||||
#include "webview/webview_interface.h"
|
#include "webview/webview_interface.h"
|
||||||
|
@ -120,6 +121,8 @@ not_null<Main::Session*> SessionFromId(const InvoiceId &id) {
|
||||||
return slug->session;
|
return slug->session;
|
||||||
} else if (const auto slug = std::get_if<InvoiceCredits>(&id.value)) {
|
} else if (const auto slug = std::get_if<InvoiceCredits>(&id.value)) {
|
||||||
return slug->session;
|
return slug->session;
|
||||||
|
} else if (const auto gift = std::get_if<InvoiceStarGift>(&id.value)) {
|
||||||
|
return &gift->user->session();
|
||||||
}
|
}
|
||||||
const auto &giftCode = v::get<InvoicePremiumGiftCode>(id.value);
|
const auto &giftCode = v::get<InvoicePremiumGiftCode>(id.value);
|
||||||
const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
|
const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
|
||||||
|
@ -376,6 +379,19 @@ MTPInputInvoice Form::inputInvoice() const {
|
||||||
MTP_long(credits->credits),
|
MTP_long(credits->credits),
|
||||||
MTP_string(credits->currency),
|
MTP_string(credits->currency),
|
||||||
MTP_long(credits->amount)));
|
MTP_long(credits->amount)));
|
||||||
|
} else if (const auto gift = std::get_if<InvoiceStarGift>(&_id.value)) {
|
||||||
|
using Flag = MTPDinputInvoiceStarGift::Flag;
|
||||||
|
return MTP_inputInvoiceStarGift(
|
||||||
|
MTP_flags((gift->anonymous ? Flag::f_hide_name : Flag(0))
|
||||||
|
| (gift->message.empty() ? Flag(0) : Flag::f_message)),
|
||||||
|
gift->user->inputUser,
|
||||||
|
MTP_long(gift->giftId),
|
||||||
|
MTP_textWithEntities(
|
||||||
|
MTP_string(gift->message.text),
|
||||||
|
Api::EntitiesToMTP(
|
||||||
|
&gift->user->session(),
|
||||||
|
gift->message.entities,
|
||||||
|
Api::ConvertOption::SkipLocal)));
|
||||||
}
|
}
|
||||||
const auto &giftCode = v::get<InvoicePremiumGiftCode>(_id.value);
|
const auto &giftCode = v::get<InvoicePremiumGiftCode>(_id.value);
|
||||||
if (giftCode.creditsAmount) {
|
if (giftCode.creditsAmount) {
|
||||||
|
@ -461,7 +477,31 @@ void Form::requestForm() {
|
||||||
};
|
};
|
||||||
_updates.fire(CreditsPaymentStarted{ .data = formData });
|
_updates.fire(CreditsPaymentStarted{ .data = formData });
|
||||||
}, [&](const MTPDpayments_paymentFormStarGift &data) {
|
}, [&](const MTPDpayments_paymentFormStarGift &data) {
|
||||||
// todo pay for star gift.
|
const auto currency = qs(data.vinvoice().data().vcurrency());
|
||||||
|
const auto &tlPrices = data.vinvoice().data().vprices().v;
|
||||||
|
const auto amount = tlPrices.empty()
|
||||||
|
? 0
|
||||||
|
: tlPrices.front().data().vamount().v;
|
||||||
|
if (currency != ::Ui::kCreditsCurrency || !amount) {
|
||||||
|
using Type = Error::Type;
|
||||||
|
_updates.fire(Error{ Type::Form, u"Bad Stars Form."_q });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto invoice = InvoiceCredits{
|
||||||
|
.session = _session,
|
||||||
|
.randomId = 0,
|
||||||
|
.credits = amount,
|
||||||
|
.currency = currency,
|
||||||
|
.amount = amount,
|
||||||
|
};
|
||||||
|
const auto formData = CreditsFormData{
|
||||||
|
.id = _id,
|
||||||
|
.formId = data.vform_id().v,
|
||||||
|
.invoice = invoice,
|
||||||
|
.inputInvoice = inputInvoice(),
|
||||||
|
.starGiftForm = true,
|
||||||
|
};
|
||||||
|
_updates.fire(CreditsPaymentStarted{ .data = formData });
|
||||||
});
|
});
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
hideProgress();
|
hideProgress();
|
||||||
|
|
|
@ -171,12 +171,21 @@ struct InvoiceCredits {
|
||||||
PeerId giftPeerId = PeerId(0);
|
PeerId giftPeerId = PeerId(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct InvoiceStarGift {
|
||||||
|
uint64 giftId = 0;
|
||||||
|
uint64 randomId = 0;
|
||||||
|
TextWithEntities message;
|
||||||
|
not_null<UserData*> user;
|
||||||
|
bool anonymous = false;
|
||||||
|
};
|
||||||
|
|
||||||
struct InvoiceId {
|
struct InvoiceId {
|
||||||
std::variant<
|
std::variant<
|
||||||
InvoiceMessage,
|
InvoiceMessage,
|
||||||
InvoiceSlug,
|
InvoiceSlug,
|
||||||
InvoicePremiumGiftCode,
|
InvoicePremiumGiftCode,
|
||||||
InvoiceCredits> value;
|
InvoiceCredits,
|
||||||
|
InvoiceStarGift> value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreditsFormData {
|
struct CreditsFormData {
|
||||||
|
@ -188,6 +197,7 @@ struct CreditsFormData {
|
||||||
PhotoData *photo = nullptr;
|
PhotoData *photo = nullptr;
|
||||||
InvoiceCredits invoice;
|
InvoiceCredits invoice;
|
||||||
MTPInputInvoice inputInvoice;
|
MTPInputInvoice inputInvoice;
|
||||||
|
bool starGiftForm = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreditsReceiptData {
|
struct CreditsReceiptData {
|
||||||
|
|
|
@ -40,10 +40,10 @@ bool IsCreditsInvoice(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessCreditsPayment(
|
void ProcessCreditsPayment(
|
||||||
std::shared_ptr<Main::SessionShow> show,
|
std::shared_ptr<Main::SessionShow> show,
|
||||||
QPointer<QWidget> fireworks,
|
QPointer<QWidget> fireworks,
|
||||||
std::shared_ptr<CreditsFormData> form,
|
std::shared_ptr<CreditsFormData> form,
|
||||||
Fn<void(CheckoutResult)> maybeReturnToBot) {
|
Fn<void(CheckoutResult)> maybeReturnToBot) {
|
||||||
const auto done = [=](Settings::SmallBalanceResult result) {
|
const auto done = [=](Settings::SmallBalanceResult result) {
|
||||||
if (result == Settings::SmallBalanceResult::Blocked) {
|
if (result == Settings::SmallBalanceResult::Blocked) {
|
||||||
if (const auto onstack = maybeReturnToBot) {
|
if (const auto onstack = maybeReturnToBot) {
|
||||||
|
@ -55,6 +55,20 @@ void ProcessCreditsPayment(
|
||||||
onstack(CheckoutResult::Cancelled);
|
onstack(CheckoutResult::Cancelled);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else if (form->starGiftForm) {
|
||||||
|
const auto done = [=](std::optional<QString> error) {
|
||||||
|
const auto onstack = maybeReturnToBot;
|
||||||
|
if (error) {
|
||||||
|
show->showToast(*error);
|
||||||
|
if (onstack) {
|
||||||
|
onstack(CheckoutResult::Failed);
|
||||||
|
}
|
||||||
|
} else if (onstack) {
|
||||||
|
onstack(CheckoutResult::Paid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ui::SendStarGift(&show->session(), form, done);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const auto unsuccessful = std::make_shared<bool>(true);
|
const auto unsuccessful = std::make_shared<bool>(true);
|
||||||
const auto box = show->show(Box(
|
const auto box = show->show(Box(
|
||||||
|
@ -65,14 +79,16 @@ void ProcessCreditsPayment(
|
||||||
if (const auto widget = fireworks.data()) {
|
if (const auto widget = fireworks.data()) {
|
||||||
Ui::StartFireworks(widget);
|
Ui::StartFireworks(widget);
|
||||||
}
|
}
|
||||||
if (maybeReturnToBot) {
|
if (const auto onstack = maybeReturnToBot) {
|
||||||
maybeReturnToBot(CheckoutResult::Paid);
|
onstack(CheckoutResult::Paid);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
box->boxClosing() | rpl::start_with_next([=] {
|
box->boxClosing() | rpl::start_with_next([=] {
|
||||||
crl::on_main([=] {
|
crl::on_main([=] {
|
||||||
if ((*unsuccessful) && maybeReturnToBot) {
|
if (*unsuccessful) {
|
||||||
maybeReturnToBot(CheckoutResult::Cancelled);
|
if (const auto onstack = maybeReturnToBot) {
|
||||||
|
onstack(CheckoutResult::Cancelled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
|
|
Loading…
Add table
Reference in a new issue