diff --git a/Telegram/Resources/icons/levels/level_warning.svg b/Telegram/Resources/icons/levels/level_warning.svg
new file mode 100644
index 0000000000..cba1db0076
--- /dev/null
+++ b/Telegram/Resources/icons/levels/level_warning.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 641f0b66d7..f7f2fef5a5 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -1882,6 +1882,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stars_rating_pending#other" = "The rating updates in 21 days after purchases.\n{count} points are pending. {link}";
"lng_stars_rating_pending_preview" = "Preview {arrow}";
"lng_stars_rating_pending_back" = "Back {arrow}";
+"lng_stars_rating_negative_label" = "Negative Rating";
+"lng_stars_rating_negative" = "A negative rating indicates that **{name}'s** payments are unreliable.";
+"lng_stars_rating_negative_your#one" = "A negative rating indicates that your payments are unreliable. Spend **{count} Star** to fix this issue.";
+"lng_stars_rating_negative_your#other" = "A negative rating indicates that your payments are unreliable. Spend **{count} Stars** to fix this issue.";
"lng_stars_rating_about" = "This rating reflects **{name}'s** activity on Telegram. What affects it:";
"lng_stars_rating_about_your" = "This rating reflects your activity on Telegram. What affects it:";
"lng_stars_title_gifts_telegram" = "Gifts from Telegram";
diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp
index 0da1a8137f..77fdb049b3 100644
--- a/Telegram/SourceFiles/data/data_peer_values.cpp
+++ b/Telegram/SourceFiles/data/data_peer_values.cpp
@@ -525,7 +525,11 @@ rpl::producer StarsRatingValue(
user,
Data::PeerUpdate::Flag::StarsRating
) | rpl::map([=] {
- return user->starsRating();
+ auto result = user->starsRating();
+ if (!user->isSelf() && result.level < 0) {
+ result.stars = 0;
+ }
+ return result;
});
}
return rpl::single(Data::StarsRating());
diff --git a/Telegram/SourceFiles/info/profile/info_levels.style b/Telegram/SourceFiles/info/profile/info_levels.style
index e1d38d51a8..9d1ae14eac 100644
--- a/Telegram/SourceFiles/info/profile/info_levels.style
+++ b/Telegram/SourceFiles/info/profile/info_levels.style
@@ -74,3 +74,7 @@ level80: LevelShape(levelBase) {
level90: LevelShape(levelBase) {
icon: icon {{ "levels/level90_inner-31x31", windowBgActive }};
}
+levelNegative: LevelShape(levelBase) {
+ icon: icon {{ "levels/level_warning-18x18", attentionButtonFg, point(6px, 7px) }};
+}
+levelNegativeBubble: icon {{ "levels/level_warning-28x28", windowFgActive }};
diff --git a/Telegram/SourceFiles/ui/controls/stars_rating.cpp b/Telegram/SourceFiles/ui/controls/stars_rating.cpp
index 0adfa4e500..6983bb64f4 100644
--- a/Telegram/SourceFiles/ui/controls/stars_rating.cpp
+++ b/Telegram/SourceFiles/ui/controls/stars_rating.cpp
@@ -157,13 +157,19 @@ void FillRatingLimit(
rpl::producer<> showFinished,
not_null container,
rpl::producer data,
+ Premium::BubbleType type,
style::margins limitLinePadding,
- int starsForScale) {
+ int starsForScale,
+ bool hideCount) {
const auto addSkip = [&](int skip) {
container->add(object_ptr(container, skip));
};
+ const auto negative = (type == Premium::BubbleType::NegativeRating);
const auto ratio = [=](Counters rating) {
+ if (negative) {
+ return 0.5;
+ }
const auto min = rating.thisLevelStars;
const auto max = rating.nextLevelStars;
@@ -213,12 +219,14 @@ void FillRatingLimit(
});
Premium::AddBubbleRow(
container,
- st::boostBubble,
+ (hideCount ? st::iconOnlyPremiumBubble : st::boostBubble),
std::move(showFinished),
rpl::duplicate(bubbleRowState),
- Premium::BubbleType::StarRating,
- BubbleTextFactory(starsForScale),
- &st::infoStarsCrown,
+ type,
+ (hideCount
+ ? [](int) { return QString(); }
+ : BubbleTextFactory(starsForScale)),
+ negative ? &st::levelNegativeBubble : &st::infoStarsCrown,
limitLinePadding);
addSkip(st::premiumLineTextSkip);
@@ -227,30 +235,35 @@ void FillRatingLimit(
};
auto limitState = std::move(
bubbleRowState
- ) | rpl::map([](const Premium::BubbleRowState &state) {
+ ) | rpl::map([negative](const Premium::BubbleRowState &state) {
return Premium::LimitRowState{
- .ratio = state.ratio,
- .animateFromZero = state.animateFromZero,
+ .ratio = negative ? 0.5 : state.ratio,
+ .animateFromZero = !negative && state.animateFromZero,
.dynamic = state.dynamic
};
});
auto left = rpl::duplicate(
adjustedData
) | rpl::map([=](Counters counters) {
- return level(counters.level);
+ return (counters.level < 0) ? QString() : level(counters.level);
});
auto right = rpl::duplicate(
adjustedData
) | rpl::map([=](Counters counters) {
- return level(counters.level + 1);
+ return (counters.level < 0)
+ ? tr::lng_stars_rating_negative_label(tr::now)
+ : level(counters.level + 1);
});
Premium::AddLimitRow(
container,
- st::boostLimits,
+ (negative ? st::negativeStarsLimits : st::boostLimits),
Premium::LimitRowLabels{
.leftLabel = std::move(left),
.rightLabel = std::move(right),
- .activeLineBg = [=] { return st::windowBgActive->b; },
+ .activeLineBg = [=] { return negative
+ ? st::attentionButtonFg->b
+ : st::windowBgActive->b;
+ },
},
std::move(limitState),
limitLinePadding);
@@ -276,8 +289,12 @@ void AboutRatingBox(
BoxShowFinishes(box),
box->verticalLayout(),
state->data.value(),
+ (data.level < 0
+ ? Premium::BubbleType::NegativeRating
+ : Premium::BubbleType::StarRating),
st::boxRowPadding,
- data.stars);
+ data.stars,
+ (data.level < 0 && !data.stars));
box->setMaxHeight(st::boostBoxMaxHeight);
@@ -294,10 +311,40 @@ void AboutRatingBox(
: tr::lng_stars_rating_about_your(
Ui::Text::RichLangValue) | rpl::type_erased();
+ if (data.level < 0) {
+ auto text = (data.stars < 0)
+ ? tr::lng_stars_rating_negative_your(
+ lt_count_decimal,
+ rpl::single(-data.stars * 1.),
+ Ui::Text::RichLangValue)
+ : tr::lng_stars_rating_negative(
+ lt_name,
+ rpl::single(TextWithEntities{ name }),
+ Ui::Text::RichLangValue);
+ const auto aboutNegative = box->addRow(
+ object_ptr(
+ box,
+ std::move(text),
+ st::boostTextNegative),
+ (st::boxRowPadding
+ + QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
+ aboutNegative->setTryMakeSimilarLines(true);
+ }
+
box->addRow(
object_ptr(box, std::move(title), st::infoStarsTitle),
st::boxRowPadding + QMargins(0, st::boostTitleSkip / 2, 0, 0));
+ AssertIsDebug();
+ pending = {
+ .value = {
+ .level = 10,
+ .stars = 100,
+ .thisLevelStars = 90,
+ .nextLevelStars = 110,
+ },
+ .date = 86400,
+ };
if (pending) {
auto text = state->pending.value(
) | rpl::map([=](bool value) {
@@ -398,6 +445,9 @@ void AboutRatingBox(
}
[[nodiscard]] not_null SelectShape(int level) {
+ if (level < 0) {
+ return &st::levelNegative;
+ }
struct Shape {
int level = 0;
not_null shape;
@@ -480,7 +530,9 @@ void StarsRating::updateData(Data::StarsRating rating) {
_shape = SelectShape(rating.level);
_collapsedText.setText(
st::levelStyle,
- Lang::FormatCountDecimal(rating.level));
+ (rating.level < 0
+ ? QString()
+ : Lang::FormatCountDecimal(rating.level)));
_widthValue = _shape->icon.width() - st::levelMargin.left();
}
updateWidth();
diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style
index 75422a7514..a510a70963 100644
--- a/Telegram/SourceFiles/ui/effects/premium.style
+++ b/Telegram/SourceFiles/ui/effects/premium.style
@@ -255,6 +255,9 @@ boostLimits: PremiumLimits(defaultPremiumLimits) {
gradientFromLeft: true;
nonPremiumBg: windowBgRipple;
}
+negativeStarsLimits: PremiumLimits(boostLimits) {
+ gradientFromLeft: false;
+}
boostBubble: PremiumBubble(defaultPremiumBubble) {
height: 32px;
padding: margins(7px, 0px, 11px, 0px);
@@ -263,6 +266,10 @@ boostBubble: PremiumBubble(defaultPremiumBubble) {
tailSize: size(14px, 6px);
font: font(16px);
}
+iconOnlyPremiumBubble: PremiumBubble(boostBubble) {
+ padding: margins(7px, 0px, 7px, 0px);
+ textSkip: 0px;
+}
boostTitleSkip: 32px;
boostTitle: FlatLabel(defaultFlatLabel) {
minWidth: 40px;
@@ -289,6 +296,9 @@ boostText: FlatLabel(defaultFlatLabel) {
boostTextPending: FlatLabel(boostText) {
textFg: windowSubTextFg;
}
+boostTextNegative: FlatLabel(boostText) {
+ textFg: attentionButtonFg;
+}
boostReassignText: FlatLabel(defaultFlatLabel) {
minWidth: 40px;
align: align(top);
diff --git a/Telegram/SourceFiles/ui/effects/premium_bubble.cpp b/Telegram/SourceFiles/ui/effects/premium_bubble.cpp
index 75a6965a1d..8d3629ec70 100644
--- a/Telegram/SourceFiles/ui/effects/premium_bubble.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_bubble.cpp
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/painter.h"
+#include "styles/style_info_levels.h"
#include "styles/style_layers.h"
#include "styles/style_premium.h"
@@ -181,11 +182,9 @@ void Bubble::paintBubble(QPainter &p, const QRect &r, const QBrush &brush) {
p.setPen(st::activeButtonFg);
p.setFont(_st.font);
const auto iconLeft = r.x() + _st.padding.left();
- _icon->paint(
- p,
- iconLeft,
- bubbleRect.y() + (bubbleRect.height() - _icon->height()) / 2,
- bubbleRect.width());
+ const auto iconTop = bubbleRect.y()
+ + (bubbleRect.height() - _icon->height()) / 2;
+ _icon->paint(p, iconLeft, iconTop, bubbleRect.width());
_numberAnimation.paint(
p,
iconLeft + _icon->width() + _st.textSkip,
@@ -424,6 +423,7 @@ void BubbleWidget::paintEvent(QPaintEvent *e) {
switch (_type) {
case BubbleType::NoPremium:
case BubbleType::StarRating: return st::windowBgActive->b;
+ case BubbleType::NegativeRating: return st::attentionButtonFg->b;
case BubbleType::Premium: return QBrush(_cachedGradient);
case BubbleType::Credits: return st::creditsBg3->b;
}
diff --git a/Telegram/SourceFiles/ui/effects/premium_bubble.h b/Telegram/SourceFiles/ui/effects/premium_bubble.h
index b867250400..4d621a47a4 100644
--- a/Telegram/SourceFiles/ui/effects/premium_bubble.h
+++ b/Telegram/SourceFiles/ui/effects/premium_bubble.h
@@ -89,6 +89,7 @@ struct BubbleRowState {
enum class BubbleType : uchar {
StarRating,
+ NegativeRating,
NoPremium,
Premium,
Credits,
diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
index 2ace588b1f..f4d7c4f90d 100644
--- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
@@ -372,7 +372,6 @@ void Line::recache(const QSize &s) {
}
};
const auto textPadding = st::premiumLineTextSkip;
- const auto textTop = (s.height() - _leftLabel.minHeight()) / 2;
const auto rwidth = _rightLabel.maxWidth();
const auto pen = [&](bool gradient) {
return gradient ? st::activeButtonFg : _st.nonPremiumFg;
@@ -385,8 +384,10 @@ void Line::recache(const QSize &s) {
if (_dynamic) {
p.setFont(st::normalFont);
p.setPen(pen(_st.gradientFromLeft));
- _leftLabel.drawLeft(p, textPadding, textTop, width, width);
- _rightLabel.drawRight(p, textPadding, textTop, rwidth, width);
+ const auto leftTop = (s.height() - _leftLabel.minHeight()) / 2;
+ _leftLabel.drawLeft(p, textPadding, leftTop, width, width);
+ const auto rightTop = (s.height() - _rightLabel.minHeight()) / 2;
+ _rightLabel.drawRight(p, textPadding, rightTop, rwidth, width);
}
_leftPixmap = std::move(leftPixmap);
}
@@ -398,8 +399,10 @@ void Line::recache(const QSize &s) {
if (_dynamic) {
p.setFont(st::normalFont);
p.setPen(pen(!_st.gradientFromLeft));
- _leftLabel.drawLeft(p, textPadding, textTop, width, width);
- _rightLabel.drawRight(p, textPadding, textTop, rwidth, width);
+ const auto leftTop = (s.height() - _leftLabel.minHeight()) / 2;
+ _leftLabel.drawLeft(p, textPadding, leftTop, width, width);
+ const auto rightTop = (s.height() - _rightLabel.minHeight()) / 2;
+ _rightLabel.drawRight(p, textPadding, rightTop, rwidth, width);
}
_rightPixmap = std::move(rightPixmap);
}