mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +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_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_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_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_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" = "{user} suggests this photo for your Telegram profile.";
|
||||
"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 "base/random.h"
|
||||
#include "api/api_credits.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/send_credits_box.h"
|
||||
#include "chat_helpers/stickers_gift_box_pack.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_session.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -27,6 +30,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "main/session/session_show.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.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/ripple_animation.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/format_values.h"
|
||||
|
@ -83,6 +90,11 @@ struct GiftTypePremium {
|
|||
const GiftTypePremium &) = default;
|
||||
};
|
||||
|
||||
struct PremiumGiftsDescriptor {
|
||||
std::vector<GiftTypePremium> list;
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api;
|
||||
};
|
||||
|
||||
struct GiftTypeStars {
|
||||
uint64 id = 0;
|
||||
int64 stars = 0;
|
||||
|
@ -102,6 +114,11 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
|
|||
const GiftDescriptor&) = default;
|
||||
};
|
||||
|
||||
struct GiftsDescriptor {
|
||||
std::vector<GiftDescriptor> list;
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api;
|
||||
};
|
||||
|
||||
struct GiftDetails {
|
||||
GiftDescriptor descriptor;
|
||||
QString text;
|
||||
|
@ -174,6 +191,8 @@ auto GenerateGiftMedia(
|
|||
Element *replacing,
|
||||
const GiftDetails &data)
|
||||
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||
Expects(v::is<GiftTypeStars>(data.descriptor));
|
||||
|
||||
return [=](Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
|
||||
const auto &descriptor = data.descriptor;
|
||||
auto pushText = [&](
|
||||
|
@ -212,27 +231,15 @@ auto GenerateGiftMedia(
|
|||
replacing,
|
||||
sticker,
|
||||
st::giftBoxPreviewStickerPadding));
|
||||
const auto title = data.anonymous
|
||||
? tr::lng_action_gift_anonymous(tr::now)
|
||||
: tr::lng_action_gift_got_subtitle(
|
||||
tr::now,
|
||||
lt_user,
|
||||
parent->data()->history()->session().user()->shortName());
|
||||
auto textFallback = v::match(descriptor, [&](GiftTypePremium data) {
|
||||
return TextWithEntities{
|
||||
u"Use all those premium features with joy!"_q
|
||||
};
|
||||
}, [&](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);
|
||||
});
|
||||
const auto title = tr::lng_action_gift_got_subtitle(
|
||||
tr::now,
|
||||
lt_user,
|
||||
parent->data()->history()->session().user()->shortName());
|
||||
auto textFallback = tr::lng_action_gift_got_stars_text(
|
||||
tr::now,
|
||||
lt_count,
|
||||
v::get<GiftTypeStars>(descriptor).stars,
|
||||
Ui::Text::RichLangValue);
|
||||
auto description = data.text.isEmpty()
|
||||
? std::move(textFallback)
|
||||
: TextWithEntities{ data.text };
|
||||
|
@ -284,14 +291,12 @@ void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
|
|||
}, [&](GiftTypeStars data) {
|
||||
return tr::lng_gift_stars_title(tr::now, lt_count, data.stars);
|
||||
});
|
||||
const auto text = details.anonymous
|
||||
? tr::lng_action_gift_received_anonymous(tr::now, lt_cost, cost)
|
||||
: tr::lng_action_gift_received(
|
||||
tr::now,
|
||||
lt_user,
|
||||
_history->session().user()->shortName(),
|
||||
lt_cost,
|
||||
cost);
|
||||
const auto text = tr::lng_action_gift_received(
|
||||
tr::now,
|
||||
lt_user,
|
||||
_history->session().user()->shortName(),
|
||||
lt_cost,
|
||||
cost);
|
||||
const auto item = _history->makeMessage({
|
||||
.id = _history->nextNonHistoryEntryId(),
|
||||
.flags = (MessageFlag::FakeAboutView
|
||||
|
@ -355,11 +360,11 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
|||
_item->draw(p, context);
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<std::vector<GiftTypePremium>> GiftsPremium(
|
||||
[[nodiscard]] rpl::producer<PremiumGiftsDescriptor> GiftsPremium(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<PeerData*> peer) {
|
||||
struct Session {
|
||||
std::vector<GiftTypePremium> last;
|
||||
PremiumGiftsDescriptor last;
|
||||
};
|
||||
static auto Map = base::flat_map<not_null<Main::Session*>, Session>();
|
||||
return [=](auto consumer) {
|
||||
|
@ -370,12 +375,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
|||
i = Map.emplace(session, Session()).first;
|
||||
session->lifetime().add([=] { Map.remove(session); });
|
||||
}
|
||||
if (!i->second.last.empty()) {
|
||||
if (!i->second.last.list.empty()) {
|
||||
consumer.put_next_copy(i->second.last);
|
||||
}
|
||||
|
||||
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) {
|
||||
consumer.put_next({});
|
||||
}, [=] {
|
||||
|
@ -411,9 +416,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
ranges::sort(list, ranges::less(), &GiftTypePremium::months);
|
||||
auto &map = Map[session];
|
||||
if (map.last != list) {
|
||||
map.last = list;
|
||||
consumer.put_next_copy(list);
|
||||
if (map.last.list != list) {
|
||||
map.last = PremiumGiftsDescriptor{
|
||||
std::move(list),
|
||||
api,
|
||||
};
|
||||
consumer.put_next_copy(map.last);
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
|
@ -923,6 +931,9 @@ void SendGiftBox(
|
|||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
const GiftDescriptor &descriptor) {
|
||||
Expects(v::is<GiftTypeStars>(descriptor));
|
||||
|
||||
const auto gift = v::get<GiftTypeStars>(descriptor);
|
||||
box->setStyle(st::giftBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setTitle(tr::lng_gift_send_title());
|
||||
|
@ -949,21 +960,11 @@ void SendGiftBox(
|
|||
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 {
|
||||
rpl::variable<GiftDetails> details;
|
||||
uint64 randomId = 0;
|
||||
bool submitting = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
state->details = GiftDetails{
|
||||
|
@ -1010,6 +1011,33 @@ void SendGiftBox(
|
|||
const auto buttonWidth = st::boxWideWidth
|
||||
- st::giftBox.buttonPadding.left()
|
||||
- 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->widthValue() | rpl::start_with_next([=](int width) {
|
||||
if (width != buttonWidth) {
|
||||
|
@ -1018,10 +1046,23 @@ void SendGiftBox(
|
|||
}, 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(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
rpl::producer<std::vector<GiftDescriptor>> gifts) {
|
||||
rpl::producer<GiftsDescriptor> gifts) {
|
||||
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
||||
const auto raw = result.data();
|
||||
|
||||
|
@ -1062,7 +1103,7 @@ void SendGiftBox(
|
|||
return _bg;
|
||||
}
|
||||
DocumentData *lookupSticker(
|
||||
const GiftDescriptor &descriptor) {
|
||||
const GiftDescriptor &descriptor) override {
|
||||
const auto &session = _window->session();
|
||||
auto &packs = session.giftBoxStickersPacks();
|
||||
packs.load();
|
||||
|
@ -1086,6 +1127,7 @@ void SendGiftBox(
|
|||
struct State {
|
||||
Delegate delegate;
|
||||
std::vector<std::unique_ptr<GiftButton>> buttons;
|
||||
bool sending = false;
|
||||
};
|
||||
const auto state = raw->lifetime().make_state<State>(State{
|
||||
.delegate = Delegate(window, peer),
|
||||
|
@ -1136,23 +1178,24 @@ void SendGiftBox(
|
|||
state->delegate.setBackground(std::move(bg));
|
||||
std::move(
|
||||
gifts
|
||||
) | rpl::start_with_next([=](const std::vector<GiftDescriptor> &gifts) {
|
||||
) | rpl::start_with_next([=](const GiftsDescriptor &gifts) {
|
||||
const auto width = st::boxWideWidth;
|
||||
const auto padding = st::giftBoxPadding;
|
||||
const auto available = width - padding.left() - padding.right();
|
||||
|
||||
auto x = padding.left();
|
||||
auto y = padding.top();
|
||||
state->buttons.resize(gifts.size());
|
||||
state->buttons.resize(gifts.list.size());
|
||||
for (auto &button : state->buttons) {
|
||||
if (!button) {
|
||||
button = std::make_unique<GiftButton>(raw, &state->delegate);
|
||||
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 &descriptor = gifts[i];
|
||||
const auto &descriptor = gifts.list[i];
|
||||
button->setDescriptor(descriptor);
|
||||
|
||||
const auto last = !((i + 1) % kGiftsPerRow);
|
||||
|
@ -1167,16 +1210,36 @@ void SendGiftBox(
|
|||
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([=] {
|
||||
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();
|
||||
} else {
|
||||
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());
|
||||
|
||||
return result;
|
||||
|
@ -1228,16 +1291,20 @@ void AddBlock(
|
|||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer) {
|
||||
struct State {
|
||||
rpl::variable<std::vector<GiftDescriptor>> gifts;
|
||||
rpl::variable<PremiumGiftsDescriptor> gifts;
|
||||
};
|
||||
auto state = std::make_unique<State>();
|
||||
|
||||
state->gifts = GiftsPremium(&window->session(), peer) | rpl::map([=](
|
||||
const std::vector<GiftTypePremium> &gifts) {
|
||||
return gifts | ranges::to<std::vector<GiftDescriptor>>;
|
||||
});
|
||||
state->gifts = GiftsPremium(&window->session(), peer);
|
||||
;
|
||||
|
||||
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)] {});
|
||||
return result;
|
||||
}
|
||||
|
@ -1267,7 +1334,9 @@ void AddBlock(
|
|||
? (!gift.limited)
|
||||
: (price && gift.stars != price);
|
||||
}), end(gifts));
|
||||
return gifts | ranges::to<std::vector<GiftDescriptor>>();
|
||||
return GiftsDescriptor{
|
||||
gifts | ranges::to<std::vector<GiftDescriptor>>(),
|
||||
};
|
||||
})));
|
||||
|
||||
return result;
|
||||
|
|
|
@ -440,4 +440,22 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
|
|||
}, 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
|
||||
|
|
|
@ -52,4 +52,9 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
|
|||
const style::FlatLabel &st,
|
||||
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
|
||||
|
|
|
@ -130,16 +130,24 @@ struct GiveawayResults {
|
|||
enum class GiftType : uchar {
|
||||
Premium, // count - months
|
||||
Credits, // count - credits
|
||||
StarGift, // count - stars
|
||||
};
|
||||
|
||||
struct GiftCode {
|
||||
QString slug;
|
||||
DocumentData *document = nullptr;
|
||||
TextWithEntities message;
|
||||
ChannelData *channel = nullptr;
|
||||
MsgId giveawayMsgId = 0;
|
||||
int convertStars = 0;
|
||||
int limitedCount = 0;
|
||||
int count = 0;
|
||||
int giveawayMsgId = 0;
|
||||
GiftType type = GiftType::Premium;
|
||||
bool viaGiveaway = false;
|
||||
bool unclaimed = false;
|
||||
bool viaGiveaway : 1 = false;
|
||||
bool unclaimed : 1 = false;
|
||||
bool anonymous : 1 = false;
|
||||
bool converted : 1 = false;
|
||||
bool saved : 1 = false;
|
||||
};
|
||||
|
||||
class Media {
|
||||
|
|
|
@ -4917,12 +4917,15 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
Ui::Text::WithEntities);
|
||||
} else {
|
||||
result.links.push_back(peer->createOpenLink());
|
||||
result.text = (isSelf
|
||||
? tr::lng_action_gift_received_me
|
||||
: tr::lng_action_gift_received)(
|
||||
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->name(), 1), // Link 1.
|
||||
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||
lt_cost,
|
||||
cost,
|
||||
Ui::Text::WithEntities);
|
||||
|
@ -5130,18 +5133,22 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
} else {
|
||||
const auto isSelf = (_from->id == _from->session().userPeerId());
|
||||
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.text = (isSelf
|
||||
? tr::lng_action_gift_received_me
|
||||
: tr::lng_action_gift_received)(
|
||||
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->name(), 1), // Link 1.
|
||||
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||
lt_cost,
|
||||
AmountAndStarCurrency(
|
||||
&_history->session(),
|
||||
action.vamount().value_or_empty(),
|
||||
qs(action.vcurrency().value_or_empty())),
|
||||
cost,
|
||||
Ui::Text::WithEntities);
|
||||
|
||||
}
|
||||
|
@ -5238,18 +5245,22 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
_history->session().giftBoxStickersPacks().load();
|
||||
const auto amount = action.vamount().v;
|
||||
const auto currency = qs(action.vcurrency());
|
||||
const auto cost = AmountAndStarCurrency(
|
||||
&_history->session(),
|
||||
amount,
|
||||
currency);
|
||||
result.links.push_back(peer->createOpenLink());
|
||||
result.text = (isSelf
|
||||
? tr::lng_action_gift_received_me
|
||||
: tr::lng_action_gift_received)(
|
||||
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->name(), 1), // Link 1.
|
||||
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||
lt_cost,
|
||||
AmountAndStarCurrency(
|
||||
&_history->session(),
|
||||
amount,
|
||||
currency),
|
||||
cost,
|
||||
Ui::Text::WithEntities);
|
||||
return result;
|
||||
};
|
||||
|
@ -5273,6 +5284,34 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
auto prepareStarGift = [&](
|
||||
const MTPDmessageActionStarGift &action) {
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -5428,18 +5467,35 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
|||
.slug = qs(data.vtransaction_id()),
|
||||
.channel = history()->owner().channel(
|
||||
peerToChannel(peerFromMTP(data.vboost_peer()))),
|
||||
.count = int(data.vstars().v),
|
||||
.giveawayMsgId = data.vgiveaway_msg_id().v,
|
||||
.count = int(data.vstars().v),
|
||||
.type = Data::GiftType::Credits,
|
||||
.viaGiveaway = true,
|
||||
.unclaimed = data.is_unclaimed(),
|
||||
});
|
||||
}, [&](const MTPDmessageActionStarGift &data) {
|
||||
_media = std::make_unique<Data::MediaGiftBox>(
|
||||
this,
|
||||
_from,
|
||||
Data::GiftType::Credits,
|
||||
data.vstars_amount().v);
|
||||
const auto &gift = data.vgift().data();
|
||||
const auto document = history()->owner().processDocument(
|
||||
gift.vsticker());
|
||||
using Fields = Data::GiftCode;
|
||||
_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 &) {
|
||||
});
|
||||
}
|
||||
|
|
|
@ -52,7 +52,14 @@ QSize PremiumGift::size() {
|
|||
}
|
||||
|
||||
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);
|
||||
} else if (const auto count = credits()) {
|
||||
return tr::lng_gift_stars_title(tr::now, lt_count, count);
|
||||
|
@ -65,6 +72,23 @@ QString PremiumGift::title() {
|
|||
}
|
||||
|
||||
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();
|
||||
if (const auto count = credits(); count && !isCreditsPrize) {
|
||||
return outgoingGift()
|
||||
|
@ -111,7 +135,9 @@ TextWithEntities PremiumGift::subtitle() {
|
|||
}
|
||||
|
||||
rpl::producer<QString> PremiumGift::button() {
|
||||
return creditsPrize()
|
||||
return (starGift() && outgoingGift())
|
||||
? nullptr
|
||||
: creditsPrize()
|
||||
? tr::lng_view_button_giftcode()
|
||||
: (gift() && (outgoingGift() || !_data.unclaimed))
|
||||
? tr::lng_sticker_premium_view()
|
||||
|
@ -119,6 +145,9 @@ rpl::producer<QString> PremiumGift::button() {
|
|||
}
|
||||
|
||||
ClickHandlerPtr PremiumGift::createViewLink() {
|
||||
if (starGift() && outgoingGift()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto from = _gift->from();
|
||||
const auto peer = _parent->history()->peer;
|
||||
const auto date = _parent->data()->date();
|
||||
|
@ -142,7 +171,7 @@ ClickHandlerPtr PremiumGift::createViewLink() {
|
|||
.barePeerId = data.channel
|
||||
? data.channel->id.value
|
||||
: 0,
|
||||
.bareGiveawayMsgId = uint64(data.giveawayMsgId),
|
||||
.bareGiveawayMsgId = uint64(data.giveawayMsgId.bare),
|
||||
.peerType = Type::Peer,
|
||||
.in = true,
|
||||
},
|
||||
|
@ -223,6 +252,10 @@ bool PremiumGift::gift() const {
|
|||
return _data.slug.isEmpty() || !_data.channel;
|
||||
}
|
||||
|
||||
bool PremiumGift::starGift() const {
|
||||
return _data.type == Data::GiftType::StarGift;
|
||||
}
|
||||
|
||||
bool PremiumGift::creditsPrize() const {
|
||||
return _data.viaGiveaway
|
||||
&& (_data.type == Data::GiftType::Credits)
|
||||
|
@ -236,6 +269,14 @@ int PremiumGift::credits() const {
|
|||
void PremiumGift::ensureStickerCreated() const {
|
||||
if (_sticker) {
|
||||
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();
|
||||
auto &packs = session.giftBoxStickersPacks();
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
private:
|
||||
[[nodiscard]] bool incomingGift() const;
|
||||
[[nodiscard]] bool outgoingGift() const;
|
||||
[[nodiscard]] bool starGift() const;
|
||||
[[nodiscard]] bool gift() const;
|
||||
[[nodiscard]] bool creditsPrize() const;
|
||||
[[nodiscard]] int credits() const;
|
||||
|
|
|
@ -181,6 +181,30 @@ void CheckoutProcess::Start(
|
|||
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(
|
||||
not_null<const HistoryItem*> item) {
|
||||
const auto session = &item->history()->session();
|
||||
|
|
|
@ -38,6 +38,7 @@ class Form;
|
|||
struct FormUpdate;
|
||||
struct Error;
|
||||
struct InvoiceCredits;
|
||||
struct InvoiceStarGift;
|
||||
struct InvoiceId;
|
||||
struct InvoicePremiumGiftCode;
|
||||
struct CreditsFormData;
|
||||
|
@ -91,6 +92,10 @@ public:
|
|||
static void Start(
|
||||
InvoiceCredits creditsInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate);
|
||||
static void Start(
|
||||
InvoiceStarGift giftInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate,
|
||||
Fn<void(NonPanelPaymentForm)> nonPanelPaymentFormProcess);
|
||||
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||
not_null<const HistoryItem*> item);
|
||||
[[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/text_entity.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "webview/webview_interface.h"
|
||||
|
@ -120,6 +121,8 @@ not_null<Main::Session*> SessionFromId(const InvoiceId &id) {
|
|||
return slug->session;
|
||||
} else if (const auto slug = std::get_if<InvoiceCredits>(&id.value)) {
|
||||
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 users = std::get_if<InvoicePremiumGiftCodeUsers>(
|
||||
|
@ -376,6 +379,19 @@ MTPInputInvoice Form::inputInvoice() const {
|
|||
MTP_long(credits->credits),
|
||||
MTP_string(credits->currency),
|
||||
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);
|
||||
if (giftCode.creditsAmount) {
|
||||
|
@ -461,7 +477,31 @@ void Form::requestForm() {
|
|||
};
|
||||
_updates.fire(CreditsPaymentStarted{ .data = formData });
|
||||
}, [&](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) {
|
||||
hideProgress();
|
||||
|
|
|
@ -171,12 +171,21 @@ struct InvoiceCredits {
|
|||
PeerId giftPeerId = PeerId(0);
|
||||
};
|
||||
|
||||
struct InvoiceStarGift {
|
||||
uint64 giftId = 0;
|
||||
uint64 randomId = 0;
|
||||
TextWithEntities message;
|
||||
not_null<UserData*> user;
|
||||
bool anonymous = false;
|
||||
};
|
||||
|
||||
struct InvoiceId {
|
||||
std::variant<
|
||||
InvoiceMessage,
|
||||
InvoiceSlug,
|
||||
InvoicePremiumGiftCode,
|
||||
InvoiceCredits> value;
|
||||
InvoiceCredits,
|
||||
InvoiceStarGift> value;
|
||||
};
|
||||
|
||||
struct CreditsFormData {
|
||||
|
@ -188,6 +197,7 @@ struct CreditsFormData {
|
|||
PhotoData *photo = nullptr;
|
||||
InvoiceCredits invoice;
|
||||
MTPInputInvoice inputInvoice;
|
||||
bool starGiftForm = false;
|
||||
};
|
||||
|
||||
struct CreditsReceiptData {
|
||||
|
|
|
@ -40,10 +40,10 @@ bool IsCreditsInvoice(not_null<HistoryItem*> item) {
|
|||
}
|
||||
|
||||
void ProcessCreditsPayment(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
QPointer<QWidget> fireworks,
|
||||
std::shared_ptr<CreditsFormData> form,
|
||||
Fn<void(CheckoutResult)> maybeReturnToBot) {
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
QPointer<QWidget> fireworks,
|
||||
std::shared_ptr<CreditsFormData> form,
|
||||
Fn<void(CheckoutResult)> maybeReturnToBot) {
|
||||
const auto done = [=](Settings::SmallBalanceResult result) {
|
||||
if (result == Settings::SmallBalanceResult::Blocked) {
|
||||
if (const auto onstack = maybeReturnToBot) {
|
||||
|
@ -55,6 +55,20 @@ void ProcessCreditsPayment(
|
|||
onstack(CheckoutResult::Cancelled);
|
||||
}
|
||||
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 box = show->show(Box(
|
||||
|
@ -65,14 +79,16 @@ void ProcessCreditsPayment(
|
|||
if (const auto widget = fireworks.data()) {
|
||||
Ui::StartFireworks(widget);
|
||||
}
|
||||
if (maybeReturnToBot) {
|
||||
maybeReturnToBot(CheckoutResult::Paid);
|
||||
if (const auto onstack = maybeReturnToBot) {
|
||||
onstack(CheckoutResult::Paid);
|
||||
}
|
||||
}));
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
crl::on_main([=] {
|
||||
if ((*unsuccessful) && maybeReturnToBot) {
|
||||
maybeReturnToBot(CheckoutResult::Cancelled);
|
||||
if (*unsuccessful) {
|
||||
if (const auto onstack = maybeReturnToBot) {
|
||||
onstack(CheckoutResult::Cancelled);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, box->lifetime());
|
||||
|
|
Loading…
Add table
Reference in a new issue