mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Initial no-design craft implementation.
This commit is contained in:
@@ -337,6 +337,8 @@ PRIVATE
|
||||
boxes/star_gift_auction_box.h
|
||||
boxes/star_gift_box.cpp
|
||||
boxes/star_gift_box.h
|
||||
boxes/star_gift_craft_box.cpp
|
||||
boxes/star_gift_craft_box.h
|
||||
boxes/star_gift_preview_box.cpp
|
||||
boxes/star_gift_preview_box.h
|
||||
boxes/star_gift_resale_box.cpp
|
||||
|
||||
@@ -3816,6 +3816,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_limited_of_one" = "unique";
|
||||
"lng_gift_limited_of_count" = "1 of {amount}";
|
||||
"lng_gift_collectible_tag" = "gift";
|
||||
"lng_gift_crafted_tag" = "crafted";
|
||||
"lng_gift_epic_tag" = "epic";
|
||||
"lng_gift_legendary_tag" = "legendary";
|
||||
"lng_gift_view_unpack" = "Unpack";
|
||||
"lng_gift_anonymous_hint" = "Only you can see the sender's name.";
|
||||
"lng_gift_anonymous_hint_channel" = "Only admins of this channel can see the sender's name.";
|
||||
@@ -4094,6 +4097,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_collection_reorder_exit" = "Apply Reorder";
|
||||
"lng_gift_collection_remove_from" = "Remove from Collection";
|
||||
"lng_gift_locked_title" = "Gift Locked";
|
||||
"lng_gift_craft_info_level" = "Level {n} Gift";
|
||||
"lng_gift_craft_info_title" = "Gift Crafting";
|
||||
"lng_gift_craft_info_about" = "Use your existing gifts to craft new ones.";
|
||||
"lng_gift_craft_combine_title" = "Combine Gifts";
|
||||
"lng_gift_craft_combine_about" = "Add up to 4 NFT Gifts to attempt crafting a new upgraded model.";
|
||||
"lng_gift_craft_input_title" = "Input Matters";
|
||||
"lng_gift_craft_input_about" = "Each craft has a success chance. Better combinations improve the outcome.";
|
||||
"lng_gift_craft_exclusive_title" = "Exclusive Look";
|
||||
"lng_gift_craft_exclusive_about" = "Reforge gifts into a rarer collectibles with a new look.";
|
||||
"lng_gift_craft_start_button" = "Start Crafting";
|
||||
"lng_gift_craft_title" = "Craft Gift";
|
||||
"lng_gift_craft_about1" = "Add up to **4** gifts to craft new {gift}.";
|
||||
"lng_gift_craft_about2" = "If crafting fails, all selected gifts will be **consumed**.";
|
||||
"lng_gift_craft_view_all" = "{emoji} View all new variants {arrow}";
|
||||
"lng_gift_craft_button" = "Craft {gift}";
|
||||
"lng_gift_craft_button_chance" = "{percent} Success Chance";
|
||||
"lng_gift_craft_select_title" = "Select Gifts";
|
||||
"lng_gift_craft_select_about" = "You need to add up to **3 gifts** from the same collection for crafting.";
|
||||
"lng_gift_craft_select_your" = "Your gifts";
|
||||
"lng_gift_craft_select_none" = "You don't have other gifts from this collection.";
|
||||
"lng_gift_craft_select_market#one" = "{count} suitable gift on sale";
|
||||
"lng_gift_craft_select_market#other" = "{count} suitable gifts on sale";
|
||||
"lng_gift_craft_progress" = "Crafting";
|
||||
"lng_gift_craft_progress_chance" = "{percent} success chance";
|
||||
"lng_gift_craft_progress_fails" = "If crafting fails, all selected gifts will be consumed.";
|
||||
|
||||
"lng_auction_about_title" = "Auction";
|
||||
"lng_auction_about_subtitle" = "Join the battle for exclusive gifts.";
|
||||
|
||||
@@ -950,6 +950,7 @@ std::optional<Data::StarGift> FromTL(
|
||||
.number = data.vnum().v,
|
||||
.onlyAcceptTon = data.is_resale_ton_only(),
|
||||
.canBeTheme = data.is_theme_available(),
|
||||
.crafted = data.is_crafted(),
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
.value = (data.vvalue_amount()
|
||||
@@ -999,6 +1000,9 @@ std::optional<Data::SavedStarGift> FromTL(
|
||||
unique->exportAt = data.vcan_export_at().value_or_empty();
|
||||
unique->canTransferAt = data.vcan_transfer_at().value_or_empty();
|
||||
unique->canResellAt = data.vcan_resell_at().value_or_empty();
|
||||
unique->canCraftAt = data.vcan_craft_at().value_or_empty();
|
||||
unique->craftChancePermille
|
||||
= data.vcraft_chance_permille().value_or_empty();
|
||||
}
|
||||
using Id = Data::SavedStarGiftId;
|
||||
const auto hasUnique = parsed->unique != nullptr;
|
||||
@@ -1045,7 +1049,17 @@ Data::UniqueGiftModel FromTL(
|
||||
.document = session->data().processDocument(data.vdocument()),
|
||||
};
|
||||
result.name = qs(data.vname());
|
||||
result.rarityPermille = data.vrarity_permille().v;
|
||||
data.vrarity().match([&](const MTPDstarGiftAttributeRarity &data) {
|
||||
result.rarityPermille = data.vpermille().v;
|
||||
}, [](const auto &) {});
|
||||
//MTPDstarGiftAttributeRarityRare &c_starGiftAttributeRarityRare() const;
|
||||
//[[nodiscard]] const MTPDstarGiftAttributeRarityEpic &c_starGiftAttributeRarityEpic() const;
|
||||
//[[nodiscard]] const MTPDstarGiftAttributeRarityLegendary &c_starGiftAttributeRarityLegendary() const;
|
||||
//[[nodiscard]] const MTPDstarGiftAttributeRarityMythic &c_starGiftAttributeRarityMythic() const;
|
||||
// );
|
||||
//data.vrarity().c_starGiftAttributeRarity();
|
||||
//data.vrarity().c_()
|
||||
//result.rarityPermille = data.vrarity_permille().v;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1057,14 +1071,18 @@ Data::UniqueGiftPattern FromTL(
|
||||
};
|
||||
result.document->overrideEmojiUsesTextColor(true);
|
||||
result.name = qs(data.vname());
|
||||
result.rarityPermille = data.vrarity_permille().v;
|
||||
data.vrarity().match([&](const MTPDstarGiftAttributeRarity &data) {
|
||||
result.rarityPermille = data.vpermille().v;
|
||||
}, [](const auto &) {});
|
||||
return result;
|
||||
}
|
||||
|
||||
Data::UniqueGiftBackdrop FromTL(const MTPDstarGiftAttributeBackdrop &data) {
|
||||
auto result = Data::UniqueGiftBackdrop{ .id = data.vbackdrop_id().v };
|
||||
result.name = qs(data.vname());
|
||||
result.rarityPermille = data.vrarity_permille().v;
|
||||
data.vrarity().match([&](const MTPDstarGiftAttributeRarity &data) {
|
||||
result.rarityPermille = data.vpermille().v;
|
||||
}, [](const auto &) {});
|
||||
result.centerColor = Ui::ColorFromSerialized(
|
||||
data.vcenter_color());
|
||||
result.edgeColor = Ui::ColorFromSerialized(
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_domain.h" // kMaxAccounts
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/controls/feature_list.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
@@ -904,42 +905,28 @@ struct VideoPreviewDocument {
|
||||
}
|
||||
|
||||
void AddGiftsInfoRows(not_null<Ui::VerticalLayout*> container) {
|
||||
const auto infoRow = [&](
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> text,
|
||||
not_null<const style::icon*> icon) {
|
||||
auto raw = container->add(
|
||||
object_ptr<Ui::VerticalLayout>(container));
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(title) | rpl::map(tr::bold),
|
||||
st::defaultFlatLabel),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(text),
|
||||
st::upgradeGiftSubtext),
|
||||
st::settingsPremiumRowAboutPadding);
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
raw,
|
||||
*icon,
|
||||
st::starrefInfoIconPosition);
|
||||
const auto features = std::vector<Ui::FeatureListEntry>{
|
||||
{
|
||||
st::menuIconUnique,
|
||||
tr::lng_gift_upgrade_unique_title(tr::now),
|
||||
tr::lng_gift_upgrade_unique_about(tr::now, tr::marked),
|
||||
},
|
||||
{
|
||||
st::menuIconTradable,
|
||||
tr::lng_gift_upgrade_tradable_title(tr::now),
|
||||
tr::lng_gift_upgrade_tradable_about(tr::now, tr::marked),
|
||||
},
|
||||
{
|
||||
st::menuIconNftWear,
|
||||
tr::lng_gift_upgrade_wearable_title(tr::now),
|
||||
tr::lng_gift_upgrade_wearable_about(tr::now, tr::marked),
|
||||
},
|
||||
};
|
||||
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_unique_title(),
|
||||
tr::lng_gift_upgrade_unique_about(),
|
||||
&st::menuIconUnique);
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_tradable_title(),
|
||||
tr::lng_gift_upgrade_tradable_about(),
|
||||
&st::menuIconTradable);
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_wearable_title(),
|
||||
tr::lng_gift_upgrade_wearable_about(),
|
||||
&st::menuIconNftWear);
|
||||
for (const auto &feature : features) {
|
||||
container->add(
|
||||
Ui::MakeFeatureListEntry(container, feature),
|
||||
st::boxRowPadding);
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewBox(
|
||||
|
||||
@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "api/api_text_entities.h"
|
||||
//#include "base/call_delayed.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "base/random.h"
|
||||
@@ -85,6 +84,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/controls/button_labels.h"
|
||||
#include "ui/controls/emoji_button.h"
|
||||
#include "ui/controls/feature_list.h"
|
||||
#include "ui/controls/ton_common.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
@@ -5224,60 +5224,54 @@ void UpgradeBox(
|
||||
|
||||
AddSkip(container, st::defaultVerticalListSkip * 2);
|
||||
|
||||
const auto infoRow = [&](
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> text,
|
||||
not_null<const style::icon*> icon) {
|
||||
auto raw = container->add(
|
||||
object_ptr<Ui::VerticalLayout>(container));
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(title) | rpl::map(tr::bold),
|
||||
st::defaultFlatLabel),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(text),
|
||||
st::upgradeGiftSubtext),
|
||||
st::settingsPremiumRowAboutPadding);
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
raw,
|
||||
*icon,
|
||||
st::starrefInfoIconPosition);
|
||||
const auto features = std::vector<Ui::FeatureListEntry>{
|
||||
{
|
||||
st::menuIconUnique,
|
||||
tr::lng_gift_upgrade_unique_title(tr::now),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_unique_about(tr::now, tr::marked)
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_unique_about_channel
|
||||
: tr::lng_gift_upgrade_unique_about_user)(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::marked(args.peer->shortName()),
|
||||
tr::marked)),
|
||||
},
|
||||
{
|
||||
st::menuIconReplace,
|
||||
tr::lng_gift_upgrade_transferable_title(tr::now),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_transferable_about(
|
||||
tr::now,
|
||||
tr::marked)
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_transferable_about_channel
|
||||
: tr::lng_gift_upgrade_transferable_about_user)(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::marked(args.peer->shortName()),
|
||||
tr::marked)),
|
||||
},
|
||||
{
|
||||
st::menuIconTradable,
|
||||
tr::lng_gift_upgrade_tradable_title(tr::now),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_tradable_about(tr::now, tr::marked)
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_tradable_about_channel
|
||||
: tr::lng_gift_upgrade_tradable_about_user)(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::marked(args.peer->shortName()),
|
||||
tr::marked)),
|
||||
},
|
||||
};
|
||||
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_unique_title(),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_unique_about()
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_unique_about_channel
|
||||
: tr::lng_gift_upgrade_unique_about_user)(
|
||||
lt_name,
|
||||
rpl::single(args.peer->shortName()))),
|
||||
&st::menuIconUnique);
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_transferable_title(),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_transferable_about()
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_transferable_about_channel
|
||||
: tr::lng_gift_upgrade_transferable_about_user)(
|
||||
lt_name,
|
||||
rpl::single(args.peer->shortName()))),
|
||||
&st::menuIconReplace);
|
||||
infoRow(
|
||||
tr::lng_gift_upgrade_tradable_title(),
|
||||
(args.savedId
|
||||
? tr::lng_gift_upgrade_tradable_about()
|
||||
: (args.peer->isBroadcast()
|
||||
? tr::lng_gift_upgrade_tradable_about_channel
|
||||
: tr::lng_gift_upgrade_tradable_about_user)(
|
||||
lt_name,
|
||||
rpl::single(args.peer->shortName()))),
|
||||
&st::menuIconTradable);
|
||||
for (const auto &feature : features) {
|
||||
container->add(
|
||||
Ui::MakeFeatureListEntry(container, feature),
|
||||
st::boxRowPadding);
|
||||
}
|
||||
|
||||
const auto gifting = !args.savedId
|
||||
&& !args.giftPrepayUpgradeHash.isEmpty();
|
||||
|
||||
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
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/star_gift_craft_box.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_credits.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_star_gift.h"
|
||||
#include "data/data_user.h"
|
||||
#include "info/peer_gifts/info_peer_gifts_common.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/about_cocoon_box.h" // AddUniqueCloseButton.
|
||||
#include "ui/controls/button_labels.h"
|
||||
#include "ui/controls/feature_list.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/top_background_gradient.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
using namespace Info::PeerGifts;
|
||||
|
||||
[[nodiscard]] QImage CreateBgGradient(
|
||||
QSize size,
|
||||
const Data::UniqueGiftBackdrop &backdrop) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto result = QImage(size * ratio, QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(ratio);
|
||||
|
||||
auto p = QPainter(&result);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
auto gradient = QRadialGradient(
|
||||
QPoint(size.width() / 2, size.height() / 2),
|
||||
size.height() / 2);
|
||||
gradient.setStops({
|
||||
{ 0., backdrop.centerColor },
|
||||
{ 1., backdrop.edgeColor },
|
||||
});
|
||||
p.setBrush(gradient);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawRect(0, 0, size.width(), size.height());
|
||||
p.end();
|
||||
|
||||
const auto mask = Images::CornersMask(st::boxRadius);
|
||||
return Images::Round(std::move(result), mask);
|
||||
}
|
||||
|
||||
void ShowSelectGiftBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::vector<Data::SavedStarGift> list,
|
||||
Fn<void(std::optional<Data::SavedStarGift>)> chosen,
|
||||
bool exists) {
|
||||
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(rpl::single(u"Choose Gift"_q));
|
||||
box->setWidth(st::boxWideWidth);
|
||||
|
||||
struct Entry {
|
||||
Data::SavedStarGift gift;
|
||||
GiftButton *button = nullptr;
|
||||
};
|
||||
struct State {
|
||||
explicit State(not_null<Main::Session*> session)
|
||||
: delegate(session, GiftButtonMode::Minimal) {
|
||||
}
|
||||
|
||||
Delegate delegate;
|
||||
std::vector<Entry> entries;
|
||||
rpl::event_stream<> changes;
|
||||
rpl::variable<int> successPercentPermille;
|
||||
rpl::variable<QString> successPercentText;
|
||||
|
||||
int requestingIndex = 0;
|
||||
bool crafting = false;
|
||||
};
|
||||
const auto session = &controller->session();
|
||||
const auto state = box->lifetime().make_state<State>(session);
|
||||
|
||||
const auto single = state->delegate.buttonSize();
|
||||
const auto extend = state->delegate.buttonExtend();
|
||||
const auto full = single.grownBy(extend).height();
|
||||
const auto skip = st::boxRowPadding.left() / 2;
|
||||
auto row = (RpWidget*)nullptr;
|
||||
for (auto &gift : list) {
|
||||
if (!row) {
|
||||
row = box->addRow(object_ptr<RpWidget>(box), QMargins());
|
||||
row->resize(row->width(), full);
|
||||
}
|
||||
const auto col = (state->entries.size() % 3);
|
||||
state->entries.push_back(Entry{
|
||||
.gift = gift,
|
||||
.button = CreateChild<GiftButton>(
|
||||
row,
|
||||
&state->delegate),
|
||||
});
|
||||
const auto button = state->entries.back().button;
|
||||
button->setClickedCallback([=] {
|
||||
chosen(gift);
|
||||
box->closeBox();
|
||||
});
|
||||
button->show();
|
||||
button->setDescriptor(GiftTypeStars{
|
||||
.info = {
|
||||
.id = gift.info.id,
|
||||
.unique = gift.info.unique,
|
||||
.document = gift.info.unique->model.document,
|
||||
},
|
||||
}, GiftButton::Mode::Minimal);
|
||||
const auto width = (st::boxWideWidth - 2 * skip - st::boxRowPadding.left() - st::boxRowPadding.right()) / 3;
|
||||
const auto left = st::boxRowPadding.left() + (width + skip) * col;
|
||||
button->setGeometry(QRect(left, extend.top(), width, single.height()), extend);
|
||||
if (col == 2) {
|
||||
row = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
box->addButton(tr::lng_box_ok(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
if (exists) {
|
||||
box->addLeftButton(rpl::single(u"Remove"_q), [=] {
|
||||
chosen(std::nullopt);
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
box->setMaxHeight(st::boxWideWidth);
|
||||
}));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<RpWidget> MakeCraftContent(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
Data::SavedStarGiftId savedId) {
|
||||
auto result = object_ptr<RpWidget>(box->verticalLayout());
|
||||
const auto raw = result.data();
|
||||
|
||||
const auto width = st::boxWidth;
|
||||
const auto height = st::boxWideWidth;
|
||||
|
||||
struct BackdropView {
|
||||
Data::UniqueGiftBackdrop colors;
|
||||
QImage gradient;
|
||||
};
|
||||
struct PatternView {
|
||||
DocumentData *document = nullptr;
|
||||
std::unique_ptr<Text::CustomEmoji> emoji;
|
||||
base::flat_map<int, base::flat_map<float64, QImage>> emojis;
|
||||
};
|
||||
struct Entry {
|
||||
Data::SavedStarGiftId id;
|
||||
std::shared_ptr<Data::UniqueGift> gift;
|
||||
GiftButton *button = nullptr;
|
||||
AbstractButton *add = nullptr;
|
||||
};
|
||||
struct State {
|
||||
explicit State(not_null<Main::Session*> session)
|
||||
: delegate(session, GiftButtonMode::Minimal) {
|
||||
}
|
||||
|
||||
Delegate delegate;
|
||||
BackdropView backdrop;
|
||||
PatternView pattern;
|
||||
std::vector<Entry> entries;
|
||||
rpl::event_stream<> changes;
|
||||
rpl::variable<int> successPercentPermille;
|
||||
rpl::variable<QString> successPercentText;
|
||||
|
||||
int requestingIndex = 0;
|
||||
bool crafting = false;
|
||||
};
|
||||
const auto session = &controller->session();
|
||||
const auto state = raw->lifetime().make_state<State>(session);
|
||||
state->backdrop.colors.centerColor = QColor(42, 61, 82, 255);
|
||||
state->backdrop.colors.edgeColor = QColor(35, 45, 63, 255);
|
||||
state->entries.push_back(Entry{ savedId, gift });
|
||||
for (auto i = 0; i != 3; ++i) {
|
||||
state->entries.emplace_back();
|
||||
}
|
||||
|
||||
state->successPercentPermille = state->changes.events_starting_with_copy(
|
||||
rpl::empty
|
||||
) | rpl::map([=] {
|
||||
auto result = 0;
|
||||
for (const auto &entry : state->entries) {
|
||||
if (const auto gift = entry.gift.get()) {
|
||||
result += gift->craftChancePermille;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
state->successPercentText = state->successPercentPermille.value(
|
||||
) | rpl::map([](int permille) {
|
||||
return QString::number(permille / 10.) + '%';
|
||||
});
|
||||
|
||||
const auto editEntry = [=](int index) {
|
||||
const auto guard = base::make_weak(raw);
|
||||
if (state->requestingIndex) {
|
||||
state->requestingIndex = index;
|
||||
return;
|
||||
}
|
||||
state->requestingIndex = index;
|
||||
session->api().request(MTPpayments_GetCraftStarGifts(
|
||||
MTP_long(gift->initialGiftId),
|
||||
MTP_string(),
|
||||
MTP_int(30)
|
||||
)).done([=](const MTPpayments_SavedStarGifts &result) {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
const auto user = session->user();
|
||||
const auto owner = &session->data();
|
||||
const auto &data = result.data();
|
||||
owner->processUsers(data.vusers());
|
||||
owner->processChats(data.vchats());
|
||||
|
||||
auto list = std::vector<Data::SavedStarGift>();
|
||||
list.reserve(data.vgifts().v.size());
|
||||
for (const auto &gift : data.vgifts().v) {
|
||||
if (auto parsed = Api::FromTL(user, gift)) {
|
||||
list.push_back(std::move(*parsed));
|
||||
}
|
||||
}
|
||||
|
||||
const auto index = base::take(state->requestingIndex);
|
||||
ShowSelectGiftBox(controller, list, [=](std::optional<Data::SavedStarGift> chosen) {
|
||||
if (chosen) {
|
||||
state->entries[index].gift = chosen->info.unique;
|
||||
state->entries[index].id = chosen->manageId;
|
||||
} else {
|
||||
state->entries[index].gift = nullptr;
|
||||
state->entries[index].id = Data::SavedStarGiftId();
|
||||
}
|
||||
state->changes.fire({});
|
||||
}, (state->entries[index].gift != nullptr));
|
||||
}).send();
|
||||
};
|
||||
const auto refreshButton = [=](int index) {
|
||||
Expects(index >= 0 && index < state->entries.size());
|
||||
|
||||
auto &entry = state->entries[index];
|
||||
const auto single = state->delegate.buttonSize();
|
||||
const auto skip = st::boxTitleClose.width;
|
||||
const auto geometry = QRect(
|
||||
(index % 2) ? (width - skip - single.width()) : skip,
|
||||
(index < 2) ? skip : (height - st::giftBox.buttonPadding.bottom() - st::giftBox.buttonHeight - st::giftBox.buttonPadding.top() - single.height()),
|
||||
single.width(),
|
||||
single.height());
|
||||
delete base::take(entry.add);
|
||||
delete base::take(entry.button);
|
||||
if (entry.gift) {
|
||||
entry.button = CreateChild<GiftButton>(raw, &state->delegate);
|
||||
entry.button->setDescriptor(GiftTypeStars{
|
||||
.info = {
|
||||
.id = entry.gift->initialGiftId,
|
||||
.unique = entry.gift,
|
||||
.document = entry.gift->model.document,
|
||||
},
|
||||
}, GiftButton::Mode::Minimal);
|
||||
entry.button->show();
|
||||
if (index > 0) {
|
||||
entry.button->setClickedCallback([=] {
|
||||
editEntry(index);
|
||||
});
|
||||
} else {
|
||||
entry.button->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
entry.button->setGeometry(
|
||||
geometry,
|
||||
state->delegate.buttonExtend());
|
||||
} else {
|
||||
entry.add = CreateChild<AbstractButton>(raw);
|
||||
entry.add->show();
|
||||
entry.add->paintOn([=](QPainter &p) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto radius = st::boxRadius;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(57, 77, 99));
|
||||
p.drawRoundedRect(
|
||||
QRect(QPoint(), geometry.size()),
|
||||
radius,
|
||||
radius);
|
||||
const auto addSize = geometry.width() / 3;
|
||||
p.setBrush(QColor(255, 255, 255));
|
||||
p.drawEllipse(addSize, (geometry.height() - addSize) / 2, addSize, addSize);
|
||||
|
||||
const auto plusSize = geometry.width() / 6;
|
||||
p.setPen(QPen(QBrush(QColor(57, 77, 99)), plusSize / 5));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawLine((geometry.width() - plusSize) / 2, geometry.height() / 2, (geometry.width() - plusSize) / 2 + plusSize, geometry.height() / 2);
|
||||
p.drawLine(geometry.width() / 2, (geometry.height() - plusSize) / 2, geometry.width() / 2, (geometry.height() - plusSize) / 2 + plusSize);
|
||||
});
|
||||
entry.add->setClickedCallback([=] {
|
||||
editEntry(index);
|
||||
});
|
||||
entry.add->setGeometry(geometry);
|
||||
}
|
||||
};
|
||||
state->changes.events_starting_with_copy(rpl::empty) | rpl::on_next([=] {
|
||||
for (auto i = 0; i != 4; ++i) {
|
||||
refreshButton(i);
|
||||
}
|
||||
}, raw->lifetime());
|
||||
|
||||
const auto setupPattern = [=](
|
||||
PatternView &to,
|
||||
const Data::UniqueGiftPattern &pattern) {
|
||||
const auto document = pattern.document;
|
||||
const auto callback = [=] {
|
||||
if (state->pattern.document == document) {
|
||||
raw->update();
|
||||
}
|
||||
};
|
||||
to.document = document;
|
||||
to.emoji = document->owner().customEmojiManager().create(
|
||||
document,
|
||||
callback,
|
||||
Data::CustomEmojiSizeTag::Large);
|
||||
[[maybe_unused]] const auto preload = to.emoji->ready();
|
||||
};
|
||||
|
||||
setupPattern(state->pattern, gift->pattern);
|
||||
raw->paintOn([=](QPainter &p) {
|
||||
const auto width = raw->width();
|
||||
const auto getBackdrop = [&](BackdropView &backdrop) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto gradientSize = QSize(width, raw->height());
|
||||
auto &gradient = backdrop.gradient;
|
||||
if (gradient.size() != gradientSize * ratio) {
|
||||
gradient = CreateBgGradient(gradientSize, backdrop.colors);
|
||||
}
|
||||
return gradient;
|
||||
};
|
||||
const auto paintPattern = [&](
|
||||
QPainter &p,
|
||||
PatternView &pattern,
|
||||
const BackdropView &backdrop,
|
||||
float64 shown) {
|
||||
const auto color = backdrop.colors.patternColor;
|
||||
const auto key = (color.red() << 16)
|
||||
| (color.green() << 8)
|
||||
| color.blue();
|
||||
PaintBgPoints(
|
||||
p,
|
||||
PatternBgPoints(),
|
||||
pattern.emojis[key],
|
||||
pattern.emoji.get(),
|
||||
color,
|
||||
QRect(0, 0, width, st::uniqueGiftSubtitleTop),
|
||||
shown);
|
||||
};
|
||||
p.drawImage(0, 0, getBackdrop(state->backdrop));
|
||||
paintPattern(p, state->pattern, state->backdrop, 1.);
|
||||
});
|
||||
|
||||
raw->resize(width, height);
|
||||
|
||||
const auto button = CreateChild<RoundButton>(
|
||||
raw,
|
||||
rpl::single(QString()),
|
||||
st::giftBox.button);
|
||||
button->setClickedCallback([=] {
|
||||
if (state->crafting) {
|
||||
return;
|
||||
}
|
||||
state->crafting = true;
|
||||
auto inputs = QVector<MTPInputSavedStarGift>();
|
||||
for (const auto &entry : state->entries) {
|
||||
if (entry.gift) {
|
||||
inputs.push_back(
|
||||
Api::InputSavedStarGiftId(entry.id, entry.gift));
|
||||
}
|
||||
}
|
||||
const auto weak = base::make_weak(controller);
|
||||
session->api().request(MTPpayments_CraftStarGift(
|
||||
MTP_vector<MTPInputSavedStarGift>(inputs)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
session->api().applyUpdates(result);
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showPeerHistory(strong->session().user()->id);
|
||||
}
|
||||
}).send();
|
||||
});
|
||||
raw->sizeValue() | rpl::on_next([=](QSize size) {
|
||||
const auto width = size.width();
|
||||
const auto height = size.height();
|
||||
const auto padding = st::giftBox.buttonPadding;
|
||||
button->setFullWidth(width - padding.left() - padding.right());
|
||||
button->moveToLeft(
|
||||
padding.left(),
|
||||
height - padding.bottom() - button->height());
|
||||
}, raw->lifetime());
|
||||
SetButtonTwoLabels(
|
||||
button,
|
||||
tr::lng_gift_craft_button(
|
||||
lt_gift,
|
||||
rpl::single(tr::marked(Data::UniqueGiftName(*gift))),
|
||||
tr::marked),
|
||||
tr::lng_gift_craft_button_chance(
|
||||
lt_percent,
|
||||
state->successPercentText.value() | rpl::map(tr::marked),
|
||||
tr::marked),
|
||||
st::resaleButtonTitle,
|
||||
st::resaleButtonSubtitle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ShowGiftCraftBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
Data::SavedStarGiftId savedId) {
|
||||
controller->show(Box([=](not_null<GenericBox*> box) {
|
||||
box->setStyle(st::giftCraftBox);
|
||||
box->setWidth(st::boxWidth);
|
||||
box->setNoContentMargin(true);
|
||||
box->addRow(
|
||||
MakeCraftContent(box, controller, gift, savedId), QMargins());
|
||||
AddUniqueCloseButton(box);
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowGiftCraftInfoBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
Data::SavedStarGiftId savedId) {
|
||||
controller->show(Box([=](not_null<GenericBox*> box) {
|
||||
const auto container = box->verticalLayout();
|
||||
auto cover = tr::lng_gift_craft_info_title(
|
||||
) | rpl::map([=](const QString &title) {
|
||||
auto result = UniqueGiftCover{ *gift };
|
||||
result.values.title = title;
|
||||
return result;
|
||||
});
|
||||
AddUniqueGiftCover(container, std::move(cover), {
|
||||
.pretitle = tr::lng_gift_craft_info_level(
|
||||
lt_n,
|
||||
rpl::single(QString::number(2))),
|
||||
.subtitle = tr::lng_gift_craft_info_about(tr::marked),
|
||||
});
|
||||
AddSkip(container);
|
||||
|
||||
AddUniqueCloseButton(box);
|
||||
|
||||
const auto features = std::vector<FeatureListEntry>{
|
||||
{
|
||||
st::menuIconUnique,
|
||||
tr::lng_gift_craft_combine_title(tr::now),
|
||||
tr::lng_gift_craft_combine_about(tr::now, tr::rich),
|
||||
},
|
||||
{
|
||||
st::menuIconTradable,
|
||||
tr::lng_gift_craft_input_title(tr::now),
|
||||
tr::lng_gift_craft_input_about(tr::now, tr::marked),
|
||||
},
|
||||
{
|
||||
st::menuIconNftWear,
|
||||
tr::lng_gift_craft_exclusive_title(tr::now),
|
||||
tr::lng_gift_craft_exclusive_about(tr::now, tr::marked),
|
||||
},
|
||||
};
|
||||
for (const auto &feature : features) {
|
||||
container->add(
|
||||
MakeFeatureListEntry(container, feature),
|
||||
st::boxRowPadding);
|
||||
}
|
||||
|
||||
box->setStyle(st::giftBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
box->addButton(tr::lng_gift_craft_start_button(), [=] {
|
||||
ShowGiftCraftBox(controller, gift, savedId);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Data {
|
||||
struct UniqueGift;
|
||||
class SavedStarGiftId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void ShowGiftCraftInfoBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
Data::SavedStarGiftId savedId);
|
||||
|
||||
} // namespace Ui
|
||||
@@ -203,6 +203,7 @@ struct GiftCode {
|
||||
bool refunded : 1 = false;
|
||||
bool upgrade : 1 = false;
|
||||
bool saved : 1 = false;
|
||||
bool craft : 1 = false;
|
||||
};
|
||||
|
||||
class Media {
|
||||
|
||||
@@ -97,15 +97,18 @@ struct UniqueGift {
|
||||
PeerData *releasedBy = nullptr;
|
||||
PeerData *themeUser = nullptr;
|
||||
int64 nanoTonForResale = -1;
|
||||
int craftChancePermille = 0;
|
||||
int starsForResale = -1;
|
||||
int starsForTransfer = -1;
|
||||
int starsMinOffer = -1;
|
||||
int number = 0;
|
||||
bool onlyAcceptTon = false;
|
||||
bool canBeTheme = false;
|
||||
bool crafted = false;
|
||||
TimeId exportAt = 0;
|
||||
TimeId canTransferAt = 0;
|
||||
TimeId canResellAt = 0;
|
||||
TimeId canCraftAt = 0;
|
||||
UniqueGiftModel model;
|
||||
UniqueGiftPattern pattern;
|
||||
UniqueGiftBackdrop backdrop;
|
||||
|
||||
@@ -6925,6 +6925,7 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
||||
.refunded = data.is_refunded(),
|
||||
.upgrade = data.is_upgrade(),
|
||||
.saved = data.is_saved(),
|
||||
.craft = data.is_craft(),
|
||||
};
|
||||
if (auto gift = Api::FromTL(&history()->session(), data.vgift())) {
|
||||
fields.stargiftId = gift->id;
|
||||
@@ -6940,6 +6941,9 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
||||
unique->exportAt = data.vcan_export_at().value_or_empty();
|
||||
unique->canTransferAt = data.vcan_transfer_at().value_or_empty();
|
||||
unique->canResellAt = data.vcan_resell_at().value_or_empty();
|
||||
unique->canCraftAt = data.vcan_craft_at().value_or_empty();
|
||||
unique->craftChancePermille
|
||||
= data.vcraft_chance_permille().value_or_empty();
|
||||
}
|
||||
}
|
||||
_media = std::make_unique<Data::MediaGiftBox>(
|
||||
|
||||
@@ -1955,9 +1955,9 @@ botVerifierSettings#b0cd6617 flags:# can_modify_custom_description:flags.1?true
|
||||
|
||||
botVerification#f93cd45c bot_id:long icon:long description:string = BotVerification;
|
||||
|
||||
starGiftAttributeModel#39d99013 name:string document:Document rarity_permille:int = StarGiftAttribute;
|
||||
starGiftAttributePattern#13acff19 name:string document:Document rarity_permille:int = StarGiftAttribute;
|
||||
starGiftAttributeBackdrop#d93d859c name:string backdrop_id:int center_color:int edge_color:int pattern_color:int text_color:int rarity_permille:int = StarGiftAttribute;
|
||||
starGiftAttributeModel#565251e2 flags:# crafted:flags.0?true name:string document:Document rarity:StarGiftAttributeRarity = StarGiftAttribute;
|
||||
starGiftAttributePattern#4e7085ea name:string document:Document rarity:StarGiftAttributeRarity = StarGiftAttribute;
|
||||
starGiftAttributeBackdrop#9f2504e4 name:string backdrop_id:int center_color:int edge_color:int pattern_color:int text_color:int rarity:StarGiftAttributeRarity = StarGiftAttribute;
|
||||
starGiftAttributeOriginalDetails#e0bff26c flags:# sender_id:flags.0?Peer recipient_id:Peer date:int message:flags.1?TextWithEntities = StarGiftAttribute;
|
||||
|
||||
payments.starGiftUpgradePreview#3de1dfed sample_attributes:Vector<StarGiftAttribute> prices:Vector<StarGiftUpgradePrice> next_prices:Vector<StarGiftUpgradePrice> = payments.StarGiftUpgradePreview;
|
||||
@@ -2114,6 +2114,12 @@ messages.emojiGameOutcome#da2ad647 seed:bytes stake_ton_amount:long ton_amount:l
|
||||
messages.emojiGameUnavailable#59e65335 = messages.EmojiGameInfo;
|
||||
messages.emojiGameDiceInfo#44e56023 flags:# game_hash:string prev_stake:long current_streak:int params:Vector<int> plays_left:flags.0?int = messages.EmojiGameInfo;
|
||||
|
||||
starGiftAttributeRarity#36437737 permille:int = StarGiftAttributeRarity;
|
||||
starGiftAttributeRarityRare#f08d516b = StarGiftAttributeRarity;
|
||||
starGiftAttributeRarityEpic#78fbf3a8 = StarGiftAttributeRarity;
|
||||
starGiftAttributeRarityLegendary#cef7e7a8 = StarGiftAttributeRarity;
|
||||
starGiftAttributeRarityMythic#f8374495 = StarGiftAttributeRarity;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
|
||||
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/gift_premium_box.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "boxes/star_gift_craft_box.h"
|
||||
#include "boxes/star_gift_resale_box.h"
|
||||
#include "boxes/transfer_gift_box.h"
|
||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||
@@ -963,6 +964,25 @@ void ProcessReceivedSubscriptions(
|
||||
// (owner->isChannel() && owner->asChannel()->canTransferGifts());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CanCraftGift(
|
||||
not_null<Main::Session*> session,
|
||||
const Data::CreditsHistoryEntry &e) {
|
||||
const auto unique = e.uniqueGift.get();
|
||||
if (!unique || !unique->craftChancePermille) {
|
||||
return false;
|
||||
}
|
||||
const auto owner = (unique && unique->ownerId)
|
||||
? session->data().peer(unique->ownerId).get()
|
||||
: nullptr;
|
||||
return !owner
|
||||
? false
|
||||
: owner->isSelf()
|
||||
? e.in
|
||||
: false;
|
||||
// Currently we're not crafting channel gifts.
|
||||
// (owner->isChannel() && owner->asChannel()->canTransferGifts());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ShowOfferBuyButton(
|
||||
not_null<Main::Session*> session,
|
||||
const Data::CreditsHistoryEntry &e) {
|
||||
@@ -1121,6 +1141,14 @@ void FillUniqueGiftMenu(
|
||||
}
|
||||
}, st.transfer ? st.transfer : &st::menuIconReplace);
|
||||
}
|
||||
if (CanCraftGift(&show->session(), e)) {
|
||||
AssertIsDebug(icon and if we even put this here);
|
||||
menu->addAction(tr::lng_gift_craft_title(tr::now), [=] {
|
||||
if (const auto window = show->resolveWindow()) {
|
||||
Ui::ShowGiftCraftInfoBox(window, unique, savedId);
|
||||
}
|
||||
}, st.transfer ? st.transfer : &st::menuIconReplace);
|
||||
}
|
||||
const auto wear = host->isSelf()
|
||||
? e.in
|
||||
: (host->isChannel() && host->asChannel()->canEditEmoji());
|
||||
|
||||
@@ -122,6 +122,7 @@ struct CreditsEntryBoxStyleOverrides {
|
||||
const style::icon *share = nullptr;
|
||||
const style::icon *theme = nullptr;
|
||||
const style::icon *transfer = nullptr;
|
||||
const style::icon *craft = nullptr;
|
||||
const style::icon *wear = nullptr;
|
||||
const style::icon *takeoff = nullptr;
|
||||
const style::icon *resell = nullptr;
|
||||
|
||||
@@ -139,6 +139,10 @@ giftBox: Box(defaultBox) {
|
||||
giftLimitedBox: Box(giftBox) {
|
||||
buttonPadding: margins(22px, 48px, 22px, 12px);
|
||||
}
|
||||
giftCraftBox: Box(giftBox) {
|
||||
buttonPadding: margins(0px, 0px, 0px, 0px);
|
||||
buttonHeight: 0px;
|
||||
}
|
||||
giftLimitedPadding: margins(8px, 4px, 8px, 4px);
|
||||
giftBoxSubtitle: FlatLabel(defaultFlatLabel) {
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
|
||||
Reference in New Issue
Block a user