diff --git a/Telegram/Resources/icons/limits/filled_rating_crown.svg b/Telegram/Resources/icons/limits/filled_rating_crown.svg
new file mode 100644
index 0000000000..e7cc2e124f
--- /dev/null
+++ b/Telegram/Resources/icons/limits/filled_rating_crown.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/icons/limits/filled_understood.svg b/Telegram/Resources/icons/limits/filled_understood.svg
new file mode 100644
index 0000000000..337b43cc71
--- /dev/null
+++ b/Telegram/Resources/icons/limits/filled_understood.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/icons/menu/rating_gifts.svg b/Telegram/Resources/icons/menu/rating_gifts.svg
new file mode 100644
index 0000000000..ed3aa9d4b1
--- /dev/null
+++ b/Telegram/Resources/icons/menu/rating_gifts.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/icons/menu/rating_refund.svg b/Telegram/Resources/icons/menu/rating_refund.svg
new file mode 100644
index 0000000000..05434da66a
--- /dev/null
+++ b/Telegram/Resources/icons/menu/rating_refund.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/icons/menu/users_stars.svg b/Telegram/Resources/icons/menu/users_stars.svg
new file mode 100644
index 0000000000..0a068f774a
--- /dev/null
+++ b/Telegram/Resources/icons/menu/users_stars.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 2711328e0a..6bc8e0a3dc 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -1876,8 +1876,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_star_ref_revoked_title" = "Link removed";
"lng_star_ref_revoked_text" = "It will no longer work.";
-"lng_stars_rating_tooltip" = "Profile level reflects the user's payment reliability.";
-"lng_stars_rating_learn_more" = "Learn More";
+"lng_stars_rating_title" = "Rating";
+"lng_stars_rating_future" = "Future Rating";
+"lng_stars_rating_subtitle" = "The rating update updates in 21 days after purchases.";
+"lng_stars_rating_pending#one" = "{count} point is pending. {link}";
+"lng_stars_rating_pending#other" = "{count} points are pending. {link}";
+"lng_stars_rating_pending_preview" = "Preview {arrow}";
+"lng_stars_rating_pending_back" = "Back {arrow}";
+"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";
+"lng_stars_about_gifts_telegram" = "{emoji} 100% of the Stars spent on gifts purchased from Telegram.";
+"lng_stars_title_gifts_users" = "Gifts and Posts from Users";
+"lng_stars_about_gifts_users" = "{emoji} 20% of the Stars spent on resold gifts, paid messages and channel posts.";
+"lng_stars_title_refunds" = "Refunds and Conversions";
+"lng_stars_about_refunds" = "{emoji} 10x of refunded Stars and 85% of bought gifts converted to Stars.";
+"lng_stars_rating_added" = "Added";
+"lng_stars_rating_deducted" = "Deducted";
+"lng_stars_rating_understood" = "Understood";
"lng_manage_discussion_group" = "Discussion";
"lng_manage_discussion_group_add" = "Add a group";
diff --git a/Telegram/SourceFiles/data/data_peer_common.h b/Telegram/SourceFiles/data/data_peer_common.h
index 87d1722ab1..d3231c3be1 100644
--- a/Telegram/SourceFiles/data/data_peer_common.h
+++ b/Telegram/SourceFiles/data/data_peer_common.h
@@ -11,12 +11,12 @@ namespace Data {
struct StarsRating {
int level = 0;
- int levelStars = 0;
- int currentStars = 0;
+ int stars = 0;
+ int thisLevelStars = 0;
int nextLevelStars = 0;
explicit operator bool() const {
- return level != 0 || levelStars != 0;
+ return level != 0 || thisLevelStars != 0;
}
friend inline bool operator==(StarsRating, StarsRating) = default;
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index dd4aa9b6bf..d3c09a06f6 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -72,8 +72,8 @@ bool ApplyBotVerifierSettings(
const auto &data = rating->data();
return {
.level = data.vlevel().v,
- .levelStars = int(data.vcurrent_level_stars().v),
- .currentStars = int(data.vstars().v),
+ .stars = int(data.vstars().v),
+ .thisLevelStars = int(data.vcurrent_level_stars().v),
.nextLevelStars = int(data.vnext_level_stars().value_or_empty()),
};
}
diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style
index 357efe9957..6904786101 100644
--- a/Telegram/SourceFiles/info/info.style
+++ b/Telegram/SourceFiles/info/info.style
@@ -1195,12 +1195,8 @@ StarsRating {
margin: margins;
padding: margins;
style: TextStyle;
- minSkip: pixels;
- border: pixels;
activeBg: color;
- inactiveBg: color;
activeFg: color;
- inactiveFg: color;
}
infoStarsRating: StarsRating {
margin: margins(6px, 6px, 6px, 6px);
@@ -1208,24 +1204,37 @@ infoStarsRating: StarsRating {
style: TextStyle(defaultTextStyle) {
font: font(11px semibold);
}
- minSkip: 32px;
- border: 2px;
activeBg: windowBgActive;
- inactiveBg: windowBgRipple;
activeFg: windowFgActive;
- inactiveFg: windowBoldFg;
}
-infoStarsRatingLearn: RoundButton(defaultActiveButton) {
- width: -24px;
- height: 44px;
- textTop: 13px;
- textFg: mediaviewTextLinkFg;
- textFgOver: mediaviewTextLinkFg;
- textBg: transparent;
- textBgOver: transparent;
- ripple: emptyRippleAnimation;
+infoStarsTitle: FlatLabel(defaultFlatLabel) {
+ minWidth: 40px;
+ textFg: windowBoldFg;
+ maxHeight: 24px;
+ style: TextStyle(boxTextStyle) {
+ font: font(17px semibold);
+ }
+ align: align(top);
+}
+infoStarsFeatureTitle: FlatLabel(defaultFlatLabel) {
+ textFg: windowBoldFg;
+ style: semiboldTextStyle;
+ minWidth: 10px;
+ maxHeight: 20px;
+}
+infoStarsFeatureAbout: FlatLabel(defaultFlatLabel) {
+ textFg: windowSubTextFg;
+ minWidth: 20px;
+}
+infoStarsFeatureIconPosition: point(3px, 7px);
+infoStarsFeatureMargin: margins(0px, 8px, 0px, 6px);
+infoStarsFeatureLabelLeft: 46px;
+infoStarsFeatureSkip: 2px;
+infoStarsCrown: icon {{ "limits/filled_rating_crown-24x24", windowFgActive }};
+infoStarsUnderstood: IconEmoji{
+ icon: icon {{ "limits/filled_understood-20x20", windowFgActive }};
+ padding: margins(0px, -1px, 0px, 0px);
}
-infoStarsRatingTooltip: defaultImportantTooltip;
collectionAbout: FlatLabel(defaultFlatLabel) {
minWidth: 256px;
diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
index 691fcbbea4..33d4ab8dad 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
@@ -651,8 +651,9 @@ Cover::Cover(
? std::make_unique(
this,
st::infoStarsRating,
- Data::StarsRatingValue(_peer),
- _parentForTooltip)
+ _controller->uiShow(),
+ _peer->isSelf() ? QString() : _peer->shortName(),
+ Data::StarsRatingValue(_peer))
: nullptr)
, _status(this, _st.status)
, _showLastSeen(this, tr::lng_status_lastseen_when(), _st.showLastSeen)
@@ -668,14 +669,6 @@ Cover::Cover(
if (!_peer->isMegagroup()) {
_status->setAttribute(Qt::WA_TransparentForMouseEvents);
if (const auto rating = _starsRating.get()) {
- _status->widthValue() | rpl::start_with_next([=](int width) {
- rating->setMinimalAddedWidth(width);
- }, rating->lifetime());
- const auto session = &_peer->session();
- rating->learnMoreRequests() | rpl::start_with_next([=] {
- const auto &appConfig = session->appConfig();
- UrlClickHandler::Open(appConfig.starsRatingLearnMoreUrl());
- }, rating->lifetime());
_statusShift = rating->collapsedWidthValue();
_statusShift.changes() | rpl::start_with_next([=] {
refreshStatusGeometry(width());
diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp
index 5f9183797e..844c68dfbd 100644
--- a/Telegram/SourceFiles/main/main_app_config.cpp
+++ b/Telegram/SourceFiles/main/main_app_config.cpp
@@ -246,12 +246,6 @@ QString AppConfig::ageVerifyBotUsername() const {
return get(u"verify_age_bot_username"_q, QString());
}
-QString AppConfig::starsRatingLearnMoreUrl() const {
- return get(
- u"stars_rating_learnmore_url"_q,
- u"https://telegram.org/blog"_q);
-}
-
int AppConfig::storiesAlbumsLimit() const {
return get(u"stories_albums_limit"_q, 100);
}
diff --git a/Telegram/SourceFiles/main/main_app_config.h b/Telegram/SourceFiles/main/main_app_config.h
index 11f6b66f34..91fac6690b 100644
--- a/Telegram/SourceFiles/main/main_app_config.h
+++ b/Telegram/SourceFiles/main/main_app_config.h
@@ -112,8 +112,6 @@ public:
[[nodiscard]] int ageVerifyMinAge() const;
[[nodiscard]] QString ageVerifyBotUsername() const;
- [[nodiscard]] QString starsRatingLearnMoreUrl() const;
-
[[nodiscard]] int storiesAlbumsLimit() const;
[[nodiscard]] int storiesAlbumLimit() const;
diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp
index d2cfeafca4..e72896a771 100644
--- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp
+++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp
@@ -451,7 +451,7 @@ void BoostBox(
: tr::lng_boost_channel_button();
}) | rpl::flatten_latest();
- const auto button = box->addButton(rpl::duplicate(submit), [=] {
+ box->addButton(rpl::duplicate(submit), [=] {
if (state->submitted) {
return;
} else if (state->data.current().nextLevelBoosts > 0
@@ -519,17 +519,6 @@ void BoostBox(
box->closeBox();
}
});
-
- rpl::combine(
- std::move(submit),
- box->widthValue()
- ) | rpl::start_with_next([=](const QString &, int width) {
- const auto &padding = st::boostBox.buttonPadding;
- button->resizeToWidth(width
- - padding.left()
- - padding.right());
- button->moveToLeft(padding.left(), button->y());
- }, button->lifetime());
}
object_ptr MakeLinkLabel(
@@ -788,20 +777,10 @@ void AskBoostBox(
data.group);
auto submit = tr::lng_boost_channel_ask_button();
- const auto button = box->addButton(rpl::duplicate(submit), [=] {
+ box->addButton(rpl::duplicate(submit), [=] {
QGuiApplication::clipboard()->setText(data.link);
box->uiShow()->showToast(tr::lng_username_copied(tr::now));
});
- rpl::combine(
- std::move(submit),
- box->widthValue()
- ) | rpl::start_with_next([=](const QString &, int width) {
- const auto &padding = st::boostBox.buttonPadding;
- button->resizeToWidth(width
- - padding.left()
- - padding.right());
- button->moveToLeft(padding.left(), button->y());
- }, button->lifetime());
}
void FillBoostLimit(
diff --git a/Telegram/SourceFiles/ui/controls/stars_rating.cpp b/Telegram/SourceFiles/ui/controls/stars_rating.cpp
index 23ec63146f..5fe6397149 100644
--- a/Telegram/SourceFiles/ui/controls/stars_rating.cpp
+++ b/Telegram/SourceFiles/ui/controls/stars_rating.cpp
@@ -7,7 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/controls/stars_rating.h"
+#include "info/profile/info_profile_icon.h"
#include "lang/lang_keys.h"
+#include "ui/effects/premium_bubble.h"
+#include "ui/effects/premium_graphics.h"
+#include "ui/layers/generic_box.h"
+#include "ui/layers/show.h"
+#include "ui/text/custom_emoji_helper.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.h"
@@ -17,25 +23,395 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#include "ui/ui_utility.h"
#include "styles/style_info.h"
+#include "styles/style_layers.h"
+#include "styles/style_premium.h"
+#include "styles/style_settings.h"
#include "styles/style_media_view.h"
+#include "styles/style_menu_icons.h"
namespace Ui {
namespace {
constexpr auto kAutoCollapseTimeout = 4 * crl::time(1000);
+using Counters = Data::StarsRating;
+
+struct Feature {
+ const style::icon &icon;
+ QString title;
+ TextWithEntities about;
+};
+
+[[nodiscard]] object_ptr MakeFeature(
+ QWidget *parent,
+ Feature feature,
+ const Text::MarkedContext &context) {
+ auto result = object_ptr>(
+ parent,
+ object_ptr(parent),
+ st::infoStarsFeatureMargin);
+ const auto widget = result->entity();
+ const auto icon = Ui::CreateChild(
+ widget,
+ feature.icon,
+ st::infoStarsFeatureIconPosition);
+ const auto title = Ui::CreateChild(
+ widget,
+ feature.title,
+ st::infoStarsFeatureTitle);
+ const auto about = Ui::CreateChild(
+ widget,
+ rpl::single(feature.about),
+ st::infoStarsFeatureAbout,
+ st::defaultPopupMenu,
+ context);
+ icon->show();
+ title->show();
+ about->show();
+ widget->widthValue(
+ ) | rpl::start_with_next([=](int width) {
+ const auto left = st::infoStarsFeatureLabelLeft;
+ const auto available = width - left;
+ title->resizeToWidth(available);
+ about->resizeToWidth(available);
+ auto top = 0;
+ title->move(left, top);
+ top += title->height() + st::infoStarsFeatureSkip;
+ about->move(left, top);
+ top += about->height();
+ widget->resize(width, top);
+ }, widget->lifetime());
+ return result;
+}
+
+[[nodiscard]] Fn CustomEmojiBadgeFactory(
+ const QString &text,
+ const style::color &bg,
+ const style::color &fg) {
+ return [=] {
+ auto string = Ui::Text::String(
+ st::settingsPremiumNewBadge.style,
+ text.toUpper());
+ const auto size = QSize(string.maxWidth(), string.minHeight());
+ const auto padding = st::settingsPremiumNewBadgePadding;
+ const auto full = size.grownBy(padding);
+ const auto ratio = style::DevicePixelRatio();
+
+ auto result = QImage(
+ full * ratio,
+ QImage::Format_ARGB32_Premultiplied);
+ result.setDevicePixelRatio(ratio);
+ result.fill(Qt::transparent);
+
+ auto p = QPainter(&result);
+ auto hq = PainterHighQualityEnabler(p);
+ p.setPen(Qt::NoPen);
+ p.setBrush(bg);
+
+ const auto r = padding.left();
+ p.drawRoundedRect(0, 0, full.width(), full.height(), r, r);
+
+ p.setPen(fg);
+ string.draw(p, { .position = { padding.left(), padding.top() } });
+
+ p.end();
+ return result;
+ };
+}
+
+[[nodiscard]] Counters AdjustByReached(Counters data) {
+ const auto reached = !data.nextLevelStars;
+ if (reached) {
+ --data.level;
+ data.stars = data.nextLevelStars = std::max({
+ data.stars,
+ data.thisLevelStars,
+ 1
+ });
+ data.thisLevelStars = 0;
+ } else {
+ data.stars = std::max(data.thisLevelStars, data.stars);
+ data.nextLevelStars = std::max(
+ data.nextLevelStars,
+ data.stars + 1);
+ }
+ return data;
+}
+
+[[nodiscard]] Fn BubbleTextFactory(int countForScale) {
+ return [=](int count) {
+ return (countForScale < 10'000)
+ ? QString::number(count)
+ : (countForScale < 10'000'000)
+ ? (QString::number((count / 100) / 10.) + 'K')
+ : (QString::number((count / 100'000) / 10.) + 'M');
+ };
+}
+
+void FillRatingLimit(
+ rpl::producer<> showFinished,
+ not_null container,
+ rpl::producer data,
+ style::margins limitLinePadding,
+ int starsForScale) {
+ const auto addSkip = [&](int skip) {
+ container->add(object_ptr(container, skip));
+ };
+
+ const auto ratio = [=](Counters rating) {
+ const auto min = rating.thisLevelStars;
+ const auto max = rating.nextLevelStars;
+
+ Assert(rating.stars >= min && rating.stars <= max);
+ const auto count = (max - min);
+ const auto index = (rating.stars - min);
+ if (!index) {
+ return 0.;
+ } else if (index == count) {
+ return 1.;
+ } else if (count == 2) {
+ return 0.5;
+ }
+ const auto available = st::boxWideWidth
+ - st::boxPadding.left()
+ - st::boxPadding.right();
+ const auto average = available / float64(count);
+ const auto levelWidth = [&](int add) {
+ return st::normalFont->width(
+ tr::lng_boost_level(
+ tr::now,
+ lt_count,
+ rating.level + add));
+ };
+ const auto paddings = 2 * st::premiumLineTextSkip;
+ const auto labelLeftWidth = paddings + levelWidth(0);
+ const auto labelRightWidth = paddings + levelWidth(1);
+ const auto first = std::max(average, labelLeftWidth * 1.);
+ const auto last = std::max(average, labelRightWidth * 1.);
+ const auto other = (available - first - last) / (count - 2);
+ return (first + (index - 1) * other) / available;
+ };
+
+ auto adjustedData = rpl::duplicate(data) | rpl::map(AdjustByReached);
+
+ auto bubbleRowState = rpl::duplicate(
+ adjustedData
+ ) | rpl::combine_previous(
+ Counters()
+ ) | rpl::map([=](Counters previous, Counters counters) {
+ return Premium::BubbleRowState{
+ .counter = counters.stars,
+ .ratio = ratio(counters),
+ .animateFromZero = (counters.level != previous.level),
+ .dynamic = true,
+ };
+ });
+ Premium::AddBubbleRow(
+ container,
+ st::boostBubble,
+ std::move(showFinished),
+ rpl::duplicate(bubbleRowState),
+ Premium::BubbleType::StarRating,
+ BubbleTextFactory(starsForScale),
+ &st::infoStarsCrown,
+ limitLinePadding);
+ addSkip(st::premiumLineTextSkip);
+
+ const auto level = [](int level) {
+ return tr::lng_boost_level(tr::now, lt_count, level);
+ };
+ auto limitState = std::move(
+ bubbleRowState
+ ) | rpl::map([](const Premium::BubbleRowState &state) {
+ return Premium::LimitRowState{
+ .ratio = state.ratio,
+ .animateFromZero = state.animateFromZero,
+ .dynamic = state.dynamic
+ };
+ });
+ auto left = rpl::duplicate(
+ adjustedData
+ ) | rpl::map([=](Counters counters) {
+ return level(counters.level);
+ });
+ auto right = rpl::duplicate(
+ adjustedData
+ ) | rpl::map([=](Counters counters) {
+ return level(counters.level + 1);
+ });
+ Premium::AddLimitRow(
+ container,
+ st::boostLimits,
+ Premium::LimitRowLabels{
+ .leftLabel = std::move(left),
+ .rightLabel = std::move(right),
+ .activeLineBg = [=] { return st::windowBgActive->b; },
+ },
+ std::move(limitState),
+ limitLinePadding);
+}
+
+object_ptr MakeBoostFeaturesBadge(
+ not_null parent,
+ rpl::producer text,
+ Fn bg) {
+ auto result = object_ptr(
+ 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;
+}
+
+void AboutRatingBox(
+ not_null box,
+ const QString &name,
+ Counters data) {
+ box->setWidth(st::boxWideWidth);
+ box->setStyle(st::boostBox);
+
+ struct State {
+ rpl::variable data;
+ rpl::variable full;
+ };
+ const auto state = box->lifetime().make_state();
+ state->data = std::move(data);
+
+ FillRatingLimit(
+ BoxShowFinishes(box),
+ box->verticalLayout(),
+ state->data.value(),
+ st::boxRowPadding,
+ data.stars);
+
+ box->setMaxHeight(st::boostBoxMaxHeight);
+ const auto close = box->addTopButton(
+ st::boxTitleClose,
+ [=] { box->closeBox(); });
+
+ auto title = tr::lng_stars_rating_title();;
+
+ auto text = !name.isEmpty()
+ ? tr::lng_stars_rating_about(
+ lt_name,
+ rpl::single(TextWithEntities{ name }),
+ Ui::Text::RichLangValue) | rpl::type_erased()
+ : tr::lng_stars_rating_about_your(
+ Ui::Text::RichLangValue) | rpl::type_erased();
+
+ box->addRow(
+ object_ptr(box, std::move(title), st::infoStarsTitle),
+ st::boxRowPadding + QMargins(0, st::boostTitleSkip / 2, 0, 0));
+
+ const auto aboutLabel = box->addRow(
+ object_ptr(
+ box,
+ std::move(text),
+ st::boostText),
+ (st::boxRowPadding
+ + QMargins(0, st::boostTextSkip, 0, st::boostBottomSkip)));
+ aboutLabel->setTryMakeSimilarLines(true);
+
+ auto helper = Ui::Text::CustomEmojiHelper();
+ const auto makeBadge = [&](
+ const QString &text,
+ const style::color &bg,
+ const style::color &fg) {
+ return helper.paletteDependent(
+ CustomEmojiBadgeFactory(text, bg, fg),
+ st::badgeEmojiMargin);
+ };
+ const auto makeActive = [&](const QString &text) {
+ return makeBadge(text, st::windowBgActive, st::windowFgActive);
+ };
+ const auto makeInactive = [&](const QString &text) {
+ return makeBadge(text, st::windowSubTextFg, st::windowFgActive);
+ };
+ const auto features = std::vector{
+ {
+ st::menuIconRatingGifts,
+ tr::lng_stars_title_gifts_telegram(tr::now),
+ tr::lng_stars_about_gifts_telegram(
+ tr::now,
+ lt_emoji,
+ makeActive(tr::lng_stars_rating_added(tr::now)),
+ Ui::Text::RichLangValue),
+ },
+ {
+ st::menuIconRatingUsers,
+ tr::lng_stars_title_gifts_users(tr::now),
+ tr::lng_stars_about_gifts_users(
+ tr::now,
+ lt_emoji,
+ makeActive(tr::lng_stars_rating_added(tr::now)),
+ Ui::Text::RichLangValue),
+ },
+ {
+ st::menuIconRatingRefund,
+ tr::lng_stars_title_refunds(tr::now),
+ tr::lng_stars_about_refunds(
+ tr::now,
+ lt_emoji,
+ makeInactive(tr::lng_stars_rating_deducted(tr::now)),
+ Ui::Text::RichLangValue),
+ },
+ };
+ const auto context = helper.context();
+ for (const auto &feature : features) {
+ box->addRow(MakeFeature(box, feature, context));
+ }
+ box->addButton(rpl::single(QString()), [=] {
+ box->closeBox();
+ })->setText(rpl::single(Ui::Text::IconEmoji(
+ &st::infoStarsUnderstood
+ ).append(' ').append(tr::lng_stars_rating_understood(tr::now))));
+}
+
} // namespace
StarsRating::StarsRating(
QWidget *parent,
const style::StarsRating &st,
- rpl::producer value,
- Fn()> parentForTooltip)
+ std::shared_ptr show,
+ const QString &name,
+ rpl::producer value)
: _widget(std::make_unique(parent))
, _st(st)
-, _parentForTooltip(std::move(parentForTooltip))
-, _value(std::move(value))
-, _collapseTimer([=] { _expanded = false; }) {
+, _show(std::move(show))
+, _name(name)
+, _value(std::move(value)) {
init();
}
@@ -43,18 +419,6 @@ StarsRating::~StarsRating() = default;
void StarsRating::init() {
_widget->setPointerCursor(true);
- _expanded.changes() | rpl::start_with_next([=](bool expanded) {
- _widget->setPointerCursor(!expanded);
- const auto from = expanded ? 0. : 1.;
- const auto till = expanded ? 1. : 0.;
- _expandedAnimation.start([=] {
- updateWidth();
- if (!_expandedAnimation.animating()) {
- updateStarsTooltipGeometry();
- }
- }, from, till, st::slideDuration);
- toggleTooltips(expanded);
- }, lifetime());
_widget->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(_widget.get());
@@ -65,189 +429,43 @@ void StarsRating::init() {
if (!_value.current()) {
return;
}
- _expanded = true;
- _collapseTimer.callOnce(kAutoCollapseTimeout);
+ _show->show(Box(AboutRatingBox, _name, _value.current()));
});
const auto added = _st.margin + _st.padding;
- const auto border = 2 * _st.border;
const auto fontHeight = _st.style.font->height;
- const auto height = added.top() + fontHeight + added.bottom() + border;
+ const auto height = added.top() + fontHeight + added.bottom();
_widget->resize(_widget->width(), height);
- _value.value() | rpl::start_with_next([=](Data::StarsRating rating) {
+ _value.value() | rpl::start_with_next([=](Counters rating) {
if (!rating) {
_widget->resize(0, _widget->height());
_collapsedWidthValue = 0;
- _expanded = false;
- updateExpandedWidth();
- _expandedAnimation.stop();
return;
}
updateTexts(rating);
}, lifetime());
}
-void StarsRating::updateTexts(Data::StarsRating rating) {
+void StarsRating::updateTexts(Counters rating) {
_collapsedText.setText(
_st.style,
Lang::FormatCountDecimal(rating.level));
- _expandedText.setText(
- _st.style,
- tr::lng_boost_level(tr::now, lt_count_decimal, rating.level));
- _nextText.setText(
- _st.style,
- (rating.nextLevelStars
- ? Lang::FormatCountDecimal(rating.level + 1)
- : QString()));
const auto added = _st.padding;
- const auto border = 2 * _st.border;
- const auto add = added.left() + added.right() + border;
- const auto min = _expandedText.maxWidth() + _nextText.maxWidth();
+ const auto add = added.left() + added.right();
const auto height = _widget->height();
- _minimalContentWidth = add + min + _st.minSkip;
_collapsedWidthValue = _st.margin.right()
+ std::max(
add + _collapsedText.maxWidth(),
height - _st.margin.top() - _st.margin.bottom());
- updateExpandedWidth();
updateWidth();
}
-void StarsRating::updateExpandedWidth() {
- _expandedWidthValue = _st.margin.right() + std::max(
- _collapsedWidthValue.current() + _minimalAddedWidth.current(),
- _minimalContentWidth.current());
-}
-
void StarsRating::updateWidth() {
- const auto widthToRight = anim::interpolate(
- _collapsedWidthValue.current(),
- _expandedWidthValue.current(),
- _expandedAnimation.value(_expanded.current() ? 1. : 0.));
+ const auto widthToRight = _collapsedWidthValue.current();
_widget->resize(_st.margin.left() + widthToRight, _widget->height());
_widget->update();
- updateStarsTooltipGeometry();
-}
-
-void StarsRating::toggleTooltips(bool shown) {
- if (!shown) {
- if (const auto strong = _about.get()) {
- strong->hideAnimated();
- }
- if (const auto strong = _stars.release()) {
- strong->toggleAnimated(false);
- }
- return;
- }
- const auto value = _value.current();
- const auto parent = _parentForTooltip
- ? _parentForTooltip().get()
- : _widget->window();
- const auto text = value.nextLevelStars
- ? (Lang::FormatCountDecimal(value.currentStars)
- + u" / "_q
- + Lang::FormatCountDecimal(value.nextLevelStars))
- : Lang::FormatCountDecimal(value.currentStars);
- _stars = std::make_unique(
- parent,
- Ui::MakeNiceTooltipLabel(
- _widget.get(),
- rpl::single(TextWithEntities{ text }),
- st::storiesInfoTooltipMaxWidth,
- st::storiesInfoTooltipLabel),
- st::infoStarsRatingTooltip);
- const auto stars = _stars.get();
- const auto weak = QPointer(stars);
- const auto destroy = [=] {
- delete weak.data();
- };
- stars->setAttribute(Qt::WA_TransparentForMouseEvents);
- stars->setHiddenCallback(destroy);
- updateStarsTooltipGeometry();
- stars->toggleAnimated(true);
-
- _aboutSt = std::make_unique(st::defaultMultilineToast);
- const auto learn = tr::lng_stars_rating_learn_more(tr::now);
- _aboutSt->padding.setRight(
- (st::infoStarsRatingLearn.style.font->width(learn)
- - st::infoStarsRatingLearn.width));
-
- _about = Ui::Toast::Show(parent, {
- .text = tr::lng_stars_rating_tooltip(
- tr::now,
- Ui::Text::WithEntities),
- .st = _aboutSt.get(),
- .attach = RectPart::Top,
- .dark = true,
- .adaptive = true,
- .acceptinput = true,
- .duration = kAutoCollapseTimeout,
- });
- const auto strong = _about.get();
- if (!strong) {
- return;
- }
- const auto widget = strong->widget();
- const auto hideToast = [weak = _about] {
- if (const auto strong = weak.get()) {
- strong->hideAnimated();
- }
- };
-
- const auto button = Ui::CreateChild(
- widget.get(),
- rpl::single(learn),
- st::infoStarsRatingLearn);
- button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
- button->show();
- rpl::combine(
- widget->sizeValue(),
- button->sizeValue()
- ) | rpl::start_with_next([=](QSize outer, QSize inner) {
- button->moveToRight(
- 0,
- (outer.height() - inner.height()) / 2,
- outer.width());
- }, widget->lifetime());
- button->setClickedCallback([=] {
- _learnMoreRequests.fire({});
- });
-}
-
-void StarsRating::updateStarsTooltipGeometry() {
- if (!_stars) {
- return;
- }
- const auto weakParent = base::make_weak(_stars->parentWidget());
- const auto weak = base::make_weak(_widget.get());
- const auto point = _st.margin.left()
- + _st.border
- + (_activeWidth / (_value.current().nextLevelStars ? 1 : 2));
- const auto countPosition = [=](QSize size) {
- const auto strong = weak.get();
- const auto parent = weakParent.get();
- if (!strong || !parent) {
- return QPoint();
- }
- const auto geometry = Ui::MapFrom(parent, strong, strong->rect());
- const auto shift = size.width() / 2;
- const auto left = geometry.x() + point - shift;
- const auto margin = st::defaultImportantTooltip.margin;
- return QPoint(
- std::min(
- std::max(left, margin.left()),
- parent->width() - size.width() - margin.right()),
- geometry.y() + geometry.height());
- };
- _stars->pointAt(
- Ui::MapFrom(
- _stars->parentWidget(),
- _widget.get(),
- QRect(point, 0, st::lineWidth, _widget->height())),
- RectPart::Bottom,
- countPosition);
}
void StarsRating::raise() {
@@ -263,92 +481,27 @@ void StarsRating::paint(QPainter &p) {
if (outer.isEmpty()) {
return;
}
- const auto border = _st.border;
- const auto middle = outer.marginsRemoved(
- { border, border, border, border });
- const auto mradius = middle.height() / 2.;
- const auto inner = middle.marginsRemoved(_st.padding);
-
- const auto expanded = _expandedAnimation.value(
- _expanded.current() ? 1. : 0.);
+ const auto inner = outer.marginsRemoved(_st.padding);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
- p.setBrush(_st.inactiveBg);
- const auto oradius = outer.height() / 2.;
- p.drawRoundedRect(outer, oradius, oradius);
p.setBrush(_st.activeBg);
const auto value = _value.current();
- const auto expandedRatio = (value.nextLevelStars > value.levelStars)
- ? ((value.currentStars - value.levelStars)
- / float64(value.nextLevelStars - value.levelStars))
- : 1.;
- const auto expandedFilled = (expandedRatio < 1.)
- ? (_st.padding.left()
- + _expandedText.maxWidth()
- + _st.padding.right()
- + expandedRatio * (middle.width()
- - _st.padding.left()
- - _expandedText.maxWidth()
- - _st.padding.right()
- - _st.padding.left()
- - _nextText.maxWidth()
- - _st.padding.right()))
- : middle.width();
- const auto collapsedFilled = _collapsedWidthValue.current()
- - _st.margin.right()
- - 2 * _st.border;
- _activeWidth = anim::interpolate(
- collapsedFilled,
- expandedFilled,
- expanded);
- p.drawRoundedRect(
- middle.x(),
- middle.y(),
- _activeWidth,
- middle.height(),
- mradius,
- mradius);
+ const auto radius = outer.height() / 2.;
+ p.drawRoundedRect(outer, radius, radius);
p.setPen(_st.activeFg);
- if (expanded < 1.) {
- p.setOpacity(1. - expanded);
- const auto skip = (inner.width() - _collapsedText.maxWidth()) / 2;
- _collapsedText.draw(p, {
- .position = inner.topLeft() + QPoint(skip, 0),
- .availableWidth = _collapsedText.maxWidth(),
- });
- }
- if (expanded > 0.) {
- p.setOpacity(expanded);
- _expandedText.draw(p, {
- .position = inner.topLeft(),
- .availableWidth = _expandedText.maxWidth(),
- });
-
- p.setPen(_st.inactiveFg);
- _nextText.draw(p, {
- .position = (inner.topLeft()
- + QPoint(inner.width() - _nextText.maxWidth(), 0)),
- .availableWidth = _nextText.maxWidth(),
- });
- }
-}
-
-void StarsRating::setMinimalAddedWidth(int addedWidth) {
- _minimalAddedWidth = addedWidth + (_st.style.font->spacew * 2);
- updateExpandedWidth();
- updateWidth();
+ const auto skip = (inner.width() - _collapsedText.maxWidth()) / 2;
+ _collapsedText.draw(p, {
+ .position = inner.topLeft() + QPoint(skip, 0),
+ .availableWidth = _collapsedText.maxWidth(),
+ });
}
rpl::producer StarsRating::collapsedWidthValue() const {
return _collapsedWidthValue.value();
}
-rpl::producer<> StarsRating::learnMoreRequests() const {
- return _learnMoreRequests.events();
-}
-
rpl::lifetime &StarsRating::lifetime() {
return _widget->lifetime();
}
diff --git a/Telegram/SourceFiles/ui/controls/stars_rating.h b/Telegram/SourceFiles/ui/controls/stars_rating.h
index 7af2a41c62..b72f061d8f 100644
--- a/Telegram/SourceFiles/ui/controls/stars_rating.h
+++ b/Telegram/SourceFiles/ui/controls/stars_rating.h
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
-#include "base/timer.h"
#include "base/weak_ptr.h"
#include "data/data_peer_common.h"
@@ -25,22 +24,22 @@ namespace Ui {
class ImportantTooltip;
class AbstractButton;
class FlatLabel;
+class Show;
class StarsRating final {
public:
StarsRating(
QWidget *parent,
const style::StarsRating &st,
- rpl::producer value,
- Fn()> parentForTooltip);
+ std::shared_ptr show,
+ const QString &name,
+ rpl::producer value);
~StarsRating();
void raise();
void moveTo(int x, int y);
- void setMinimalAddedWidth(int addedWidth);
[[nodiscard]] rpl::producer collapsedWidthValue() const;
- [[nodiscard]] rpl::producer<> learnMoreRequests() const;
[[nodiscard]] rpl::lifetime &lifetime();
@@ -48,35 +47,17 @@ private:
void init();
void paint(QPainter &p);
void updateTexts(Data::StarsRating rating);
- void updateExpandedWidth();
void updateWidth();
- void toggleTooltips(bool shown);
- void updateStarsTooltipGeometry();
const std::unique_ptr _widget;
const style::StarsRating &_st;
- const Fn()> _parentForTooltip;
-
- std::unique_ptr _aboutSt;
- base::weak_ptr _about;
- std::unique_ptr _stars;
+ const std::shared_ptr _show;
+ const QString _name;
Ui::Text::String _collapsedText;
- Ui::Text::String _expandedText;
- Ui::Text::String _nextText;
rpl::variable _value;
rpl::variable _collapsedWidthValue;
- rpl::variable _expandedWidthValue;
- rpl::variable _minimalContentWidth;
- rpl::variable _minimalAddedWidth;
- rpl::variable _expanded = false;
- Ui::Animations::Simple _expandedAnimation;
- mutable int _activeWidth = 0;
-
- rpl::event_stream<> _learnMoreRequests;
-
- base::Timer _collapseTimer;
};
diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style
index 4abbea6f14..f5353f100c 100644
--- a/Telegram/SourceFiles/ui/effects/premium.style
+++ b/Telegram/SourceFiles/ui/effects/premium.style
@@ -294,6 +294,7 @@ boostBottomSkip: 6px;
boostBox: Box(premiumPreviewDoubledLimitsBox) {
buttonPadding: margins(16px, 12px, 16px, 12px);
buttonHeight: 42px;
+ buttonWide: true;
button: RoundButton(defaultActiveButton) {
height: 42px;
textTop: 12px;
diff --git a/Telegram/SourceFiles/ui/effects/premium_bubble.cpp b/Telegram/SourceFiles/ui/effects/premium_bubble.cpp
index 6106201af4..a507f78816 100644
--- a/Telegram/SourceFiles/ui/effects/premium_bubble.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_bubble.cpp
@@ -422,7 +422,8 @@ void BubbleWidget::paintEvent(QPaintEvent *e) {
_bubble.paintBubble(p, bubbleRect, [&] {
switch (_type) {
- case BubbleType::NoPremium: return st::windowBgActive->b;
+ case BubbleType::NoPremium:
+ case BubbleType::StarRating: return st::windowBgActive->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 88e37771d0..404a75abef 100644
--- a/Telegram/SourceFiles/ui/effects/premium_bubble.h
+++ b/Telegram/SourceFiles/ui/effects/premium_bubble.h
@@ -88,6 +88,7 @@ struct BubbleRowState {
};
enum class BubbleType : uchar {
+ StarRating,
NoPremium,
Premium,
Credits,
diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
index 5b277429d4..2ace588b1f 100644
--- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
@@ -504,9 +504,17 @@ void AddLimitRow(
LimitRowLabels labels,
rpl::producer state,
const style::margins &padding) {
- parent->add(
+ const auto color = std::move(labels.activeLineBg);
+ const auto line = parent->add(
object_ptr(parent, st, std::move(labels), std::move(state)),
padding);
+ if (color) {
+ line->setColorOverride(color());
+
+ style::PaletteChanged() | rpl::start_with_next([=] {
+ line->setColorOverride(color());
+ }, line->lifetime());
+ }
}
void AddAccountsRow(
diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.h b/Telegram/SourceFiles/ui/effects/premium_graphics.h
index 86b136457b..049788ddc0 100644
--- a/Telegram/SourceFiles/ui/effects/premium_graphics.h
+++ b/Telegram/SourceFiles/ui/effects/premium_graphics.h
@@ -65,6 +65,7 @@ struct LimitRowLabels {
rpl::producer leftCount;
rpl::producer rightLabel;
rpl::producer rightCount;
+ Fn activeLineBg;
};
struct LimitRowState {
diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style
index 3c37626681..f3c1eb27b4 100644
--- a/Telegram/SourceFiles/ui/menu_icons.style
+++ b/Telegram/SourceFiles/ui/menu_icons.style
@@ -185,7 +185,10 @@ menuIconPayment: icon {{ "payments/payment_card", menuIconColor }};
menuIconOrderPrice: icon {{ "menu/order_price", menuIconColor }};
menuIconOrderDate: icon {{ "menu/order_date", menuIconColor }};
menuIconOrderNumber: icon {{ "menu/order_number", menuIconColor }};
-menuIconAdd: icon{{ "menu/add", menuIconColor }};
+menuIconAdd: icon {{ "menu/add", menuIconColor }};
+menuIconRatingGifts: icon {{ "menu/rating_gifts-24x24", menuIconColor }};
+menuIconRatingUsers: icon {{ "menu/users_stars-24x24", menuIconColor }};
+menuIconRatingRefund: icon {{ "menu/rating_refund-24x24", menuIconColor }};
menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
menuIconTTLAnyTextPosition: point(11px, 22px);
diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index ea3c209939..cd01ab20e4 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit ea3c209939fc10e93750c4207f5aa1fe6af66e99
+Subproject commit cd01ab20e485aabd0991bb43c7d8302cd0a2ebc4