mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-06 15:13:57 +02:00
Start UI of the gifts.
This commit is contained in:
parent
761617c1ce
commit
4cdd1fec95
2 changed files with 186 additions and 7 deletions
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "history/view/media/history_view_sticker_player.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/session/session_show.h"
|
#include "main/session/session_show.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -27,9 +28,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
#include "ui/widgets/label_with_custom_emoji.h"
|
#include "ui/widgets/label_with_custom_emoji.h"
|
||||||
|
#include "ui/widgets/shadow.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_channel_earn.h"
|
#include "styles/style_channel_earn.h"
|
||||||
|
@ -47,6 +50,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kPriceTabAll = 0;
|
constexpr auto kPriceTabAll = 0;
|
||||||
constexpr auto kPriceTabLimited = -1;
|
constexpr auto kPriceTabLimited = -1;
|
||||||
|
constexpr auto kGiftsPerRow = 3;
|
||||||
|
|
||||||
struct GiftTypePremium {
|
struct GiftTypePremium {
|
||||||
int64 cost = 0;
|
int64 cost = 0;
|
||||||
|
@ -119,9 +123,9 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
|
||||||
for (auto &gift : list) {
|
for (auto &gift : list) {
|
||||||
if (gift.months > minMonthsGift.months
|
if (gift.months > minMonthsGift.months
|
||||||
&& gift.currency == minMonthsGift.currency) {
|
&& gift.currency == minMonthsGift.currency) {
|
||||||
const auto costPerMonth = gift.cost / gift.months;
|
const auto costPerMonth = gift.cost / (1. * gift.months);
|
||||||
const auto maxCostPerMonth = minMonthsGift.cost
|
const auto maxCostPerMonth = minMonthsGift.cost
|
||||||
/ minMonthsGift.months;
|
/ (1. * minMonthsGift.months);
|
||||||
const auto costRatio = costPerMonth / maxCostPerMonth;
|
const auto costRatio = costPerMonth / maxCostPerMonth;
|
||||||
const auto discount = 1. - costRatio;
|
const auto discount = 1. - costRatio;
|
||||||
const auto discountPercent = 100 * discount;
|
const auto discountPercent = 100 * discount;
|
||||||
|
@ -131,6 +135,7 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ranges::sort(list, ranges::less(), &GiftTypePremium::months);
|
||||||
Map[session].last = list;
|
Map[session].last = list;
|
||||||
consumer.put_next_copy(list);
|
consumer.put_next_copy(list);
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
|
@ -385,8 +390,174 @@ struct GiftPriceTabs {
|
||||||
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
||||||
const auto raw = result.data();
|
const auto raw = result.data();
|
||||||
|
|
||||||
raw->paintRequest() | rpl::start_with_next([=] {
|
struct Button {
|
||||||
|
GiftDescriptor descriptor;
|
||||||
|
Text::String text;
|
||||||
|
Text::String price;
|
||||||
|
QRect geometry;
|
||||||
|
QRect button;
|
||||||
|
std::unique_ptr<HistoryView::StickerPlayer> player;
|
||||||
|
};
|
||||||
|
struct State {
|
||||||
|
std::vector<Button> buttons;
|
||||||
|
QPoint bgShift;
|
||||||
|
QImage bg;
|
||||||
|
};
|
||||||
|
const auto state = raw->lifetime().make_state<State>();
|
||||||
|
|
||||||
|
const auto width = st::boxWideWidth;
|
||||||
|
const auto padding = st::giftBoxPadding;
|
||||||
|
const auto available = width - padding.left() - padding.right();
|
||||||
|
const auto singlew = (available - 2 * st::giftBoxGiftSkip.x())
|
||||||
|
/ kGiftsPerRow;
|
||||||
|
const auto shadow = st::defaultDropdownMenu.wrap.shadow;
|
||||||
|
const auto extend = shadow.extend;
|
||||||
|
|
||||||
|
state->bgShift = -QPoint(extend.left(), extend.top());
|
||||||
|
const auto single = QSize(singlew, st::giftBoxGiftHeight);
|
||||||
|
const auto bgSize = QRect(QPoint(), single ).marginsAdded(extend).size();
|
||||||
|
state->bg = QImage(
|
||||||
|
bgSize * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
state->bg.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
state->bg.fill(Qt::transparent);
|
||||||
|
|
||||||
|
auto p = QPainter(&state->bg);
|
||||||
|
const auto rect = QRect(QPoint(), bgSize).marginsRemoved(extend);
|
||||||
|
Shadow::paint(p, rect, bgSize.width(), shadow);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(st::windowBg);
|
||||||
|
const auto radius = st::roundRadiusSmall;
|
||||||
|
p.drawRoundedRect(rect, radius, radius);
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
gifts
|
||||||
|
) | rpl::start_with_next([=](const std::vector<GiftDescriptor> &gifts) {
|
||||||
|
const auto context = Core::MarkedTextContext{
|
||||||
|
.session = &peer->session(),
|
||||||
|
.customEmojiRepaint = [] {},
|
||||||
|
};
|
||||||
|
const auto star = [&] {
|
||||||
|
return peer->owner().customEmojiManager().creditsEmoji();
|
||||||
|
};
|
||||||
|
auto x = padding.left();
|
||||||
|
auto y = padding.top();
|
||||||
|
state->buttons.resize(gifts.size());
|
||||||
|
for (auto i = 0, count = int(gifts.size()); i != count; ++i) {
|
||||||
|
auto &button = state->buttons[i];
|
||||||
|
const auto &descriptor = gifts[i];
|
||||||
|
if (button.descriptor != descriptor) {
|
||||||
|
button.descriptor = descriptor;
|
||||||
|
v::match(descriptor, [&](const GiftTypePremium &data) {
|
||||||
|
const auto months = data.months;
|
||||||
|
const auto years = (months % 12) ? 0 : months / 12;
|
||||||
|
button.text = Text::String(singlew / 2);
|
||||||
|
button.text.setMarkedText(
|
||||||
|
st::defaultTextStyle,
|
||||||
|
Text::Bold(years
|
||||||
|
? tr::lng_years(tr::now, lt_count, years)
|
||||||
|
: tr::lng_months(tr::now, lt_count, months)
|
||||||
|
).append('\n').append(
|
||||||
|
tr::lng_gift_premium_label(tr::now)
|
||||||
|
));
|
||||||
|
button.price.setText(
|
||||||
|
st::semiboldTextStyle,
|
||||||
|
FillAmountAndCurrency(
|
||||||
|
data.cost,
|
||||||
|
data.currency,
|
||||||
|
true));
|
||||||
|
}, [&](const GiftTypeStars &data) {
|
||||||
|
button.price.setMarkedText(
|
||||||
|
st::semiboldTextStyle,
|
||||||
|
star().append(QString::number(data.stars)),
|
||||||
|
kMarkupTextOptions,
|
||||||
|
context);
|
||||||
|
});
|
||||||
|
const auto buttonw = button.price.maxWidth();
|
||||||
|
const auto buttonh = st::semiboldFont->height;
|
||||||
|
const auto inner = QRect(
|
||||||
|
QPoint(),
|
||||||
|
QSize(buttonw, buttonh)
|
||||||
|
).marginsAdded(st::giftBoxButtonPadding);
|
||||||
|
const auto skipx = (singlew - inner.width()) / 2;
|
||||||
|
const auto skipy = single.height()
|
||||||
|
- st::giftBoxButtonBottom
|
||||||
|
- inner.height();
|
||||||
|
const auto outer = (singlew - 2 * skipx);
|
||||||
|
button.button = QRect(skipx, skipy, outer, inner.height());
|
||||||
|
}
|
||||||
|
const auto last = !((i + 1) % kGiftsPerRow);
|
||||||
|
if (last) {
|
||||||
|
x = padding.left() + available - single.width();
|
||||||
|
}
|
||||||
|
button.geometry = QRect(QPoint(x, y), single);
|
||||||
|
if (last) {
|
||||||
|
x = padding.left();
|
||||||
|
y += single.height() + st::giftBoxGiftSkip.y();
|
||||||
|
} else {
|
||||||
|
x += single.width() + st::giftBoxGiftSkip.x();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gifts.size() % kGiftsPerRow) {
|
||||||
|
y += padding.bottom() + single.height();
|
||||||
|
} else {
|
||||||
|
y += padding.bottom() - st::giftBoxGiftSkip.y();
|
||||||
|
}
|
||||||
|
raw->resize(raw->width(), gifts.empty() ? 0 : y);
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
raw->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(raw);
|
||||||
|
for (const auto &button : state->buttons) {
|
||||||
|
const auto position = button.geometry.topLeft();
|
||||||
|
p.drawImage(position + state->bgShift, state->bg);
|
||||||
|
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
const auto premium = v::is<GiftTypePremium>(button.descriptor);
|
||||||
|
p.setFont(st::normalFont);
|
||||||
|
v::match(button.descriptor, [&](const GiftTypePremium &data) {
|
||||||
|
if (data.discountPercent > 0) {
|
||||||
|
p.setPen(st::attentionBoxButton.textFg);
|
||||||
|
p.drawText(QRect(position, QSize(singlew, st::normalFont->height * 2)), '-' + QString::number(data.discountPercent) + '%', style::al_center);
|
||||||
|
}
|
||||||
|
}, [&](const GiftTypeStars &data) {
|
||||||
|
if (data.limited) {
|
||||||
|
p.setPen(st::windowActiveTextFg);
|
||||||
|
p.drawText(QRect(position, QSize(singlew, st::normalFont->height * 2)), u"limited"_q, style::al_center);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
p.setBrush(premium ? st::lightButtonBgOver : st::creditsBg3);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
if (!premium) {
|
||||||
|
p.setOpacity(0.12);
|
||||||
|
}
|
||||||
|
const auto geometry = button.button.translated(position);
|
||||||
|
const auto radius = geometry.height() / 2.;
|
||||||
|
p.drawRoundedRect(geometry, radius, radius);
|
||||||
|
if (!premium) {
|
||||||
|
p.setOpacity(1.);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!button.text.isEmpty()) {
|
||||||
|
p.setPen(st::windowFg);
|
||||||
|
button.text.draw(p, {
|
||||||
|
.position = (position
|
||||||
|
+ QPoint(0, st::giftBoxPremiumTextTop)),
|
||||||
|
.availableWidth = singlew,
|
||||||
|
.align = style::al_top,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto padding = st::giftBoxButtonPadding;
|
||||||
|
p.setPen(premium ? st::windowActiveTextFg : st::creditsFg);
|
||||||
|
button.price.draw(p, {
|
||||||
|
.position = (geometry.topLeft()
|
||||||
|
+ QPoint(padding.left(), padding.top())),
|
||||||
|
.availableWidth = button.price.maxWidth(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -437,19 +608,19 @@ void AddBlock(
|
||||||
[[nodiscard]] object_ptr<RpWidget> MakePremiumGifts(
|
[[nodiscard]] object_ptr<RpWidget> MakePremiumGifts(
|
||||||
not_null<Window::SessionController*> window,
|
not_null<Window::SessionController*> window,
|
||||||
not_null<PeerData*> peer) {
|
not_null<PeerData*> peer) {
|
||||||
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<std::vector<GiftDescriptor>> gifts;
|
rpl::variable<std::vector<GiftDescriptor>> gifts;
|
||||||
};
|
};
|
||||||
const auto state = std::make_unique<State>();
|
auto state = std::make_unique<State>();
|
||||||
|
|
||||||
state->gifts = GiftsPremium(&window->session(), peer) | rpl::map([=](
|
state->gifts = GiftsPremium(&window->session(), peer) | rpl::map([=](
|
||||||
const std::vector<GiftTypePremium> &gifts) {
|
const std::vector<GiftTypePremium> &gifts) {
|
||||||
return gifts | ranges::to<std::vector<GiftDescriptor>>;
|
return gifts | ranges::to<std::vector<GiftDescriptor>>;
|
||||||
});
|
});
|
||||||
|
|
||||||
return MakeGiftsList(window, peer, state->gifts.value());
|
auto result = MakeGiftsList(window, peer, state->gifts.value());
|
||||||
|
result->lifetime().add([state = std::move(state)] {});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<RpWidget> MakeStarsGifts(
|
[[nodiscard]] object_ptr<RpWidget> MakeStarsGifts(
|
||||||
|
|
|
@ -79,3 +79,11 @@ giftBoxTabStyle: semiboldTextStyle;
|
||||||
giftBoxTabFg: windowSubTextFg;
|
giftBoxTabFg: windowSubTextFg;
|
||||||
giftBoxTabFgActive: windowBoldFg;
|
giftBoxTabFgActive: windowBoldFg;
|
||||||
giftBoxTabBgActive: windowBgRipple;
|
giftBoxTabBgActive: windowBgRipple;
|
||||||
|
giftBoxPadding: margins(20px, 4px, 20px, 24px);
|
||||||
|
giftBoxGiftSkip: point(10px, 8px);
|
||||||
|
giftBoxGiftHeight: 136px;
|
||||||
|
giftBoxPremiumIconSize: 64px;
|
||||||
|
giftBoxPremiumIconTop: 10px;
|
||||||
|
giftBoxPremiumTextTop: 56px;
|
||||||
|
giftBoxButtonBottom: 12px;
|
||||||
|
giftBoxButtonPadding: margins(8px, 4px, 8px, 4px);
|
||||||
|
|
Loading…
Add table
Reference in a new issue