Show information about disabled reactions.

This commit is contained in:
John Preston 2022-05-27 23:57:40 +04:00
parent 2362d6c6fb
commit d2abcdacee
5 changed files with 122 additions and 17 deletions

View File

@ -1662,6 +1662,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_stickers_description" = "You can choose a sticker set which will be available for every member while in the group chat.";
"lng_group_stickers_add" = "Choose sticker set";
"lng_premium_stickers" = "Premium stickers";
"lng_premium_reaction_no_group" = "This reaction is not available in this group.";
"lng_premium_reaction_no_channel" = "This reaction is not available in this channel.";
"lng_premium" = "Premium";
"lng_premium_free" = "Free";

View File

@ -42,13 +42,13 @@ constexpr auto kEnumerateCount = 3;
struct Descriptor {
PremiumPreview section = PremiumPreview::Stickers;
DocumentData *requestedSticker = nullptr;
base::flat_set<QString> disabledReactions;
base::flat_map<QString, ReactionDisableType> disabled;
};
bool operator==(const Descriptor &a, const Descriptor &b) {
return (a.section == b.section)
&& (a.requestedSticker == b.requestedSticker)
&& (a.disabledReactions == b.disabledReactions);
&& (a.disabled == b.disabled);
}
bool operator!=(const Descriptor &a, const Descriptor &b) {
@ -201,17 +201,20 @@ public:
ReactionPreview(
not_null<Window::SessionController*> controller,
const Data::Reaction &reaction,
ReactionDisableType type,
Fn<void()> update);
[[nodiscard]] bool playsEffect() const;
void paint(QPainter &p, int x, int y, float64 scale);
void paint(Painter &p, int x, int y, float64 scale);
void paintEffect(QPainter &p, int x, int y, float64 scale);
void paintRestricted(Painter &p, int x, int bottom, float64 scale);
void startAnimations();
void cancelAnimations();
private:
void checkReady();
void paintTitle(Painter &p, int x, int y, float64 scale);
const not_null<Window::SessionController*> _controller;
const Fn<void()> _update;
@ -220,6 +223,8 @@ private:
std::unique_ptr<Lottie::SinglePlayer> _center;
std::unique_ptr<Lottie::SinglePlayer> _around;
std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
Ui::Text::String _name;
Ui::Text::String _disabled;
QImage _cache1;
QImage _cache2;
QImage _cache3;
@ -230,9 +235,20 @@ private:
};
[[nodiscard]] QString DisabledText(ReactionDisableType type) {
switch (type) {
case ReactionDisableType::Group:
return tr::lng_premium_reaction_no_group(tr::now);
case ReactionDisableType::Channel:
return tr::lng_premium_reaction_no_channel(tr::now);
}
return QString();
}
ReactionPreview::ReactionPreview(
not_null<Window::SessionController*> controller,
const Data::Reaction &reaction,
ReactionDisableType type,
Fn<void()> update)
: _controller(controller)
, _update(std::move(update))
@ -241,7 +257,9 @@ ReactionPreview::ReactionPreview(
, _pathGradient(
HistoryView::MakePathShiftGradient(
controller->chatStyle(),
_update)) {
_update))
, _name(st::premiumReactionName, reaction.title)
, _disabled(st::defaultTextStyle, DisabledText(type)) {
_centerMedia->checkStickerLarge();
_aroundMedia->checkStickerLarge();
checkReady();
@ -258,7 +276,7 @@ void ReactionPreview::checkReady() {
nullptr,
ChatHelpers::StickerLottieSize::PremiumReactionPreview,
QSize(size, size) * style::DevicePixelRatio(),
Lottie::Quality::High);
Lottie::Quality::Default);
result->updates() | rpl::start_with_next(_update, _lifetime);
return result;
};
@ -282,7 +300,7 @@ void ReactionPreview::cancelAnimations() {
_playRequested = false;
}
void ReactionPreview::paint(QPainter &p, int x, int y, float64 scale) {
void ReactionPreview::paint(Painter &p, int x, int y, float64 scale) {
const auto size = st::premiumReactionAround;
const auto center = st::premiumReactionSize;
const auto inner = QRect(
@ -357,6 +375,54 @@ void ReactionPreview::paint(QPainter &p, int x, int y, float64 scale) {
if (useScale) {
p.restore();
}
paintTitle(p, x, y, scale);
}
void ReactionPreview::paintTitle(Painter &p, int x, int y, float64 scale) {
const auto first = st::premiumReactionScale1;
if (scale <= first) {
return;
}
const auto opacity = (scale - first) / (1. - first);
p.setOpacity(opacity * 0.2);
auto hq = PainterHighQualityEnabler(p);
const auto width = _name.maxWidth();
const auto sticker = st::premiumReactionAround;
const auto inner = QRect(
x + (sticker - width) / 2,
y + (sticker / 2) + st::premiumReactionNameTop,
width,
st::premiumReactionName.font->height);
const auto outer = inner.marginsAdded(st::premiumReactionNamePadding);
const auto radius = outer.height() / 2;
p.setPen(Qt::NoPen);
p.setBrush(st::premiumButtonFg);
p.drawRoundedRect(outer, radius, radius);
p.setOpacity(opacity);
p.setPen(st::premiumButtonFg);
_name.draw(p, inner.x(), inner.y(), width);
if (!_disabled.isEmpty()) {
const auto left = x + (sticker / 2) - (_disabled.maxWidth() / 2);
}
p.setOpacity(1.);
}
void ReactionPreview::paintRestricted(
Painter &p,
int x,
int bottom,
float64 scale) {
const auto first = st::premiumReactionScale1;
if (scale <= first || _disabled.isEmpty()) {
return;
}
const auto sticker = st::premiumReactionAround;
const auto opacity = (scale - first) / (1. - first);
p.setOpacity(opacity);
p.setPen(st::premiumButtonFg);
const auto left = x + (sticker / 2) - (_disabled.maxWidth() / 2);
_disabled.draw(p, left, bottom - 2.5 * st::normalFont->height, _disabled.maxWidth());
p.setOpacity(1.);
}
bool ReactionPreview::playsEffect() const {
@ -389,7 +455,8 @@ void ReactionPreview::paintEffect(QPainter &p, int x, int y, float64 scale) {
[[nodiscard]] not_null<Ui::RpWidget*> ReactionsPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller) {
not_null<Window::SessionController*> controller,
const base::flat_map<QString, ReactionDisableType> &disabled) {
struct State {
std::vector<std::unique_ptr<ReactionPreview>> entries;
Ui::Animations::Simple shifting;
@ -417,9 +484,11 @@ void ReactionPreview::paintEffect(QPainter &p, int x, int y, float64 scale) {
|| !reaction.aroundAnimation) {
continue;
}
const auto i = disabled.find(reaction.emoji);
state->entries.push_back(std::make_unique<ReactionPreview>(
controller,
reaction,
(i != end(disabled)) ? i->second : ReactionDisableType::None,
[=] { result->update(); }));
}
const auto enumerate = [=](
@ -485,16 +554,15 @@ void ReactionPreview::paintEffect(QPainter &p, int x, int y, float64 scale) {
if (entry->playsEffect()
|| (paintedRight > 0 && paintedLeft < st::boxWideWidth)) {
callback(entry, left, scale, delta + index);
} else {
int a = 0;
}
}
};
result->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(result);
const auto top = result->height() / 2 - st::premiumReactionTop;
auto p = Painter(result);
const auto bottom = result->height();
const auto top = (bottom / 2) - st::premiumReactionTop;
auto effects = std::vector<Fn<void()>>();
if (!state->played && !state->shifting.animating()) {
state->played = true;
@ -509,6 +577,7 @@ void ReactionPreview::paintEffect(QPainter &p, int x, int y, float64 scale) {
float64 scale,
int index) {
entry->paint(p, left, top, scale);
entry->paintRestricted(p, left, bottom, scale);
if (entry->playsEffect()) {
effects.push_back([=, &p] {
entry->paintEffect(p, left, top, scale);
@ -629,7 +698,10 @@ void StickerBox(
state->content = StickerPreview(outer, controller, media);
break;
case PremiumPreview::Reactions:
state->content = ReactionsPreview(outer, controller);
state->content = ReactionsPreview(
outer,
controller,
descriptor.disabled);
break;
case PremiumPreview::Avatars:
break;
@ -774,9 +846,9 @@ void ShowStickerPreviewBox(
void ShowPremiumPreviewBox(
not_null<Window::SessionController*> controller,
PremiumPreview section,
const base::flat_set<QString> &disabledReactions) {
const base::flat_map<QString, ReactionDisableType> &disabled) {
Show(controller, Descriptor{
.section = section,
.disabledReactions = disabledReactions,
.disabled = disabled,
});
}

View File

@ -22,8 +22,13 @@ enum class PremiumPreview {
Stickers,
Avatars,
};
enum class ReactionDisableType {
None,
Group,
Channel,
};
void ShowPremiumPreviewBox(
not_null<Window::SessionController*> controller,
PremiumPreview section,
const base::flat_set<QString> &disabledReactions = {});
const base::flat_map<QString, ReactionDisableType> &disabled = {});

View File

@ -310,4 +310,10 @@ premiumReactionSkip2: 78px;
premiumReactionScale2: 0.62;
premiumReactionSkip3: 64px;
premiumReactionScale3: 0.5;
premiumReactionName: TextStyle(defaultTextStyle) {
font: font(15px semibold);
linkFont: font(15px semibold);
linkFontOver: font(15px semibold);
}
premiumReactionNameTop: 64px;
premiumReactionNamePadding: margins(12px, 4px, 12px, 6px);

View File

@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_cloud_themes.h"
#include "data/data_message_reactions.h"
#include "data/data_peer_values.h"
#include "history/history.h"
#include "history/history_item.h"
#include "settings/settings_premium.h"
#include "main/main_session.h"
@ -341,6 +343,24 @@ bool ShowSendPremiumError(
return true;
}
[[nodiscard]] auto ExtractDisabledReactions(
not_null<PeerData*> peer,
const std::vector<Data::Reaction> &list)
-> base::flat_map<QString, ReactionDisableType> {
auto result = base::flat_map<QString, ReactionDisableType>();
const auto type = peer->isBroadcast()
? ReactionDisableType::Channel
: ReactionDisableType::Group;
if (const auto allowed = Data::PeerAllowedReactions(peer)) {
for (const auto &reaction : list) {
if (reaction.premium && !allowed->contains(reaction.emoji)) {
result.emplace(reaction.emoji, type);
}
}
}
return result;
}
bool ShowReactPremiumError(
not_null<SessionController*> controller,
not_null<HistoryItem*> item,
@ -358,7 +378,7 @@ bool ShowReactPremiumError(
ShowPremiumPreviewBox(
controller,
PremiumPreview::Reactions,
{});
ExtractDisabledReactions(item->history()->peer, list));
return true;
}