Added support for credits in messages for starting and ending giveaways.

This commit is contained in:
23rd 2024-08-28 22:41:08 +03:00 committed by John Preston
parent 4520480604
commit 202c81b2e5
10 changed files with 183 additions and 89 deletions

View file

@ -2845,6 +2845,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_prizes_additional_with" = "with";
"lng_prizes_about#one" = "**{count}** Telegram Premium Subscription {duration}.";
"lng_prizes_about#other" = "**{count}** Telegram Premium Subscriptions {duration}.";
"lng_prizes_credits_about_single" = "**{amount}** will be distributed to the **1** winner.";
"lng_prizes_credits_about#one" = "**{amount}** will be distributed among {count} winner.";
"lng_prizes_credits_about#other" = "**{amount}** will be distributed among {count} winners.";
"lng_prizes_credits_about_amount#one" = "{count} Star";
"lng_prizes_credits_about_amount#other" = "{count} Stars";
"lng_prizes_participants" = "Participants";
"lng_prizes_participants_all#one" = "All subscribers of the channel:";
"lng_prizes_participants_all#other" = "All subscribers of the channels:";
@ -2918,6 +2923,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_prizes_results_more#other" = "and {count} more!";
"lng_prizes_results_one" = "The winner received their gift link in a private message.";
"lng_prizes_results_all" = "All winners received gift links in private messages.";
"lng_prizes_credits_results_one#one" = "The winner received {count} Star.";
"lng_prizes_credits_results_one#other" = "The winner received {count} Stars.";
"lng_prizes_credits_results_all#one" = "All winners received {count} Star in total.";
"lng_prizes_credits_results_all#other" = "All winners received {count} Stars in total.";
"lng_prizes_results_some" = "Some winners couldn't be selected.";
"lng_gift_link_title" = "Gift Link";

View file

@ -471,8 +471,8 @@ GiveawayStart ComputeGiveawayStartData(
auto result = GiveawayStart{
.untilDate = data.vuntil_date().v,
.quantity = data.vquantity().v,
.months = data.vmonths().value_or_empty(),AssertIsDebug()
//.stars = data.vstars().value_or_empty(),
.months = data.vmonths().value_or_empty(),
.credits = data.vstars().value_or_empty(),
.all = !data.is_only_new_subscribers(),
};
result.channels.reserve(data.vchannels().v.size());
@ -503,8 +503,8 @@ GiveawayResults ComputeGiveawayResultsData(
.additionalPeersCount = additional.value_or_empty(),
.winnersCount = data.vwinners_count().v,
.unclaimedCount = data.vunclaimed_count().v,
.months = data.vmonths().value_or_empty(), AssertIsDebug()
//.stars = data.vstars().value_or_empty(),
.months = data.vmonths().value_or_empty(),
.credits = data.vstars().value_or_empty(),
.refunded = data.is_refunded(),
.all = !data.is_only_new_subscribers(),
};

View file

@ -108,6 +108,7 @@ struct GiveawayStart {
TimeId untilDate = 0;
int quantity = 0;
int months = 0;
uint64 credits = 0;
bool all = false;
};
@ -121,6 +122,7 @@ struct GiveawayResults {
int winnersCount = 0;
int unclaimedCount = 0;
int months = 0;
uint64 credits = 0;
bool refunded = false;
bool all = false;
};

View file

@ -21,11 +21,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_helpers.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/effects/credits_graphics.h"
#include "ui/text/text_utilities.h"
#include "styles/style_chat.h"
namespace HistoryView {
constexpr auto kOutlineRatio = 0.85;
auto GenerateGiveawayStart(
not_null<Element*> parent,
not_null<Data::GiveawayStart*> data)
@ -49,10 +52,20 @@ auto GenerateGiveawayStart(
nullptr,
sticker,
st::chatGiveawayStickerPadding,
tr::lng_prizes_badge(
tr::now,
lt_amount,
QString::number(quantity))));
data->credits
? QString::number(data->credits)
: tr::lng_prizes_badge(
tr::now,
lt_amount,
QString::number(quantity)),
data->credits
? Ui::CreditsWhiteDoubledIcon(
st::chatGiveawayCreditsIconHeight,
kOutlineRatio)
: QImage(),
data->credits
? std::make_optional(st::creditsBg3->c)
: std::nullopt));
auto pushText = [&](
TextWithEntities text,
@ -83,8 +96,29 @@ auto GenerateGiveawayStart(
st::chatGiveawayPrizesWithPadding));
}
pushText(
tr::lng_prizes_about(
pushText((data->credits && (quantity == 1))
? tr::lng_prizes_credits_about_single(
tr::now,
lt_amount,
tr::lng_prizes_credits_about_amount(
tr::now,
lt_count,
data->credits,
Ui::Text::RichLangValue),
Ui::Text::RichLangValue)
: (data->credits && (quantity > 1))
? tr::lng_prizes_credits_about(
tr::now,
lt_count,
quantity,
lt_amount,
tr::lng_prizes_credits_about_amount(
tr::now,
lt_count,
data->credits,
Ui::Text::RichLangValue),
Ui::Text::RichLangValue)
: tr::lng_prizes_about(
tr::now,
lt_count,
quantity,
@ -186,10 +220,20 @@ auto GenerateGiveawayResults(
nullptr,
sticker,
st::chatGiveawayStickerPadding,
tr::lng_prizes_badge(
tr::now,
lt_amount,
QString::number(quantity))));
data->credits
? QString::number(data->credits)
: tr::lng_prizes_badge(
tr::now,
lt_amount,
QString::number(quantity)),
data->credits
? Ui::CreditsWhiteDoubledIcon(
st::chatGiveawayCreditsIconHeight,
kOutlineRatio)
: QImage(),
data->credits
? std::make_optional(st::creditsBg3->c)
: std::nullopt));
auto pushText = [&](
TextWithEntities text,
@ -237,7 +281,17 @@ auto GenerateGiveawayResults(
data->winnersCount - data->winners.size())),
st::chatGiveawayNoCountriesTitleMargin);
}
pushText({ data->unclaimedCount
pushText({ (data->credits && isSingleWinner)
? tr::lng_prizes_credits_results_one(
tr::now,
lt_count,
data->credits)
: (data->credits && !isSingleWinner)
? tr::lng_prizes_credits_results_all(
tr::now,
lt_count,
data->credits)
: data->unclaimedCount
? tr::lng_prizes_results_some(tr::now)
: isSingleWinner
? tr::lng_prizes_results_one(tr::now)

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/dynamic_image.h"
#include "ui/dynamic_thumbnails.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/round_rect.h"
#include "styles/style_chat.h"
@ -438,9 +439,13 @@ StickerWithBadgePart::StickerWithBadgePart(
Element *replacing,
Fn<Data()> lookup,
QMargins padding,
QString badge)
: _sticker(parent, replacing, std::move(lookup), padding)
, _badgeText(badge) {
QString badge,
QImage customLeftIcon,
std::optional<QColor> colorOverride)
: _customLeftIcon(std::move(customLeftIcon))
, _sticker(parent, replacing, std::move(lookup), padding)
, _badgeText(badge)
, _colorOverride(std::move(colorOverride)) {
}
void StickerWithBadgePart::draw(
@ -500,10 +505,13 @@ void StickerWithBadgePart::paintBadge(
{
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(context.messageStyle()->msgFileBg);
if (_colorOverride) {
p.setBrush(*_colorOverride);
} else {
p.setBrush(context.messageStyle()->msgFileBg);
}
const auto half = st::chatGiveawayBadgeStroke / 2.;
const auto inner = QRectF(rect).marginsRemoved(
{ half, half, half, half });
const auto inner = QRectF(rect) - Margins(half);
const auto radius = inner.height() / 2.;
p.drawRoundedRect(inner, radius, radius);
}
@ -534,9 +542,12 @@ void StickerWithBadgePart::validateBadge(
const auto &font = st::chatGiveawayBadgeFont;
_badgeFg = badgeFg;
_badgeBorder = badgeBorder;
const auto width = font->width(_badgeText);
const auto iconWidth = _customLeftIcon.isNull()
? 0
: (_customLeftIcon.width() / style::DevicePixelRatio());
const auto width = font->width(_badgeText) + iconWidth;
const auto inner = QRect(0, 0, width, font->height);
const auto rect = inner.marginsAdded(st::chatGiveawayBadgePadding);
const auto rect = inner + st::chatGiveawayBadgePadding;
const auto size = rect.size();
const auto ratio = style::DevicePixelRatio();
_badge = QImage(size * ratio, QImage::Format_ARGB32_Premultiplied);
@ -548,17 +559,27 @@ void StickerWithBadgePart::validateBadge(
p.setPen(QPen(_badgeBorder, st::chatGiveawayBadgeStroke * 1.));
p.setBrush(Qt::NoBrush);
const auto half = st::chatGiveawayBadgeStroke / 2.;
const auto smaller = QRectF(
rect.translated(-rect.topLeft())
).marginsRemoved({ half, half, half, half });
const auto left = _customLeftIcon.isNull()
? st::chatGiveawayBadgePadding.left()
: (st::chatGiveawayBadgePadding.left() - half * 2);
const auto smaller = QRectF(rect.translated(-rect.topLeft()))
- Margins(half);
const auto radius = smaller.height() / 2.;
p.drawRoundedRect(smaller, radius, radius);
p.setPen(_badgeFg);
p.setFont(font);
p.drawText(
st::chatGiveawayBadgePadding.left(),
left + iconWidth,
st::chatGiveawayBadgePadding.top() + font->ascent,
_badgeText);
if (!_customLeftIcon.isNull()) {
const auto iconHeight = _customLeftIcon.height()
/ style::DevicePixelRatio();
p.drawImage(
left,
half + (inner.height() - iconHeight) / 2,
_customLeftIcon);
}
}
PeerBubbleListPart::PeerBubbleListPart(

View file

@ -227,7 +227,9 @@ public:
Element *replacing,
Fn<Data()> lookup,
QMargins padding,
QString badge);
QString badge,
QImage customLeftIcon,
std::optional<QColor> colorOverride);
void draw(
Painter &p,
@ -252,12 +254,14 @@ private:
void validateBadge(const PaintContext &context) const;
void paintBadge(Painter &p, const PaintContext &context) const;
const QImage _customLeftIcon;
StickerInBubblePart _sticker;
QString _badgeText;
mutable QColor _badgeFg;
mutable QColor _badgeBorder;
mutable QImage _badge;
mutable QImage _badgeCache;
std::optional<QColor> _colorOverride;
};

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/channel_statistics/boosts/giveaway/giveaway_type_row.h"
#include "lang/lang_keys.h"
#include "ui/effects/credits_graphics.h"
#include "ui/effects/premium_graphics.h"
#include "ui/painter.h"
#include "ui/rect.h"
@ -18,67 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_giveaway.h"
#include "styles/style_statistics.h"
#include <QtSvg/QSvgRenderer>
namespace Giveaway {
namespace {
[[nodiscard]] QImage CreditsCustomUserpic(int photoSize) {
auto svg = QSvgRenderer(Ui::Premium::Svg());
auto result = QImage(
Size(photoSize) * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
result.setDevicePixelRatio(style::DevicePixelRatio());
constexpr auto kPoints = uint(16);
constexpr auto kAngleStep = 2. * M_PI / kPoints;
constexpr auto kOutlineWidth = 1.6;
constexpr auto kStarShift = 3.8;
const auto userpicRect = Rect(Size(photoSize));
const auto starRect = userpicRect - Margins(userpicRect.width() / 4.);
const auto starSize = starRect.size();
const auto drawSingle = [&](QPainter &q) {
const auto s = style::ConvertFloatScale(kOutlineWidth);
q.save();
q.setCompositionMode(QPainter::CompositionMode_Clear);
for (auto i = 0; i < kPoints; ++i) {
const auto angle = i * kAngleStep;
const auto x = s * std::cos(angle);
const auto y = s * std::sin(angle);
svg.render(&q, QRectF(QPointF(x, y), starSize));
}
q.setCompositionMode(QPainter::CompositionMode_SourceOver);
svg.render(&q, Rect(starSize));
q.restore();
};
{
auto p = QPainter(&result);
p.setPen(Qt::NoPen);
p.setBrush(st::lightButtonFg);
p.translate(starRect.topLeft());
p.translate(style::ConvertFloatScale(kStarShift) / 2., 0);
drawSingle(p);
{
// Remove the previous star at bottom.
p.setCompositionMode(QPainter::CompositionMode_Clear);
p.save();
p.resetTransform();
p.fillRect(
userpicRect.x(),
userpicRect.y(),
userpicRect.width() / 2.,
userpicRect.height(),
Qt::transparent);
p.restore();
}
p.translate(-style::ConvertFloatScale(kStarShift), 0);
drawSingle(p);
}
return result;
}
} // namespace
constexpr auto kColorIndexSpecific = int(4);
constexpr auto kColorIndexRandom = int(2);
@ -129,7 +70,7 @@ GiveawayTypeRow::GiveawayTypeRow(
QString())
, _badge(std::move(badge)) {
if (_type == Type::Credits) {
_customUserpic = CreditsCustomUserpic(_st.photoSize);
_customUserpic = Ui::CreditsWhiteDoubledIcon(_st.photoSize, 1.);
}
std::move(
subtitle

View file

@ -1009,6 +1009,7 @@ chatGiveawayEndDateMargin: margins(11px, 0px, 11px, 16px);
chatGiveawayPeerSize: 32px;
chatGiveawayPeerPadding: margins(5px, 7px, 12px, 0px);
chatGiveawayPeerSkip: 8px;
chatGiveawayCreditsIconHeight: 19px;
chatSimilarRadius: 12px;
chatSimilarArrowSize: 6px;

View file

@ -472,4 +472,64 @@ TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) {
TextWithEntities::Simple);
}
QImage CreditsWhiteDoubledIcon(int size, float64 outlineRatio) {
auto svg = QSvgRenderer(Ui::Premium::Svg());
auto result = QImage(
Size(size) * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
result.setDevicePixelRatio(style::DevicePixelRatio());
// constexpr auto kIdealSize = 42;
constexpr auto kPoints = uint(16);
constexpr auto kAngleStep = 2. * M_PI / kPoints;
constexpr auto kOutlineWidth = 1.6;
constexpr auto kStarShift = 3.8;
const auto userpicRect = Rect(Size(size));
const auto starRect = userpicRect - Margins(userpicRect.width() / 4.);
const auto starSize = starRect.size();
const auto drawSingle = [&](QPainter &q) {
const auto s = style::ConvertFloatScale(kOutlineWidth * outlineRatio);
q.save();
q.setCompositionMode(QPainter::CompositionMode_Clear);
for (auto i = 0; i < kPoints; ++i) {
const auto angle = i * kAngleStep;
const auto x = s * std::cos(angle);
const auto y = s * std::sin(angle);
svg.render(&q, QRectF(QPointF(x, y), starSize));
}
q.setCompositionMode(QPainter::CompositionMode_SourceOver);
svg.render(&q, Rect(starSize));
q.restore();
};
{
auto p = QPainter(&result);
p.setPen(Qt::NoPen);
p.setBrush(st::lightButtonFg);
p.translate(starRect.topLeft());
p.translate(
style::ConvertFloatScale(kStarShift * outlineRatio) / 2.,
0);
drawSingle(p);
{
// Remove the previous star at bottom.
p.setCompositionMode(QPainter::CompositionMode_Clear);
p.save();
p.resetTransform();
p.fillRect(
userpicRect.x(),
userpicRect.y(),
userpicRect.width() / 2.,
userpicRect.height(),
Qt::transparent);
p.restore();
}
p.translate(
-style::ConvertFloatScale(kStarShift * outlineRatio),
0);
drawSingle(p);
}
return result;
}
} // namespace Ui

View file

@ -73,4 +73,6 @@ Fn<PaintRoundImageCallback(Fn<void()>)> PaintPreviewCallback(
[[nodiscard]] TextWithEntities GenerateEntryName(
const Data::CreditsHistoryEntry &entry);
[[nodiscard]] QImage CreditsWhiteDoubledIcon(int size, float64 outlineRatio);
} // namespace Ui