mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add top reactors to paid reaction details.
This commit is contained in:
parent
9bb1fa8782
commit
afe30da9f4
14 changed files with 301 additions and 58 deletions
|
@ -2042,6 +2042,11 @@ auto MessageReactions::recent() const
|
||||||
return _recent;
|
return _recent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto MessageReactions::topPaid() const -> const std::vector<TopPaid> & {
|
||||||
|
static const auto kEmpty = std::vector<TopPaid>();
|
||||||
|
return _paid ? _paid->top : kEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
bool MessageReactions::empty() const {
|
bool MessageReactions::empty() const {
|
||||||
return _list.empty();
|
return _list.empty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -379,6 +379,7 @@ public:
|
||||||
[[nodiscard]] const std::vector<MessageReaction> &list() const;
|
[[nodiscard]] const std::vector<MessageReaction> &list() const;
|
||||||
[[nodiscard]] auto recent() const
|
[[nodiscard]] auto recent() const
|
||||||
-> const base::flat_map<ReactionId, std::vector<RecentReaction>> &;
|
-> const base::flat_map<ReactionId, std::vector<RecentReaction>> &;
|
||||||
|
[[nodiscard]] const std::vector<TopPaid> &topPaid() const;
|
||||||
[[nodiscard]] std::vector<ReactionId> chosen() const;
|
[[nodiscard]] std::vector<ReactionId> chosen() const;
|
||||||
[[nodiscard]] bool empty() const;
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
||||||
|
|
|
@ -2573,11 +2573,11 @@ const std::vector<Data::MessageReaction> &HistoryItem::reactions() const {
|
||||||
|
|
||||||
std::vector<Data::MessageReaction> HistoryItem::reactionsWithLocal() const {
|
std::vector<Data::MessageReaction> HistoryItem::reactionsWithLocal() const {
|
||||||
auto result = reactions();
|
auto result = reactions();
|
||||||
|
const auto i = ranges::find(
|
||||||
|
result,
|
||||||
|
Data::ReactionId::Paid(),
|
||||||
|
&Data::MessageReaction::id);
|
||||||
if (const auto local = _reactions ? _reactions->localPaidCount() : 0) {
|
if (const auto local = _reactions ? _reactions->localPaidCount() : 0) {
|
||||||
const auto i = ranges::find(
|
|
||||||
result,
|
|
||||||
Data::ReactionId::Paid(),
|
|
||||||
&Data::MessageReaction::id);
|
|
||||||
if (i != end(result)) {
|
if (i != end(result)) {
|
||||||
i->my = true;
|
i->my = true;
|
||||||
i->count += local;
|
i->count += local;
|
||||||
|
@ -2591,10 +2591,16 @@ std::vector<Data::MessageReaction> HistoryItem::reactionsWithLocal() const {
|
||||||
.my = true,
|
.my = true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (i != end(result) && i != begin(result)) {
|
||||||
|
std::rotate(begin(result), i, i + 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HistoryItem::reactionsPaidScheduled() const {
|
||||||
|
return _reactions ? _reactions->scheduledPaid() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryItem::reactionsAreTags() const {
|
bool HistoryItem::reactionsAreTags() const {
|
||||||
return _flags & MessageFlag::ReactionsAreTags;
|
return _flags & MessageFlag::ReactionsAreTags;
|
||||||
}
|
}
|
||||||
|
@ -2609,6 +2615,12 @@ auto HistoryItem::recentReactions() const
|
||||||
return _reactions ? _reactions->recent() : kEmpty;
|
return _reactions ? _reactions->recent() : kEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto HistoryItem::topPaidReactions() const
|
||||||
|
-> const std::vector<Data::MessageReactionsTopPaid> & {
|
||||||
|
static const auto kEmpty = std::vector<Data::MessageReactionsTopPaid>();
|
||||||
|
return _reactions ? _reactions->topPaid() : kEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryItem::canViewReactions() const {
|
bool HistoryItem::canViewReactions() const {
|
||||||
return (_flags & MessageFlag::CanViewReactions)
|
return (_flags & MessageFlag::CanViewReactions)
|
||||||
&& _reactions
|
&& _reactions
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct RippleAnimation;
|
||||||
namespace Data {
|
namespace Data {
|
||||||
struct MessagePosition;
|
struct MessagePosition;
|
||||||
struct RecentReaction;
|
struct RecentReaction;
|
||||||
|
struct MessageReactionsTopPaid;
|
||||||
struct ReactionId;
|
struct ReactionId;
|
||||||
class Media;
|
class Media;
|
||||||
struct MessageReaction;
|
struct MessageReaction;
|
||||||
|
@ -456,6 +457,9 @@ public:
|
||||||
-> const base::flat_map<
|
-> const base::flat_map<
|
||||||
Data::ReactionId,
|
Data::ReactionId,
|
||||||
std::vector<Data::RecentReaction>> &;
|
std::vector<Data::RecentReaction>> &;
|
||||||
|
[[nodiscard]] auto topPaidReactions() const
|
||||||
|
-> const std::vector<Data::MessageReactionsTopPaid> &;
|
||||||
|
[[nodiscard]] int reactionsPaidScheduled() const;
|
||||||
[[nodiscard]] bool canViewReactions() const;
|
[[nodiscard]] bool canViewReactions() const;
|
||||||
[[nodiscard]] std::vector<Data::ReactionId> chosenReactions() const;
|
[[nodiscard]] std::vector<Data::ReactionId> chosenReactions() const;
|
||||||
[[nodiscard]] Data::ReactionId lookupUnreadReaction(
|
[[nodiscard]] Data::ReactionId lookupUnreadReaction(
|
||||||
|
|
|
@ -55,6 +55,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
#include "data/business/data_shortcut_messages.h"
|
#include "data/business/data_shortcut_messages.h"
|
||||||
|
#include "data/components/credits.h"
|
||||||
#include "data/components/scheduled_messages.h"
|
#include "data/components/scheduled_messages.h"
|
||||||
#include "data/components/sponsored_messages.h"
|
#include "data/components/sponsored_messages.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
|
@ -869,6 +870,11 @@ HistoryWidget::HistoryWidget(
|
||||||
}
|
}
|
||||||
if (flags & PeerUpdateFlag::FullInfo) {
|
if (flags & PeerUpdateFlag::FullInfo) {
|
||||||
fullInfoUpdated();
|
fullInfoUpdated();
|
||||||
|
if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||||
|
if (channel->allowedReactions().paidEnabled) {
|
||||||
|
session().credits().load();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct InlineList::Button {
|
||||||
int textWidth = 0;
|
int textWidth = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
bool chosen = false;
|
bool chosen = false;
|
||||||
|
bool paid = false;
|
||||||
bool tag = false;
|
bool tag = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,7 +181,7 @@ void InlineList::layoutButtons() {
|
||||||
}
|
}
|
||||||
|
|
||||||
InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
||||||
auto result = Button{ .id = id };
|
auto result = Button{ .id = id, .paid = id.paid()};
|
||||||
if (const auto customId = id.custom()) {
|
if (const auto customId = id.custom()) {
|
||||||
result.custom = _owner->owner().customEmojiManager().create(
|
result.custom = _owner->owner().customEmojiManager().create(
|
||||||
customId,
|
customId,
|
||||||
|
@ -421,14 +422,18 @@ void InlineList::paint(
|
||||||
} else if (!bubbleReady) {
|
} else if (!bubbleReady) {
|
||||||
opacity = bubbleProgress;
|
opacity = bubbleProgress;
|
||||||
}
|
}
|
||||||
color = stm->msgFileBg->c;
|
color = button.paid
|
||||||
|
? st->creditsBg3()->c
|
||||||
|
: stm->msgFileBg->c;
|
||||||
} else {
|
} else {
|
||||||
if (!bubbleReady) {
|
if (!bubbleReady) {
|
||||||
opacity = bubbleProgress;
|
opacity = bubbleProgress;
|
||||||
}
|
}
|
||||||
color = (chosen
|
color = (!chosen
|
||||||
? st->msgServiceFg()
|
? st->msgServiceBg()
|
||||||
: st->msgServiceBg())->c;
|
: button.paid
|
||||||
|
? st->creditsBg2()
|
||||||
|
: st->msgServiceFg())->c;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fill = geometry.marginsAdded({
|
const auto fill = geometry.marginsAdded({
|
||||||
|
@ -451,7 +456,7 @@ void InlineList::paint(
|
||||||
? QPen(AdaptChosenServiceFg(st->msgServiceBg()->c))
|
? QPen(AdaptChosenServiceFg(st->msgServiceBg()->c))
|
||||||
: st->msgServiceFg())
|
: st->msgServiceFg())
|
||||||
: !chosen
|
: !chosen
|
||||||
? stm->msgServiceFg
|
? (button.paid ? st->creditsFg() : stm->msgServiceFg)
|
||||||
: context.outbg
|
: context.outbg
|
||||||
? (context.selected()
|
? (context.selected()
|
||||||
? st->historyFileOutIconFgSelected()
|
? st->historyFileOutIconFgSelected()
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/send_credits_box.h" // CreditsEmojiSmall.
|
#include "boxes/send_credits_box.h" // CreditsEmojiSmall.
|
||||||
#include "core/ui_integration.h" // MarkedTextContext.
|
#include "core/ui_integration.h" // MarkedTextContext.
|
||||||
#include "data/components/credits.h"
|
#include "data/components/credits.h"
|
||||||
|
#include "data/data_message_reactions.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
@ -27,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/layers/show.h"
|
#include "ui/layers/show.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/dynamic_thumbnails.h"
|
||||||
|
|
||||||
namespace Payments {
|
namespace Payments {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -167,10 +169,26 @@ void ShowPaidReactionDetails(
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
auto top = std::vector<Ui::PaidReactionTop>();
|
||||||
|
const auto &topPaid = item->topPaidReactions();
|
||||||
|
top.reserve(topPaid.size());
|
||||||
|
for (const auto &entry : topPaid) {
|
||||||
|
if (!entry.top) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
top.push_back({
|
||||||
|
.name = entry.peer->shortName(),
|
||||||
|
.photo = Ui::MakeUserpicThumbnail(entry.peer),
|
||||||
|
.count = int(entry.count),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ranges::sort(top, ranges::greater(), &Ui::PaidReactionTop::count);
|
||||||
|
|
||||||
state->selectBox = show->show(Ui::MakePaidReactionBox({
|
state->selectBox = show->show(Ui::MakePaidReactionBox({
|
||||||
.min = min,
|
.min = min,
|
||||||
.max = max,
|
.max = max,
|
||||||
.chosen = chosen,
|
.chosen = chosen,
|
||||||
|
.top = std::move(top),
|
||||||
.channel = item->history()->peer->name(),
|
.channel = item->history()->peer->name(),
|
||||||
.submit = std::move(submitText),
|
.submit = std::move(submitText),
|
||||||
.balanceValue = session->credits().balanceValue(),
|
.balanceValue = session->credits().balanceValue(),
|
||||||
|
|
|
@ -8,10 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "payments/ui/payments_reaction_box.h"
|
#include "payments/ui/payments_reaction_box.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/boxes/boost_box.h" // MakeBoostFeaturesBadge.
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/continuous_sliders.h"
|
#include "ui/widgets/continuous_sliders.h"
|
||||||
|
#include "ui/dynamic_image.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_credits.h"
|
#include "styles/style_credits.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_premium.h"
|
#include "styles/style_premium.h"
|
||||||
|
@ -27,6 +31,8 @@ namespace Settings {
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kMaxTopPaidShown = 3;
|
||||||
|
|
||||||
void PaidReactionSlider(
|
void PaidReactionSlider(
|
||||||
not_null<VerticalLayout*> container,
|
not_null<VerticalLayout*> container,
|
||||||
int min,
|
int min,
|
||||||
|
@ -35,9 +41,9 @@ void PaidReactionSlider(
|
||||||
Fn<void(int)> changed) {
|
Fn<void(int)> changed) {
|
||||||
const auto top = st::boxTitleClose.height + st::creditsHistoryRightSkip;
|
const auto top = st::boxTitleClose.height + st::creditsHistoryRightSkip;
|
||||||
const auto slider = container->add(
|
const auto slider = container->add(
|
||||||
object_ptr<MediaSlider>(container, st::settingsScale),
|
object_ptr<MediaSlider>(container, st::paidReactSlider),
|
||||||
st::boxRowPadding + QMargins(0, top, 0, 0));
|
st::boxRowPadding + QMargins(0, top, 0, 0));
|
||||||
slider->resize(slider->width(), st::settingsScale.seekSize.height());
|
slider->resize(slider->width(), st::paidReactSlider.seekSize.height());
|
||||||
slider->setPseudoDiscrete(
|
slider->setPseudoDiscrete(
|
||||||
max + 1 - min,
|
max + 1 - min,
|
||||||
[=](int index) { return min + index; },
|
[=](int index) { return min + index; },
|
||||||
|
@ -46,13 +52,145 @@ void PaidReactionSlider(
|
||||||
changed);
|
changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QImage GenerateBadgeImage(int count) {
|
||||||
|
const auto text = Lang::FormatCountDecimal(count);
|
||||||
|
const auto length = st::chatSimilarBadgeFont->width(text);
|
||||||
|
const auto contents = length
|
||||||
|
+ st::chatSimilarLockedIcon.width();
|
||||||
|
const auto badge = QRect(
|
||||||
|
st::chatSimilarBadgePadding.left(),
|
||||||
|
st::chatSimilarBadgePadding.top(),
|
||||||
|
contents,
|
||||||
|
st::chatSimilarBadgeFont->height);
|
||||||
|
const auto rect = badge.marginsAdded(st::chatSimilarBadgePadding);
|
||||||
|
|
||||||
|
auto result = QImage(
|
||||||
|
rect.size() * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
result.fill(Qt::transparent);
|
||||||
|
auto q = QPainter(&result);
|
||||||
|
|
||||||
|
const auto &font = st::chatSimilarBadgeFont;
|
||||||
|
const auto textTop = badge.y() + font->ascent;
|
||||||
|
const auto icon = &st::chatSimilarLockedIcon;
|
||||||
|
const auto position = st::chatSimilarLockedIconPosition;
|
||||||
|
|
||||||
|
auto hq = PainterHighQualityEnabler(q);
|
||||||
|
q.setBrush(st::creditsBg3);
|
||||||
|
q.setPen(Qt::NoPen);
|
||||||
|
const auto radius = rect.height() / 2.;
|
||||||
|
q.drawRoundedRect(rect, radius, radius);
|
||||||
|
|
||||||
|
auto textLeft = 0;
|
||||||
|
if (icon) {
|
||||||
|
icon->paint(
|
||||||
|
q,
|
||||||
|
badge.x() + position.x(),
|
||||||
|
badge.y() + position.y(),
|
||||||
|
rect.width());
|
||||||
|
textLeft += position.x() + icon->width();
|
||||||
|
}
|
||||||
|
|
||||||
|
q.setFont(font);
|
||||||
|
q.setPen(st::premiumButtonFg);
|
||||||
|
q.drawText(textLeft, textTop, text);
|
||||||
|
q.end();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::RpWidget*> MakeTopReactor(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
const PaidReactionTop &data) {
|
||||||
|
const auto result = Ui::CreateChild<Ui::RpWidget>(parent);
|
||||||
|
result->show();
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
QImage badge;
|
||||||
|
Ui::Text::String name;
|
||||||
|
};
|
||||||
|
const auto state = result->lifetime().make_state<State>();
|
||||||
|
state->name.setText(st::defaultTextStyle, data.name);
|
||||||
|
|
||||||
|
const auto count = data.count;
|
||||||
|
const auto photo = data.photo;
|
||||||
|
photo->subscribeToUpdates([=] {
|
||||||
|
result->update();
|
||||||
|
});
|
||||||
|
style::PaletteChanged(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
state->badge = QImage();
|
||||||
|
}, result->lifetime());
|
||||||
|
result->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = Painter(result);
|
||||||
|
const auto left = (result->width() - st::paidReactTopUserpic) / 2;
|
||||||
|
p.drawImage(left, 0, photo->image(st::paidReactTopUserpic));
|
||||||
|
|
||||||
|
if (state->badge.isNull()) {
|
||||||
|
state->badge = GenerateBadgeImage(count);
|
||||||
|
}
|
||||||
|
const auto bwidth = state->badge.width()
|
||||||
|
/ state->badge.devicePixelRatio();
|
||||||
|
p.drawImage(
|
||||||
|
(result->width() - bwidth) / 2,
|
||||||
|
st::paidReactTopBadgeSkip,
|
||||||
|
state->badge);
|
||||||
|
|
||||||
|
p.setPen(st::windowFg);
|
||||||
|
const auto skip = st::normalFont->spacew;
|
||||||
|
const auto nameTop = st::paidReactTopNameSkip;
|
||||||
|
const auto available = result->width() - skip * 2;
|
||||||
|
state->name.draw(p, skip, nameTop, available, style::al_top);
|
||||||
|
}, result->lifetime());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillTopReactors(
|
||||||
|
not_null<VerticalLayout*> container,
|
||||||
|
std::vector<PaidReactionTop> top) {
|
||||||
|
container->add(
|
||||||
|
MakeBoostFeaturesBadge(
|
||||||
|
container,
|
||||||
|
tr::lng_paid_react_top_title(),
|
||||||
|
[](QRect) { return st::creditsBg3->b; }),
|
||||||
|
st::boxRowPadding + st::paidReactTopTitleMargin);
|
||||||
|
|
||||||
|
const auto height = st::paidReactTopNameSkip + st::normalFont->height;
|
||||||
|
const auto wrap = container->add(
|
||||||
|
object_ptr<Ui::FixedHeightWidget>(container, height),
|
||||||
|
st::paidReactTopMargin);
|
||||||
|
struct State {
|
||||||
|
std::vector<not_null<Ui::RpWidget*>> widgets;
|
||||||
|
};
|
||||||
|
const auto state = wrap->lifetime().make_state<State>();
|
||||||
|
|
||||||
|
const auto topCount = std::min(int(top.size()), kMaxTopPaidShown);
|
||||||
|
for (auto i = 0; i != topCount; ++i) {
|
||||||
|
state->widgets.push_back(MakeTopReactor(wrap, top[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||||
|
const auto single = width / 4;
|
||||||
|
if (single <= st::paidReactTopUserpic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto left = (width - single * topCount) / 2;
|
||||||
|
for (const auto widget : state->widgets) {
|
||||||
|
widget->setGeometry(left, 0, single, height);
|
||||||
|
left += single;
|
||||||
|
}
|
||||||
|
}, wrap->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void PaidReactionsBox(
|
void PaidReactionsBox(
|
||||||
not_null<GenericBox*> box,
|
not_null<GenericBox*> box,
|
||||||
PaidReactionBoxArgs &&args) {
|
PaidReactionBoxArgs &&args) {
|
||||||
box->setWidth(st::boxWideWidth);
|
box->setWidth(st::boxWideWidth);
|
||||||
box->setStyle(st::boostBox);
|
box->setStyle(st::paidReactBox);
|
||||||
box->setNoContentMargin(true);
|
box->setNoContentMargin(true);
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
@ -77,7 +215,7 @@ void PaidReactionsBox(
|
||||||
box,
|
box,
|
||||||
tr::lng_paid_react_title(),
|
tr::lng_paid_react_title(),
|
||||||
st::boostCenteredTitle),
|
st::boostCenteredTitle),
|
||||||
st::boxRowPadding + QMargins(0, st::boostTitleSkip, 0, 0));
|
st::boxRowPadding + QMargins(0, st::paidReactTitleSkip, 0, 0));
|
||||||
box->addRow(
|
box->addRow(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
box,
|
box,
|
||||||
|
@ -87,7 +225,11 @@ void PaidReactionsBox(
|
||||||
Text::RichLangValue),
|
Text::RichLangValue),
|
||||||
st::boostText),
|
st::boostText),
|
||||||
(st::boxRowPadding
|
(st::boxRowPadding
|
||||||
+ QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
|
+ QMargins(0, st::lineWidth, 0, st::boostBottomSkip)));
|
||||||
|
|
||||||
|
if (!args.top.empty()) {
|
||||||
|
FillTopReactors(box->verticalLayout(), std::move(args.top));
|
||||||
|
}
|
||||||
|
|
||||||
const auto button = box->addButton(rpl::single(QString()), [=] {
|
const auto button = box->addButton(rpl::single(QString()), [=] {
|
||||||
args.send(state->chosen.current());
|
args.send(state->chosen.current());
|
||||||
|
@ -117,7 +259,7 @@ void PaidReactionsBox(
|
||||||
|
|
||||||
box->widthValue(
|
box->widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
const auto &padding = st::boostBox.buttonPadding;
|
const auto &padding = st::paidReactBox.buttonPadding;
|
||||||
button->resizeToWidth(width
|
button->resizeToWidth(width
|
||||||
- padding.left()
|
- padding.left()
|
||||||
- padding.right());
|
- padding.right());
|
||||||
|
|
|
@ -13,17 +13,26 @@ namespace Ui {
|
||||||
|
|
||||||
class BoxContent;
|
class BoxContent;
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
|
class DynamicImage;
|
||||||
|
|
||||||
struct TextWithContext {
|
struct TextWithContext {
|
||||||
TextWithEntities text;
|
TextWithEntities text;
|
||||||
std::any context;
|
std::any context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PaidReactionTop {
|
||||||
|
QString name;
|
||||||
|
std::shared_ptr<DynamicImage> photo;
|
||||||
|
int count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct PaidReactionBoxArgs {
|
struct PaidReactionBoxArgs {
|
||||||
int min = 0;
|
int min = 0;
|
||||||
int max = 0;
|
int max = 0;
|
||||||
int chosen = 0;
|
int chosen = 0;
|
||||||
|
|
||||||
|
std::vector<PaidReactionTop> top;
|
||||||
|
|
||||||
QString channel;
|
QString channel;
|
||||||
Fn<rpl::producer<TextWithContext>(rpl::producer<int> amount)> submit;
|
Fn<rpl::producer<TextWithContext>(rpl::producer<int> amount)> submit;
|
||||||
rpl::producer<uint64> balanceValue;
|
rpl::producer<uint64> balanceValue;
|
||||||
|
|
|
@ -111,48 +111,13 @@ namespace {
|
||||||
[[nodiscard]] object_ptr<Ui::FlatLabel> MakeFeaturesBadge(
|
[[nodiscard]] object_ptr<Ui::FlatLabel> MakeFeaturesBadge(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
rpl::producer<QString> text) {
|
rpl::producer<QString> text) {
|
||||||
auto result = object_ptr<Ui::FlatLabel>(
|
return MakeBoostFeaturesBadge(parent, std::move(text), [](QRect rect) {
|
||||||
parent,
|
|
||||||
std::move(text),
|
|
||||||
st::boostLevelBadge);
|
|
||||||
const auto label = result.data();
|
|
||||||
|
|
||||||
label->show();
|
|
||||||
label->paintRequest() | rpl::start_with_next([=] {
|
|
||||||
const auto size = label->textMaxWidth();
|
|
||||||
const auto rect = QRect(
|
|
||||||
(label->width() - size) / 2,
|
|
||||||
st::boostLevelBadge.margin.top(),
|
|
||||||
size,
|
|
||||||
st::boostLevelBadge.style.font->height
|
|
||||||
).marginsAdded(st::boostLevelBadge.margin);
|
|
||||||
auto p = QPainter(label);
|
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
|
||||||
auto gradient = QLinearGradient(
|
auto gradient = QLinearGradient(
|
||||||
rect.topLeft(),
|
rect.topLeft(),
|
||||||
rect.topRight());
|
rect.topRight());
|
||||||
gradient.setStops(Ui::Premium::GiftGradientStops());
|
gradient.setStops(Ui::Premium::GiftGradientStops());
|
||||||
p.setBrush(gradient);
|
return QBrush(gradient);
|
||||||
p.setPen(Qt::NoPen);
|
});
|
||||||
p.drawRoundedRect(rect, rect.height() / 2., rect.height() / 2.);
|
|
||||||
|
|
||||||
const auto &lineFg = st::windowBgRipple;
|
|
||||||
const auto line = st::boostLevelBadgeLine;
|
|
||||||
const auto top = st::boostLevelBadge.margin.top()
|
|
||||||
+ ((st::boostLevelBadge.style.font->height - line) / 2);
|
|
||||||
const auto left = 0;
|
|
||||||
const auto skip = st::boostLevelBadgeSkip;
|
|
||||||
if (const auto right = rect.x() - skip; right > left) {
|
|
||||||
p.fillRect(left, top, right - left, line, lineFg);
|
|
||||||
}
|
|
||||||
const auto right = label->width();
|
|
||||||
if (const auto left = rect.x() + rect.width() + skip
|
|
||||||
; left < right) {
|
|
||||||
p.fillRect(left, top, right - left, line, lineFg);
|
|
||||||
}
|
|
||||||
}, label->lifetime());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddFeaturesList(
|
void AddFeaturesList(
|
||||||
|
@ -885,4 +850,48 @@ void FillBoostLimit(
|
||||||
limitLinePadding);
|
limitLinePadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::FlatLabel> MakeBoostFeaturesBadge(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
rpl::producer<QString> text,
|
||||||
|
Fn<QBrush(QRect)> bg) {
|
||||||
|
auto result = object_ptr<Ui::FlatLabel>(
|
||||||
|
parent,
|
||||||
|
std::move(text),
|
||||||
|
st::boostLevelBadge);
|
||||||
|
const auto label = result.data();
|
||||||
|
|
||||||
|
label->show();
|
||||||
|
label->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
const auto size = label->textMaxWidth();
|
||||||
|
const auto rect = QRect(
|
||||||
|
(label->width() - size) / 2,
|
||||||
|
st::boostLevelBadge.margin.top(),
|
||||||
|
size,
|
||||||
|
st::boostLevelBadge.style.font->height
|
||||||
|
).marginsAdded(st::boostLevelBadge.margin);
|
||||||
|
auto p = QPainter(label);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setBrush(bg(rect));
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.drawRoundedRect(rect, rect.height() / 2., rect.height() / 2.);
|
||||||
|
|
||||||
|
const auto &lineFg = st::windowBgRipple;
|
||||||
|
const auto line = st::boostLevelBadgeLine;
|
||||||
|
const auto top = st::boostLevelBadge.margin.top()
|
||||||
|
+ ((st::boostLevelBadge.style.font->height - line) / 2);
|
||||||
|
const auto left = 0;
|
||||||
|
const auto skip = st::boostLevelBadgeSkip;
|
||||||
|
if (const auto right = rect.x() - skip; right > left) {
|
||||||
|
p.fillRect(left, top, right - left, line, lineFg);
|
||||||
|
}
|
||||||
|
const auto right = label->width();
|
||||||
|
if (const auto left = rect.x() + rect.width() + skip
|
||||||
|
; left < right) {
|
||||||
|
p.fillRect(left, top, right - left, line, lineFg);
|
||||||
|
}
|
||||||
|
}, label->lifetime());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Show;
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
|
class FlatLabel;
|
||||||
|
|
||||||
struct BoostCounters {
|
struct BoostCounters {
|
||||||
int level = 0;
|
int level = 0;
|
||||||
|
@ -129,4 +130,9 @@ void FillBoostLimit(
|
||||||
rpl::producer<BoostCounters> data,
|
rpl::producer<BoostCounters> data,
|
||||||
style::margins limitLinePadding);
|
style::margins limitLinePadding);
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::FlatLabel> MakeBoostFeaturesBadge(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
rpl::producer<QString> text,
|
||||||
|
Fn<QBrush(QRect)> bg);
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -46,7 +46,7 @@ creditsBoxButtonLabel: FlatLabel(defaultFlatLabel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
starIconSmall: icon{{ "payments/small_star", windowFg }};
|
starIconSmall: icon{{ "payments/small_star", windowFg }};
|
||||||
starIconSmallPadding: margins(0px, -2px, 0px, 0px);
|
starIconSmallPadding: margins(0px, -3px, 0px, 0px);
|
||||||
|
|
||||||
creditsHistoryEntryTypeAds: icon {{ "folders/folders_channels", premiumButtonFg }};
|
creditsHistoryEntryTypeAds: icon {{ "folders/folders_channels", premiumButtonFg }};
|
||||||
|
|
||||||
|
|
|
@ -340,13 +340,13 @@ showOrBox: Box(boostBox) {
|
||||||
|
|
||||||
boostBoxMaxHeight: 512px;
|
boostBoxMaxHeight: 512px;
|
||||||
boostLevelBadge: FlatLabel(defaultFlatLabel) {
|
boostLevelBadge: FlatLabel(defaultFlatLabel) {
|
||||||
margin: margins(12px, 4px, 12px, 4px);
|
margin: margins(12px, 4px, 12px, 5px);
|
||||||
style: semiboldTextStyle;
|
style: semiboldTextStyle;
|
||||||
textFg: premiumButtonFg;
|
textFg: premiumButtonFg;
|
||||||
align: align(top);
|
align: align(top);
|
||||||
}
|
}
|
||||||
boostLevelBadgePadding: margins(30px, 12px, 32px, 12px);
|
boostLevelBadgePadding: margins(30px, 12px, 32px, 12px);
|
||||||
boostLevelBadgeSkip: 8px;
|
boostLevelBadgeSkip: 12px;
|
||||||
boostLevelBadgeLine: 1px;
|
boostLevelBadgeLine: 1px;
|
||||||
|
|
||||||
boostFeatureLabel: FlatLabel(defaultFlatLabel) {
|
boostFeatureLabel: FlatLabel(defaultFlatLabel) {
|
||||||
|
@ -365,3 +365,29 @@ boostFeatureName: icon{{ "settings/premium/features/feature_color_names", window
|
||||||
boostFeatureStories: icon{{ "settings/premium/features/feature_stories", windowBgActive }};
|
boostFeatureStories: icon{{ "settings/premium/features/feature_stories", windowBgActive }};
|
||||||
boostFeatureTranscribe: icon{{ "settings/premium/features/feature_voice", windowBgActive }};
|
boostFeatureTranscribe: icon{{ "settings/premium/features/feature_voice", windowBgActive }};
|
||||||
boostFeatureOffSponsored: icon{{ "settings/premium/features/feature_off_sponsored", windowBgActive }};
|
boostFeatureOffSponsored: icon{{ "settings/premium/features/feature_off_sponsored", windowBgActive }};
|
||||||
|
|
||||||
|
paidReactTitleSkip: 23px;
|
||||||
|
paidReactTopTitleMargin: margins(10px, 26px, 10px, 12px);
|
||||||
|
paidReactTopMargin: margins(0px, 12px, 0px, 11px);
|
||||||
|
paidReactTopUserpic: 42px;
|
||||||
|
paidReactTopNameSkip: 47px;
|
||||||
|
paidReactTopBadgeSkip: 32px;
|
||||||
|
paidReactSlider: MediaSlider(defaultContinuousSlider) {
|
||||||
|
activeFg: creditsBg3;
|
||||||
|
inactiveFg: creditsBg2;
|
||||||
|
activeFgOver: creditsBg3;
|
||||||
|
inactiveFgOver: creditsBg2;
|
||||||
|
activeFgDisabled: creditsBg3;
|
||||||
|
inactiveFgDisabled: creditsBg2;
|
||||||
|
width: 6px;
|
||||||
|
seekSize: size(16px, 16px);
|
||||||
|
}
|
||||||
|
paidReactBox: Box(boostBox) {
|
||||||
|
buttonPadding: margins(22px, 22px, 22px, 22px);
|
||||||
|
buttonHeight: 42px;
|
||||||
|
button: RoundButton(defaultActiveButton) {
|
||||||
|
height: 42px;
|
||||||
|
textTop: 12px;
|
||||||
|
font: font(13px semibold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8db5d1aa533334c75ed2598ecf3607768ae9b418
|
Subproject commit a5b1266a8c340ed916466a83cbbc5793471c2438
|
Loading…
Add table
Reference in a new issue