2022-07-03 14:56:12 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "boxes/gift_premium_box.h"
|
|
|
|
|
|
|
|
#include "apiwrap.h"
|
2023-09-28 18:44:22 +00:00
|
|
|
#include "api/api_premium.h"
|
2022-08-25 12:40:00 +00:00
|
|
|
#include "api/api_premium_option.h"
|
2023-09-28 18:44:22 +00:00
|
|
|
#include "base/unixtime.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "base/weak_ptr.h"
|
2023-09-28 18:44:22 +00:00
|
|
|
#include "boxes/peers/prepare_short_info_box.h"
|
2023-10-24 10:07:06 +00:00
|
|
|
#include "data/data_boosts.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "data/data_changes.h"
|
2023-10-11 09:29:02 +00:00
|
|
|
#include "data/data_channel.h"
|
|
|
|
#include "data/data_media_types.h" // Data::Giveaway
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "data/data_peer_values.h" // Data::PeerPremiumValue.
|
|
|
|
#include "data/data_session.h"
|
2022-08-25 02:14:33 +00:00
|
|
|
#include "data/data_subscription_option.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "data/data_user.h"
|
|
|
|
#include "lang/lang_keys.h"
|
|
|
|
#include "main/main_session.h"
|
2023-10-11 10:33:59 +00:00
|
|
|
#include "mainwidget.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "settings/settings_premium.h"
|
|
|
|
#include "ui/basic_click_handlers.h" // UrlClickHandler::Open.
|
2023-10-11 09:47:21 +00:00
|
|
|
#include "ui/boxes/boost_box.h" // StartFireworks.
|
2022-12-19 11:48:24 +00:00
|
|
|
#include "ui/controls/userpic_button.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "ui/effects/premium_graphics.h"
|
2022-08-23 18:01:54 +00:00
|
|
|
#include "ui/effects/premium_stars_colored.h"
|
2023-09-28 18:44:22 +00:00
|
|
|
#include "ui/effects/premium_top_bar.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "ui/layers/generic_box.h"
|
|
|
|
#include "ui/text/text_utilities.h"
|
|
|
|
#include "ui/widgets/checkbox.h"
|
|
|
|
#include "ui/widgets/gradient_round_button.h"
|
|
|
|
#include "ui/wrap/padding_wrap.h"
|
2023-09-28 18:44:22 +00:00
|
|
|
#include "ui/wrap/table_layout.h"
|
2023-10-11 10:33:59 +00:00
|
|
|
#include "window/window_peer_menu.h" // ShowChooseRecipientBox.
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "window/window_session_controller.h"
|
|
|
|
#include "styles/style_boxes.h"
|
2023-10-31 23:47:21 +00:00
|
|
|
#include "styles/style_giveaway.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "styles/style_info.h"
|
2023-11-01 01:45:18 +00:00
|
|
|
#include "styles/style_layers.h"
|
2022-07-03 14:56:12 +00:00
|
|
|
#include "styles/style_premium.h"
|
|
|
|
|
2023-09-28 18:44:22 +00:00
|
|
|
#include <QtGui/QGuiApplication>
|
|
|
|
|
2022-07-03 14:56:12 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kDiscountDivider = 5.;
|
|
|
|
|
2022-08-25 02:14:33 +00:00
|
|
|
using GiftOption = Data::SubscriptionOption;
|
|
|
|
using GiftOptions = Data::SubscriptionOptions;
|
2022-07-03 14:56:12 +00:00
|
|
|
|
2022-07-22 04:33:43 +00:00
|
|
|
GiftOptions GiftOptionFromTL(const MTPDuserFull &data) {
|
2022-07-03 14:56:12 +00:00
|
|
|
auto result = GiftOptions();
|
|
|
|
const auto gifts = data.vpremium_gifts();
|
|
|
|
if (!gifts) {
|
|
|
|
return result;
|
|
|
|
}
|
2022-08-25 12:40:00 +00:00
|
|
|
result = Api::SubscriptionOptionsFromTL(gifts->v);
|
|
|
|
for (auto &option : result) {
|
|
|
|
option.costPerMonth = tr::lng_premium_gift_per(
|
|
|
|
tr::now,
|
|
|
|
lt_cost,
|
|
|
|
option.costPerMonth);
|
2022-07-03 14:56:12 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GiftBox(
|
|
|
|
not_null<Ui::GenericBox*> box,
|
|
|
|
not_null<Window::SessionController*> controller,
|
|
|
|
not_null<UserData*> user,
|
|
|
|
GiftOptions options) {
|
|
|
|
const auto boxWidth = st::boxWideWidth;
|
|
|
|
box->setWidth(boxWidth);
|
|
|
|
box->setNoContentMargin(true);
|
|
|
|
const auto buttonsParent = box->verticalLayout().get();
|
|
|
|
|
|
|
|
struct State {
|
|
|
|
rpl::event_stream<QString> buttonText;
|
|
|
|
};
|
|
|
|
const auto state = box->lifetime().make_state<State>();
|
|
|
|
|
|
|
|
const auto userpicPadding = st::premiumGiftUserpicPadding;
|
|
|
|
const auto top = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
|
|
|
buttonsParent,
|
|
|
|
userpicPadding.top()
|
|
|
|
+ userpicPadding.bottom()
|
|
|
|
+ st::defaultUserpicButton.size.height()));
|
|
|
|
|
2022-08-23 18:01:54 +00:00
|
|
|
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
2022-07-06 23:46:18 +00:00
|
|
|
const auto stars = box->lifetime().make_state<ColoredMiniStars>(top);
|
|
|
|
|
2022-07-03 14:56:12 +00:00
|
|
|
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
|
|
|
top,
|
|
|
|
user,
|
|
|
|
st::defaultUserpicButton);
|
|
|
|
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
top->widthValue(
|
|
|
|
) | rpl::start_with_next([=](int width) {
|
|
|
|
userpic->moveToLeft(
|
|
|
|
(width - userpic->width()) / 2,
|
|
|
|
userpicPadding.top());
|
2022-07-06 23:46:18 +00:00
|
|
|
|
|
|
|
const auto center = top->rect().center();
|
|
|
|
const auto size = QSize(
|
|
|
|
userpic->width() * Ui::Premium::MiniStars::kSizeFactor,
|
|
|
|
userpic->height());
|
|
|
|
const auto ministarsRect = QRect(
|
|
|
|
QPoint(center.x() - size.width(), center.y() - size.height()),
|
|
|
|
QPoint(center.x() + size.width(), center.y() + size.height()));
|
|
|
|
stars->setPosition(ministarsRect.topLeft());
|
|
|
|
stars->setSize(ministarsRect.size());
|
2022-07-03 14:56:12 +00:00
|
|
|
}, userpic->lifetime());
|
|
|
|
|
2022-07-06 23:46:18 +00:00
|
|
|
top->paintRequest(
|
|
|
|
) | rpl::start_with_next([=](const QRect &r) {
|
2022-09-16 20:23:27 +00:00
|
|
|
auto p = QPainter(top);
|
2022-07-06 23:46:18 +00:00
|
|
|
|
|
|
|
p.fillRect(r, Qt::transparent);
|
|
|
|
stars->paint(p);
|
|
|
|
}, top->lifetime());
|
|
|
|
|
2022-07-03 14:56:12 +00:00
|
|
|
const auto close = Ui::CreateChild<Ui::IconButton>(
|
|
|
|
buttonsParent,
|
|
|
|
st::infoTopBarClose);
|
|
|
|
close->setClickedCallback([=] { box->closeBox(); });
|
|
|
|
|
|
|
|
buttonsParent->widthValue(
|
|
|
|
) | rpl::start_with_next([=](int width) {
|
|
|
|
close->moveToRight(0, 0, width);
|
|
|
|
}, close->lifetime());
|
|
|
|
|
|
|
|
// Header.
|
|
|
|
const auto &padding = st::premiumGiftAboutPadding;
|
|
|
|
const auto available = boxWidth - padding.left() - padding.right();
|
|
|
|
const auto &stTitle = st::premiumPreviewAboutTitle;
|
|
|
|
auto titleLabel = object_ptr<Ui::FlatLabel>(
|
|
|
|
box,
|
|
|
|
tr::lng_premium_gift_title(),
|
|
|
|
stTitle);
|
|
|
|
titleLabel->resizeToWidth(available);
|
|
|
|
box->addRow(
|
|
|
|
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
|
|
|
box,
|
|
|
|
std::move(titleLabel)),
|
|
|
|
st::premiumGiftTitlePadding);
|
|
|
|
|
|
|
|
auto textLabel = object_ptr<Ui::FlatLabel>(
|
|
|
|
box,
|
|
|
|
tr::lng_premium_gift_about(
|
|
|
|
lt_user,
|
|
|
|
user->session().changes().peerFlagsValue(
|
|
|
|
user,
|
|
|
|
Data::PeerUpdate::Flag::Name
|
|
|
|
) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
|
|
|
|
Ui::Text::RichLangValue),
|
|
|
|
st::premiumPreviewAbout);
|
|
|
|
textLabel->setTextColorOverride(stTitle.textFg->c);
|
|
|
|
textLabel->resizeToWidth(available);
|
|
|
|
box->addRow(
|
|
|
|
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(box, std::move(textLabel)),
|
|
|
|
padding);
|
|
|
|
|
|
|
|
// List.
|
|
|
|
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
|
2023-07-24 15:38:08 +00:00
|
|
|
const auto groupValueChangedCallback = [=](int value) {
|
2022-07-03 14:56:12 +00:00
|
|
|
Expects(value < options.size() && value >= 0);
|
|
|
|
auto text = tr::lng_premium_gift_button(
|
|
|
|
tr::now,
|
|
|
|
lt_cost,
|
2022-08-30 17:50:37 +00:00
|
|
|
options[value].costTotal);
|
2022-07-03 14:56:12 +00:00
|
|
|
state->buttonText.fire(std::move(text));
|
2023-07-24 15:38:08 +00:00
|
|
|
};
|
|
|
|
group->setChangedCallback(groupValueChangedCallback);
|
2022-08-25 17:30:10 +00:00
|
|
|
Ui::Premium::AddGiftOptions(
|
|
|
|
buttonsParent,
|
|
|
|
group,
|
|
|
|
options,
|
|
|
|
st::premiumGiftOption);
|
2022-07-03 14:56:12 +00:00
|
|
|
|
|
|
|
// Footer.
|
|
|
|
auto terms = object_ptr<Ui::FlatLabel>(
|
|
|
|
box,
|
|
|
|
tr::lng_premium_gift_terms(
|
|
|
|
lt_link,
|
|
|
|
tr::lng_premium_gift_terms_link(
|
|
|
|
) | rpl::map([=](const QString &t) {
|
|
|
|
return Ui::Text::Link(t, 1);
|
|
|
|
}),
|
|
|
|
Ui::Text::WithEntities),
|
|
|
|
st::premiumGiftTerms);
|
|
|
|
terms->setLink(1, std::make_shared<LambdaClickHandler>([=] {
|
|
|
|
box->closeBox();
|
|
|
|
Settings::ShowPremium(&user->session(), QString());
|
|
|
|
}));
|
|
|
|
terms->resizeToWidth(available);
|
|
|
|
box->addRow(
|
|
|
|
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(box, std::move(terms)),
|
|
|
|
st::premiumGiftTermsPadding);
|
|
|
|
|
|
|
|
// Button.
|
|
|
|
const auto &stButton = st::premiumGiftBox;
|
|
|
|
box->setStyle(stButton);
|
|
|
|
auto raw = Settings::CreateSubscribeButton({
|
|
|
|
controller,
|
|
|
|
box,
|
|
|
|
[] { return QString("gift"); },
|
|
|
|
state->buttonText.events(),
|
|
|
|
Ui::Premium::GiftGradientStops(),
|
2022-08-25 17:30:10 +00:00
|
|
|
[=] {
|
|
|
|
const auto value = group->value();
|
|
|
|
return (value < options.size() && value >= 0)
|
|
|
|
? options[value].botUrl
|
|
|
|
: QString();
|
|
|
|
},
|
2022-07-03 14:56:12 +00:00
|
|
|
});
|
|
|
|
auto button = object_ptr<Ui::GradientButton>::fromRaw(raw);
|
|
|
|
button->resizeToWidth(boxWidth
|
|
|
|
- stButton.buttonPadding.left()
|
|
|
|
- stButton.buttonPadding.right());
|
|
|
|
box->setShowFinishedCallback([raw = button.data()]{
|
|
|
|
raw->startGlareAnimation();
|
|
|
|
});
|
|
|
|
box->addButton(std::move(button));
|
|
|
|
|
2023-07-24 15:38:08 +00:00
|
|
|
groupValueChangedCallback(0);
|
2022-07-03 14:56:12 +00:00
|
|
|
|
|
|
|
Data::PeerPremiumValue(
|
|
|
|
user
|
|
|
|
) | rpl::skip(1) | rpl::start_with_next([=] {
|
|
|
|
box->closeBox();
|
|
|
|
}, box->lifetime());
|
|
|
|
}
|
|
|
|
|
2023-10-24 10:07:06 +00:00
|
|
|
[[nodiscard]] Data::GiftCodeLink MakeGiftCodeLink(
|
2023-09-28 18:44:22 +00:00
|
|
|
not_null<Main::Session*> session,
|
|
|
|
const QString &slug) {
|
|
|
|
const auto path = u"giftcode/"_q + slug;
|
|
|
|
return {
|
|
|
|
session->createInternalLink(path),
|
|
|
|
session->createInternalLinkFull(path),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-27 13:49:37 +00:00
|
|
|
[[nodiscard]] object_ptr<Ui::RpWidget> MakeLinkCopyIcon(
|
|
|
|
not_null<QWidget*> parent) {
|
|
|
|
auto result = object_ptr<Ui::RpWidget>(parent);
|
2023-09-28 18:44:22 +00:00
|
|
|
const auto raw = result.data();
|
|
|
|
|
|
|
|
raw->paintRequest() | rpl::start_with_next([=] {
|
|
|
|
auto p = QPainter(raw);
|
|
|
|
const auto &icon = st::giveawayGiftCodeLinkCopy;
|
2023-10-27 13:49:37 +00:00
|
|
|
const auto left = (raw->width() - icon.width()) / 2;
|
2023-09-28 18:44:22 +00:00
|
|
|
const auto top = (raw->height() - icon.height()) / 2;
|
|
|
|
icon.paint(p, left, top, raw->width());
|
|
|
|
}, raw->lifetime());
|
|
|
|
|
2023-10-27 13:49:37 +00:00
|
|
|
raw->resize(
|
|
|
|
st::giveawayGiftCodeLinkCopyWidth,
|
|
|
|
st::giveawayGiftCodeLinkHeight);
|
2023-09-28 18:44:22 +00:00
|
|
|
|
2023-10-27 13:49:37 +00:00
|
|
|
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
|
2023-09-28 18:44:22 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-10-11 09:29:02 +00:00
|
|
|
[[nodiscard]] tr::phrase<lngtag_count> GiftDurationPhrase(int months) {
|
2023-09-28 18:44:22 +00:00
|
|
|
return (months < 12)
|
2023-10-11 09:29:02 +00:00
|
|
|
? tr::lng_premium_gift_duration_months
|
|
|
|
: tr::lng_premium_gift_duration_years;
|
2023-09-28 18:44:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] object_ptr<Ui::RpWidget> MakePeerTableValue(
|
|
|
|
not_null<QWidget*> parent,
|
2023-11-01 12:47:25 +00:00
|
|
|
not_null<Window::SessionNavigation*> controller,
|
2023-09-28 18:44:22 +00:00
|
|
|
PeerId id) {
|
|
|
|
auto result = object_ptr<Ui::AbstractButton>(parent);
|
|
|
|
const auto raw = result.data();
|
|
|
|
|
|
|
|
const auto &st = st::giveawayGiftCodeUserpic;
|
|
|
|
raw->resize(raw->width(), st.photoSize);
|
|
|
|
|
|
|
|
const auto peer = controller->session().data().peer(id);
|
|
|
|
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(raw, peer, st);
|
|
|
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
|
|
|
raw,
|
|
|
|
peer->name(),
|
|
|
|
st::giveawayGiftCodeValue);
|
|
|
|
raw->widthValue(
|
|
|
|
) | rpl::start_with_next([=](int width) {
|
|
|
|
const auto position = st::giveawayGiftCodeNamePosition;
|
|
|
|
label->resizeToNaturalWidth(width - position.x());
|
|
|
|
label->moveToLeft(position.x(), position.y(), width);
|
|
|
|
const auto top = (raw->height() - userpic->height()) / 2;
|
|
|
|
userpic->moveToLeft(0, top, width);
|
|
|
|
}, label->lifetime());
|
|
|
|
|
|
|
|
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
label->setTextColorOverride(st::windowActiveTextFg->c);
|
|
|
|
|
|
|
|
raw->setClickedCallback([=] {
|
2023-11-01 12:47:25 +00:00
|
|
|
controller->uiShow()->showBox(PrepareShortInfoBox(peer, controller));
|
2023-09-28 18:44:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddTableRow(
|
|
|
|
not_null<Ui::TableLayout*> table,
|
|
|
|
rpl::producer<QString> label,
|
|
|
|
object_ptr<Ui::RpWidget> value,
|
|
|
|
style::margins valueMargins) {
|
|
|
|
table->addRow(
|
|
|
|
object_ptr<Ui::FlatLabel>(
|
|
|
|
table,
|
|
|
|
std::move(label),
|
|
|
|
st::giveawayGiftCodeLabel),
|
|
|
|
std::move(value),
|
|
|
|
st::giveawayGiftCodeLabelMargin,
|
|
|
|
valueMargins);
|
|
|
|
}
|
|
|
|
|
2023-10-11 09:47:21 +00:00
|
|
|
not_null<Ui::FlatLabel*> AddTableRow(
|
2023-09-28 18:44:22 +00:00
|
|
|
not_null<Ui::TableLayout*> table,
|
|
|
|
rpl::producer<QString> label,
|
2023-10-11 09:47:21 +00:00
|
|
|
rpl::producer<TextWithEntities> value) {
|
|
|
|
auto widget = object_ptr<Ui::FlatLabel>(
|
|
|
|
table,
|
|
|
|
std::move(value),
|
|
|
|
st::giveawayGiftCodeValue);
|
|
|
|
const auto result = widget.data();
|
2023-09-28 18:44:22 +00:00
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
std::move(label),
|
2023-10-11 09:47:21 +00:00
|
|
|
std::move(widget),
|
2023-09-28 18:44:22 +00:00
|
|
|
st::giveawayGiftCodeValueMargin);
|
2023-10-11 09:47:21 +00:00
|
|
|
return result;
|
2023-09-28 18:44:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AddTableRow(
|
|
|
|
not_null<Ui::TableLayout*> table,
|
|
|
|
rpl::producer<QString> label,
|
2023-11-01 12:47:25 +00:00
|
|
|
not_null<Window::SessionNavigation*> controller,
|
2023-09-28 18:44:22 +00:00
|
|
|
PeerId id) {
|
|
|
|
if (!id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
std::move(label),
|
|
|
|
MakePeerTableValue(table, controller, id),
|
|
|
|
st::giveawayGiftCodePeerMargin);
|
|
|
|
}
|
|
|
|
|
2022-07-03 14:56:12 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
GiftPremiumValidator::GiftPremiumValidator(
|
|
|
|
not_null<Window::SessionController*> controller)
|
|
|
|
: _controller(controller)
|
|
|
|
, _api(&_controller->session().mtp()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void GiftPremiumValidator::cancel() {
|
|
|
|
_requestId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GiftPremiumValidator::showBox(not_null<UserData*> user) {
|
|
|
|
if (_requestId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_requestId = _api.request(MTPusers_GetFullUser(
|
|
|
|
user->inputUser
|
|
|
|
)).done([=](const MTPusers_UserFull &result) {
|
|
|
|
if (!_requestId) {
|
|
|
|
// Canceled.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_requestId = 0;
|
|
|
|
// _controller->api().processFullPeer(peer, result);
|
2022-07-10 14:59:52 +00:00
|
|
|
_controller->session().data().processUsers(result.data().vusers());
|
|
|
|
_controller->session().data().processChats(result.data().vchats());
|
2022-07-03 14:56:12 +00:00
|
|
|
|
2022-07-10 14:59:52 +00:00
|
|
|
const auto &fullUser = result.data().vfull_user().data();
|
2022-07-22 04:33:43 +00:00
|
|
|
auto options = GiftOptionFromTL(fullUser);
|
2022-07-03 14:56:12 +00:00
|
|
|
if (!options.empty()) {
|
|
|
|
_controller->show(
|
|
|
|
Box(GiftBox, _controller, user, std::move(options)));
|
|
|
|
}
|
|
|
|
}).fail([=] {
|
|
|
|
_requestId = 0;
|
|
|
|
}).send();
|
|
|
|
}
|
2023-09-28 18:44:22 +00:00
|
|
|
|
2023-10-11 09:29:02 +00:00
|
|
|
rpl::producer<QString> GiftDurationValue(int months) {
|
|
|
|
return GiftDurationPhrase(months)(
|
|
|
|
lt_count,
|
|
|
|
rpl::single(float64((months < 12) ? months : (months / 12))));
|
|
|
|
}
|
|
|
|
|
|
|
|
QString GiftDuration(int months) {
|
|
|
|
return GiftDurationPhrase(months)(
|
|
|
|
tr::now,
|
|
|
|
lt_count,
|
|
|
|
(months < 12) ? months : (months / 12));
|
|
|
|
}
|
|
|
|
|
2023-09-28 18:44:22 +00:00
|
|
|
void GiftCodeBox(
|
|
|
|
not_null<Ui::GenericBox*> box,
|
2023-11-01 12:47:25 +00:00
|
|
|
not_null<Window::SessionNavigation*> controller,
|
2023-09-28 18:44:22 +00:00
|
|
|
const QString &slug) {
|
|
|
|
struct State {
|
|
|
|
rpl::variable<Api::GiftCode> data;
|
|
|
|
rpl::variable<bool> used;
|
2023-10-11 09:29:02 +00:00
|
|
|
bool sent = false;
|
2023-09-28 18:44:22 +00:00
|
|
|
};
|
|
|
|
const auto session = &controller->session();
|
|
|
|
const auto state = box->lifetime().make_state<State>(State{});
|
|
|
|
state->data = session->api().premium().giftCodeValue(slug);
|
|
|
|
state->used = state->data.value(
|
|
|
|
) | rpl::map([=](const Api::GiftCode &data) {
|
|
|
|
return data.used;
|
|
|
|
});
|
|
|
|
|
|
|
|
box->setWidth(st::boxWideWidth);
|
|
|
|
box->setStyle(st::giveawayGiftCodeBox);
|
|
|
|
box->setNoContentMargin(true);
|
|
|
|
|
|
|
|
const auto bar = box->setPinnedToTopContent(
|
|
|
|
object_ptr<Ui::Premium::TopBar>(
|
|
|
|
box,
|
|
|
|
st::giveawayGiftCodeCover,
|
|
|
|
nullptr,
|
|
|
|
rpl::conditional(
|
|
|
|
state->used.value(),
|
|
|
|
tr::lng_gift_link_used_title(),
|
|
|
|
tr::lng_gift_link_title()),
|
|
|
|
rpl::conditional(
|
|
|
|
state->used.value(),
|
|
|
|
tr::lng_gift_link_used_about(Ui::Text::RichLangValue),
|
|
|
|
tr::lng_gift_link_about(Ui::Text::RichLangValue)),
|
|
|
|
true));
|
|
|
|
|
|
|
|
const auto max = st::giveawayGiftCodeTopHeight;
|
|
|
|
bar->setMaximumHeight(max);
|
|
|
|
bar->setMinimumHeight(st::infoLayerTopBarHeight);
|
|
|
|
|
|
|
|
bar->resize(bar->width(), bar->maximumHeight());
|
|
|
|
|
|
|
|
const auto link = MakeGiftCodeLink(&controller->session(), slug);
|
|
|
|
box->addRow(
|
2023-10-27 13:49:37 +00:00
|
|
|
Ui::MakeLinkLabel(
|
2023-09-28 18:44:22 +00:00
|
|
|
box,
|
|
|
|
rpl::single(link.text),
|
|
|
|
rpl::single(link.link),
|
2023-10-27 13:49:37 +00:00
|
|
|
box->uiShow(),
|
|
|
|
MakeLinkCopyIcon(box)),
|
2023-09-28 18:44:22 +00:00
|
|
|
st::giveawayGiftCodeLinkMargin);
|
|
|
|
|
|
|
|
auto table = box->addRow(
|
|
|
|
object_ptr<Ui::TableLayout>(
|
|
|
|
box,
|
|
|
|
st::giveawayGiftCodeTable),
|
|
|
|
st::giveawayGiftCodeTableMargin);
|
|
|
|
const auto current = state->data.current();
|
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
tr::lng_gift_link_label_from(),
|
|
|
|
controller,
|
|
|
|
current.from);
|
2023-10-28 17:26:56 +00:00
|
|
|
if (current.to) {
|
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
tr::lng_gift_link_label_to(),
|
|
|
|
controller,
|
|
|
|
current.to);
|
|
|
|
} else {
|
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
tr::lng_gift_link_label_to(),
|
|
|
|
tr::lng_gift_link_label_to_unclaimed(Ui::Text::WithEntities));
|
|
|
|
}
|
2023-09-28 18:44:22 +00:00
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
tr::lng_gift_link_label_gift(),
|
|
|
|
tr::lng_gift_link_gift_premium(
|
|
|
|
lt_duration,
|
2023-10-11 09:47:21 +00:00
|
|
|
GiftDurationValue(current.months) | Ui::Text::ToWithEntities(),
|
|
|
|
Ui::Text::WithEntities));
|
|
|
|
const auto reason = AddTableRow(
|
2023-09-28 18:44:22 +00:00
|
|
|
table,
|
|
|
|
tr::lng_gift_link_label_reason(),
|
2023-10-11 09:47:21 +00:00
|
|
|
(current.giveawayId
|
2023-10-28 17:26:56 +00:00
|
|
|
? ((current.to
|
|
|
|
? tr::lng_gift_link_reason_giveaway
|
|
|
|
: tr::lng_gift_link_reason_unclaimed)(
|
|
|
|
) | Ui::Text::ToLink())
|
2023-10-11 09:47:21 +00:00
|
|
|
: current.giveaway
|
2023-10-28 17:26:56 +00:00
|
|
|
? ((current.to
|
|
|
|
? tr::lng_gift_link_reason_giveaway
|
|
|
|
: tr::lng_gift_link_reason_unclaimed)(
|
|
|
|
Ui::Text::WithEntities
|
|
|
|
) | rpl::type_erased())
|
2023-10-11 09:47:21 +00:00
|
|
|
: tr::lng_gift_link_reason_chosen(Ui::Text::WithEntities)));
|
|
|
|
reason->setClickHandlerFilter([=](const auto &...) {
|
|
|
|
controller->showPeerHistory(
|
|
|
|
current.from,
|
|
|
|
Window::SectionShow::Way::Forward,
|
|
|
|
current.giveawayId);
|
|
|
|
return false;
|
|
|
|
});
|
2023-10-06 12:21:23 +00:00
|
|
|
if (current.date) {
|
|
|
|
AddTableRow(
|
|
|
|
table,
|
|
|
|
tr::lng_gift_link_label_date(),
|
2023-10-11 09:47:21 +00:00
|
|
|
rpl::single(Ui::Text::WithEntities(
|
|
|
|
langDateTime(base::unixtime::parse(current.date)))));
|
2023-10-06 12:21:23 +00:00
|
|
|
}
|
2023-09-28 18:44:22 +00:00
|
|
|
|
|
|
|
auto shareLink = tr::lng_gift_link_also_send_link(
|
|
|
|
) | rpl::map([](const QString &text) {
|
|
|
|
return Ui::Text::Link(text);
|
|
|
|
});
|
|
|
|
auto richDate = [](const Api::GiftCode &data) {
|
|
|
|
return TextWithEntities{
|
|
|
|
langDateTime(base::unixtime::parse(data.used)),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const auto footer = box->addRow(
|
|
|
|
object_ptr<Ui::FlatLabel>(
|
|
|
|
box,
|
|
|
|
rpl::conditional(
|
|
|
|
state->used.value(),
|
|
|
|
tr::lng_gift_link_used_footer(
|
|
|
|
lt_date,
|
|
|
|
state->data.value() | rpl::map(richDate),
|
|
|
|
Ui::Text::WithEntities),
|
|
|
|
tr::lng_gift_link_also_send(
|
|
|
|
lt_link,
|
|
|
|
std::move(shareLink),
|
|
|
|
Ui::Text::WithEntities)),
|
|
|
|
st::giveawayGiftCodeFooter),
|
|
|
|
st::giveawayGiftCodeFooterMargin);
|
|
|
|
footer->setClickHandlerFilter([=](const auto &...) {
|
2023-10-11 10:33:59 +00:00
|
|
|
const auto chosen = [=](not_null<Data::Thread*> thread) {
|
2023-11-01 12:47:25 +00:00
|
|
|
const auto content = controller->parentController()->content();
|
2023-10-11 10:33:59 +00:00
|
|
|
return content->shareUrl(
|
|
|
|
thread,
|
|
|
|
MakeGiftCodeLink(&controller->session(), slug).link,
|
|
|
|
QString());
|
|
|
|
};
|
|
|
|
Window::ShowChooseRecipientBox(controller, chosen);
|
2023-09-28 18:44:22 +00:00
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
const auto close = Ui::CreateChild<Ui::IconButton>(
|
|
|
|
box.get(),
|
|
|
|
st::boxTitleClose);
|
|
|
|
close->setClickedCallback([=] {
|
|
|
|
box->closeBox();
|
|
|
|
});
|
|
|
|
box->widthValue(
|
|
|
|
) | rpl::start_with_next([=](int width) {
|
|
|
|
close->moveToRight(0, 0);
|
|
|
|
}, box->lifetime());
|
|
|
|
|
|
|
|
const auto button = box->addButton(rpl::conditional(
|
|
|
|
state->used.value(),
|
|
|
|
tr::lng_box_ok(),
|
|
|
|
tr::lng_gift_link_use()
|
|
|
|
), [=] {
|
|
|
|
if (state->used.current()) {
|
|
|
|
box->closeBox();
|
2023-10-11 09:29:02 +00:00
|
|
|
} else if (!state->sent) {
|
|
|
|
state->sent = true;
|
|
|
|
const auto done = crl::guard(box, [=](const QString &error) {
|
|
|
|
if (error.isEmpty()) {
|
|
|
|
auto copy = state->data.current();
|
|
|
|
copy.used = base::unixtime::now();
|
|
|
|
state->data = std::move(copy);
|
2023-10-11 09:47:21 +00:00
|
|
|
|
|
|
|
Ui::StartFireworks(box->parentWidget());
|
2023-10-11 09:29:02 +00:00
|
|
|
} else {
|
|
|
|
box->uiShow()->showToast(error);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
controller->session().api().premium().applyGiftCode(slug, done);
|
2023-09-28 18:44:22 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
const auto buttonPadding = st::giveawayGiftCodeBox.buttonPadding;
|
|
|
|
const auto buttonWidth = st::boxWideWidth
|
|
|
|
- buttonPadding.left()
|
|
|
|
- buttonPadding.right();
|
|
|
|
button->widthValue() | rpl::filter([=] {
|
|
|
|
return (button->widthNoMargins() != buttonWidth);
|
|
|
|
}) | rpl::start_with_next([=] {
|
|
|
|
button->resizeToWidth(buttonWidth);
|
|
|
|
}, button->lifetime());
|
|
|
|
}
|
2023-10-06 12:21:23 +00:00
|
|
|
|
|
|
|
void ResolveGiftCode(
|
2023-11-01 12:47:25 +00:00
|
|
|
not_null<Window::SessionNavigation*> controller,
|
2023-10-06 12:21:23 +00:00
|
|
|
const QString &slug) {
|
|
|
|
const auto done = [=](Api::GiftCode code) {
|
|
|
|
if (!code) {
|
|
|
|
controller->showToast(tr::lng_gift_link_expired(tr::now));
|
|
|
|
} else {
|
2023-11-01 12:47:25 +00:00
|
|
|
controller->uiShow()->showBox(Box(GiftCodeBox, controller, slug));
|
2023-10-06 12:21:23 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
controller->session().api().premium().checkGiftCode(
|
|
|
|
slug,
|
|
|
|
crl::guard(controller, done));
|
|
|
|
}
|
2023-10-11 09:29:02 +00:00
|
|
|
|
|
|
|
void GiveawayInfoBox(
|
|
|
|
not_null<Ui::GenericBox*> box,
|
2023-11-01 12:47:25 +00:00
|
|
|
not_null<Window::SessionNavigation*> controller,
|
2023-10-11 09:29:02 +00:00
|
|
|
Data::Giveaway giveaway,
|
|
|
|
Api::GiveawayInfo info) {
|
|
|
|
using State = Api::GiveawayState;
|
|
|
|
const auto finished = (info.state == State::Finished)
|
|
|
|
|| (info.state == State::Refunded);
|
|
|
|
|
|
|
|
box->setTitle((finished
|
|
|
|
? tr::lng_prizes_end_title
|
|
|
|
: tr::lng_prizes_how_title)());
|
|
|
|
|
|
|
|
const auto first = !giveaway.channels.empty()
|
|
|
|
? giveaway.channels.front()->name()
|
|
|
|
: u"channel"_q;
|
|
|
|
auto text = (finished
|
|
|
|
? tr::lng_prizes_end_text
|
|
|
|
: tr::lng_prizes_how_text)(
|
|
|
|
tr::now,
|
|
|
|
lt_admins,
|
|
|
|
tr::lng_prizes_admins(
|
|
|
|
tr::now,
|
|
|
|
lt_count,
|
|
|
|
giveaway.quantity,
|
|
|
|
lt_channel,
|
|
|
|
Ui::Text::Bold(first),
|
|
|
|
lt_duration,
|
|
|
|
TextWithEntities{ GiftDuration(giveaway.months) },
|
|
|
|
Ui::Text::RichLangValue),
|
|
|
|
Ui::Text::RichLangValue);
|
|
|
|
const auto many = (giveaway.channels.size() > 1);
|
|
|
|
const auto count = info.winnersCount
|
|
|
|
? info.winnersCount
|
|
|
|
: giveaway.quantity;
|
|
|
|
auto winners = giveaway.all
|
|
|
|
? (many
|
|
|
|
? tr::lng_prizes_winners_all_of_many
|
|
|
|
: tr::lng_prizes_winners_all_of_one)(
|
|
|
|
tr::now,
|
|
|
|
lt_count,
|
|
|
|
count,
|
|
|
|
lt_channel,
|
|
|
|
Ui::Text::Bold(first),
|
|
|
|
Ui::Text::RichLangValue)
|
|
|
|
: (many
|
|
|
|
? tr::lng_prizes_winners_new_of_many
|
|
|
|
: tr::lng_prizes_winners_new_of_one)(
|
|
|
|
tr::now,
|
|
|
|
lt_count,
|
|
|
|
count,
|
|
|
|
lt_channel,
|
|
|
|
Ui::Text::Bold(first),
|
|
|
|
lt_start_date,
|
2023-10-13 12:17:02 +00:00
|
|
|
Ui::Text::Bold(
|
|
|
|
langDateTime(base::unixtime::parse(info.startDate))),
|
2023-10-11 09:29:02 +00:00
|
|
|
Ui::Text::RichLangValue);
|
|
|
|
text.append("\n\n").append((finished
|
|
|
|
? tr::lng_prizes_end_when_finish
|
|
|
|
: tr::lng_prizes_how_when_finish)(
|
|
|
|
tr::now,
|
|
|
|
lt_date,
|
|
|
|
Ui::Text::Bold(langDayOfMonthFull(
|
|
|
|
base::unixtime::parse(giveaway.untilDate).date())),
|
|
|
|
lt_winners,
|
|
|
|
winners,
|
|
|
|
Ui::Text::RichLangValue));
|
|
|
|
if (info.activatedCount > 0) {
|
|
|
|
text.append(' ').append(tr::lng_prizes_end_activated(
|
|
|
|
tr::now,
|
|
|
|
lt_count,
|
2023-11-06 16:29:41 +00:00
|
|
|
info.activatedCount,
|
|
|
|
Ui::Text::RichLangValue));
|
2023-10-11 09:29:02 +00:00
|
|
|
}
|
|
|
|
if (!info.giftCode.isEmpty()) {
|
|
|
|
text.append("\n\n");
|
|
|
|
text.append(tr::lng_prizes_you_won(
|
|
|
|
tr::now,
|
|
|
|
lt_cup,
|
|
|
|
QString::fromUtf8("\xf0\x9f\x8f\x86")));
|
|
|
|
} else if (info.state == State::Finished) {
|
|
|
|
text.append("\n\n");
|
|
|
|
text.append(tr::lng_prizes_you_didnt(tr::now));
|
|
|
|
} else if (info.state == State::Preparing) {
|
|
|
|
|
|
|
|
} else if (info.state != State::Refunded) {
|
|
|
|
if (info.adminChannelId) {
|
|
|
|
const auto channel = controller->session().data().channel(
|
|
|
|
info.adminChannelId);
|
|
|
|
text.append("\n\n").append(tr::lng_prizes_how_no_admin(
|
|
|
|
tr::now,
|
|
|
|
lt_channel,
|
|
|
|
Ui::Text::Bold(channel->name()),
|
|
|
|
Ui::Text::RichLangValue));
|
|
|
|
} else if (info.tooEarlyDate) {
|
|
|
|
text.append("\n\n").append(tr::lng_prizes_how_no_joined(
|
|
|
|
tr::now,
|
|
|
|
lt_date,
|
|
|
|
Ui::Text::Bold(
|
|
|
|
langDateTime(base::unixtime::parse(info.tooEarlyDate))),
|
|
|
|
Ui::Text::RichLangValue));
|
2023-10-13 12:17:02 +00:00
|
|
|
} else if (!info.disallowedCountry.isEmpty()) {
|
|
|
|
text.append("\n\n").append(tr::lng_prizes_how_no_country(
|
|
|
|
tr::now,
|
|
|
|
Ui::Text::RichLangValue));
|
2023-10-11 09:29:02 +00:00
|
|
|
} else if (info.participating) {
|
|
|
|
text.append("\n\n").append((many
|
|
|
|
? tr::lng_prizes_how_yes_joined_many
|
|
|
|
: tr::lng_prizes_how_yes_joined_one)(
|
|
|
|
tr::now,
|
|
|
|
lt_channel,
|
|
|
|
Ui::Text::Bold(first),
|
|
|
|
Ui::Text::RichLangValue));
|
|
|
|
} else {
|
|
|
|
text.append("\n\n").append((many
|
|
|
|
? tr::lng_prizes_how_participate_many
|
|
|
|
: tr::lng_prizes_how_participate_one)(
|
|
|
|
tr::now,
|
|
|
|
lt_channel,
|
|
|
|
Ui::Text::Bold(first),
|
|
|
|
lt_date,
|
|
|
|
Ui::Text::Bold(langDayOfMonthFull(
|
|
|
|
base::unixtime::parse(giveaway.untilDate).date())),
|
|
|
|
Ui::Text::RichLangValue));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const auto padding = st::boxPadding;
|
|
|
|
box->addRow(
|
|
|
|
object_ptr<Ui::FlatLabel>(
|
|
|
|
box.get(),
|
|
|
|
rpl::single(std::move(text)),
|
|
|
|
st::boxLabel),
|
|
|
|
{ padding.left(), 0, padding.right(), padding.bottom() });
|
|
|
|
|
|
|
|
if (info.state == State::Refunded) {
|
|
|
|
const auto wrap = box->addRow(
|
|
|
|
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
|
|
|
box.get(),
|
|
|
|
object_ptr<Ui::FlatLabel>(
|
|
|
|
box.get(),
|
|
|
|
tr::lng_prizes_cancelled(),
|
|
|
|
st::giveawayRefundedLabel),
|
|
|
|
st::giveawayRefundedPadding),
|
|
|
|
{ padding.left(), 0, padding.right(), padding.bottom() });
|
|
|
|
const auto bg = wrap->lifetime().make_state<Ui::RoundRect>(
|
|
|
|
st::boxRadius,
|
|
|
|
st::attentionBoxButton.textBgOver);
|
|
|
|
wrap->paintRequest() | rpl::start_with_next([=] {
|
|
|
|
auto p = QPainter(wrap);
|
|
|
|
bg->paint(p, wrap->rect());
|
|
|
|
}, wrap->lifetime());
|
|
|
|
}
|
|
|
|
if (const auto slug = info.giftCode; !slug.isEmpty()) {
|
|
|
|
box->addButton(tr::lng_prizes_view_prize(), [=] {
|
|
|
|
ResolveGiftCode(controller, slug);
|
|
|
|
});
|
|
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
|
|
} else {
|
|
|
|
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResolveGiveawayInfo(
|
2023-11-01 12:47:25 +00:00
|
|
|
not_null<Window::SessionNavigation*> controller,
|
2023-10-11 09:29:02 +00:00
|
|
|
not_null<PeerData*> peer,
|
|
|
|
MsgId messageId,
|
|
|
|
Data::Giveaway giveaway) {
|
|
|
|
const auto show = [=](Api::GiveawayInfo info) {
|
|
|
|
if (!info) {
|
|
|
|
controller->showToast(
|
|
|
|
tr::lng_confirm_phone_link_invalid(tr::now));
|
|
|
|
} else {
|
2023-11-01 12:47:25 +00:00
|
|
|
controller->uiShow()->showBox(
|
2023-10-11 09:29:02 +00:00
|
|
|
Box(GiveawayInfoBox, controller, giveaway, info));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
controller->session().api().premium().resolveGiveawayInfo(
|
|
|
|
peer,
|
|
|
|
messageId,
|
|
|
|
crl::guard(controller, show));
|
|
|
|
}
|