/* 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_credits_box.h" #include "api/api_credits.h" #include "api/api_premium.h" #include "boxes/peer_list_controllers.h" #include "data/data_peer.h" #include "data/data_session.h" #include "data/data_user.h" #include "data/stickers/data_custom_emoji.h" #include "lang/lang_keys.h" #include "main/session/session_show.h" #include "main/main_session.h" #include "settings/settings_credits_graphics.h" #include "settings/settings_credits.h" #include "settings/settings_premium.h" #include "ui/controls/userpic_button.h" #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_stars_colored.h" #include "ui/effects/ripple_animation.h" #include "ui/layers/generic_box.h" #include "ui/painter.h" #include "ui/rect.h" #include "ui/text/text_utilities.h" #include "ui/vertical_list.h" #include "ui/widgets/label_with_custom_emoji.h" #include "window/window_session_controller.h" #include "styles/style_boxes.h" #include "styles/style_channel_earn.h" #include "styles/style_chat.h" #include "styles/style_credits.h" #include "styles/style_giveaway.h" #include "styles/style_layers.h" #include "styles/style_premium.h" #include "data/stickers/data_stickers.h" #include "data/data_document.h" namespace Ui { namespace { constexpr auto kPriceTabAll = 0; constexpr auto kPriceTabLimited = -1; struct GiftTypePremium { int64 cost = 0; QString currency; int months = 0; int discountPercent = 0; [[nodiscard]] friend inline bool operator==( const GiftTypePremium &, const GiftTypePremium &) = default; }; struct GiftTypeStars { DocumentData *document = nullptr; int stars = 0; bool limited = false; [[nodiscard]] friend inline bool operator==( const GiftTypeStars&, const GiftTypeStars&) = default; }; struct GiftDescriptor : std::variant { using variant::variant; [[nodiscard]] friend inline bool operator==( const GiftDescriptor&, const GiftDescriptor&) = default; }; [[nodiscard]] rpl::producer> GiftsPremium( not_null session, not_null peer) { struct Session { std::vector last; }; static auto Map = base::flat_map, Session>(); return [=](auto consumer) { auto lifetime = rpl::lifetime(); auto i = Map.find(session); if (i == end(Map)) { i = Map.emplace(session, Session()).first; session->lifetime().add([=] { Map.remove(session); }); } if (!i->second.last.empty()) { consumer.put_next_copy(i->second.last); } using namespace Api; const auto api = lifetime.make_state(peer); api->request() | rpl::start_with_error_done([=](QString error) { consumer.put_next({}); }, [=] { const auto &options = api->optionsForPeer(); auto list = std::vector(); list.reserve(options.size()); auto minMonthsGift = GiftTypePremium(); for (const auto &option : options) { list.push_back({ .cost = option.cost, .currency = option.currency, .months = option.months, }); if (!minMonthsGift.months || option.months < minMonthsGift.months) { minMonthsGift = list.back(); } } for (auto &gift : list) { if (gift.months > minMonthsGift.months && gift.currency == minMonthsGift.currency) { const auto costPerMonth = gift.cost / gift.months; const auto maxCostPerMonth = minMonthsGift.cost / minMonthsGift.months; const auto costRatio = costPerMonth / maxCostPerMonth; const auto discount = 1. - costRatio; const auto discountPercent = 100 * discount; const auto value = int(base::SafeRound(discountPercent)); if (value > 0 && value < 100) { gift.discountPercent = value; } } } Map[session].last = list; consumer.put_next_copy(list); }, lifetime); return lifetime; }; } [[nodiscard]] rpl::producer> GiftsStars( not_null session, not_null peer) { //struct Session { // std::vector last; //}; //static auto Map = base::flat_map, Session>(); return [=](auto consumer) { auto lifetime = rpl::lifetime(); auto list = std::vector(); const auto add = [&](uint64 setId, int price, bool limited) { auto &sets = session->data().stickers().setsRef(); const auto i = sets.find(setId); if (i != end(sets)) { for (const auto document : i->second->stickers) { price = document->isPremiumSticker() ? 1000 : price; list.push_back({ .document = document, .stars = price, .limited = limited, }); } } }; add(Data::Stickers::CloudRecentSetId, 100, false); add(Data::Stickers::RecentSetId, 250, false); add(Data::Stickers::FavedSetId, 50, true); consumer.put_next(std::move(list)); return lifetime; }; } [[nodiscard]] Text::String TabTextForPrice( not_null session, int price) { const auto simple = [](const QString &text) { return Text::String(st::semiboldTextStyle, text); }; if (price == kPriceTabAll) { return simple(tr::lng_gift_stars_tabs_all(tr::now)); } else if (price == kPriceTabLimited) { return simple(tr::lng_gift_stars_tabs_limited(tr::now)); } auto &manager = session->data().customEmojiManager(); auto result = Text::String(); const auto context = Core::MarkedTextContext{ .session = session, .customEmojiRepaint = [] {}, }; result.setMarkedText( st::semiboldTextStyle, manager.creditsEmoji().append(QString::number(price)), kMarkupTextOptions, context); return result; } struct GiftPriceTabs { rpl::producer priceTab; object_ptr widget; }; [[nodiscard]] GiftPriceTabs MakeGiftsPriceTabs( not_null window, not_null peer, rpl::producer> gifts) { auto widget = object_ptr((QWidget*)nullptr); const auto raw = widget.data(); struct Button { QRect geometry; Text::String text; int price = 0; bool active = false; }; struct State { rpl::variable> prices; rpl::variable priceTab = kPriceTabAll; std::vector