Show paid stars information above a message.

This commit is contained in:
John Preston 2025-02-19 13:55:46 +04:00
parent 4729e51e14
commit 5b809c4fc6
9 changed files with 94 additions and 10 deletions

View file

@ -2170,6 +2170,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_paid_message_sent#other" = "You paid {count} Stars to send a message";
"lng_action_paid_message_got#one" = "You received {count} Star from {name}";
"lng_action_paid_message_got#other" = "You received {count} Stars from {name}";
"lng_action_paid_message_group#one" = "{from} paid {count} Star to send a message";
"lng_action_paid_message_group#other" = "{from} paid {count} Stars to send a message";
"lng_similar_channels_title" = "Similar channels";
"lng_similar_channels_view_all" = "View all";

View file

@ -229,6 +229,7 @@ void SendExistingMedia(
.replyTo = action.replyTo,
.date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId,
}, media, caption);
@ -411,6 +412,7 @@ bool SendDice(MessageToSend &message) {
.replyTo = action.replyTo,
.date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId,
}, TextWithEntities(), MTP_messageMediaDice(

View file

@ -3373,12 +3373,12 @@ void ApiWrap::forwardMessages(
const auto requestType = Data::Histories::RequestType::Send;
const auto idsCopy = localIds;
const auto scheduled = action.options.scheduled;
auto paidStars = std::min(
const auto starsPaid = std::min(
action.options.starsApproved,
int(ids.size() * peer->starsPerMessageChecked()));
auto oneFlags = sendFlags;
if (paidStars) {
action.options.starsApproved -= paidStars;
if (starsPaid) {
action.options.starsApproved -= starsPaid;
oneFlags |= SendFlag::f_allow_paid_stars;
}
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
@ -3393,7 +3393,7 @@ void ApiWrap::forwardMessages(
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTPint(), // video_timestamp
MTP_long(paidStars)
MTP_long(starsPaid)
)).done([=](const MTPUpdates &result) {
if (!scheduled) {
this->updates().checkForSentToScheduled(result);
@ -3438,6 +3438,7 @@ void ApiWrap::forwardMessages(
.replyTo = { .topicRootId = topMsgId },
.date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.starsPaid = action.options.starsApproved,
.postAuthor = NewMessagePostAuthor(action),
// forwarded messages don't have effects
@ -3906,6 +3907,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
.replyTo = action.replyTo,
.date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId,
}, sending, media);
@ -4092,6 +4094,7 @@ void ApiWrap::sendInlineResult(
.replyTo = action.replyTo,
.date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.viaBotId = ((bot && !action.options.hideViaBot)
? peerToUser(bot->id)
: UserId()),

View file

@ -460,6 +460,9 @@ not_null<HistoryItem*> History::createItem(
});
if (newMessage && result->out() && result->isRegular()) {
session().topPeers().increment(peer, result->date());
if (result->starsPaid()) {
session().credits().load(true);
}
}
return result;
}

View file

@ -401,6 +401,7 @@ HistoryItem::HistoryItem(
.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0),
.date = data.vdate().v,
.shortcutId = data.vquick_reply_shortcut_id().value_or_empty(),
.starsPaid = int(data.vpaid_message_stars().value_or_empty()),
.effectId = data.veffect().value_or_empty(),
}) {
_boostsApplied = data.vfrom_boosts_applied().value_or_empty();
@ -745,6 +746,7 @@ HistoryItem::HistoryItem(
: history->peer)
, _flags(FinalizeMessageFlags(history, fields.flags))
, _date(fields.date)
, _starsPaid(fields.starsPaid)
, _shortcutId(fields.shortcutId)
, _effectId(fields.effectId) {
Expects(!_shortcutId
@ -793,6 +795,10 @@ TimeId HistoryItem::date() const {
return _date;
}
int HistoryItem::starsPaid() const {
return _starsPaid;
}
bool HistoryItem::awaitingVideoProcessing() const {
return (_flags & MessageFlag::EstimatedDate);
}

View file

@ -103,6 +103,7 @@ struct HistoryItemCommonFields {
FullReplyTo replyTo;
TimeId date = 0;
BusinessShortcutId shortcutId = 0;
int starsPaid = 0;
UserId viaBotId = 0;
QString postAuthor;
uint64 groupedId = 0;
@ -548,6 +549,7 @@ public:
// content uses the color of the original sender.
[[nodiscard]] PeerData *contentColorsFrom() const;
[[nodiscard]] uint8 contentColorIndex() const;
[[nodiscard]] int starsPaid() const;
[[nodiscard]] std::unique_ptr<HistoryView::Element> createView(
not_null<HistoryView::ElementDelegate*> delegate,
@ -682,6 +684,7 @@ private:
TimeId _date = 0;
TimeId _ttlDestroyAt = 0;
int _boostsApplied = 0;
int _starsPaid = 0;
BusinessShortcutId _shortcutId = 0;
MessageGroupId _groupId = MessageGroupId();

View file

@ -472,12 +472,15 @@ void DateBadge::paint(
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
}
void ServicePreMessage::init(TextWithEntities string) {
void ServicePreMessage::init(PreparedServiceText string) {
text = Ui::Text::String(
st::serviceTextStyle,
string,
string.text,
kMarkupTextOptions,
st::msgMinWidth);
for (auto i = 0; i != int(string.links.size()); ++i) {
text.setLink(i + 1, string.links[i]);
}
}
int ServicePreMessage::resizeToWidth(int newWidth, bool chatWide) {
@ -547,6 +550,27 @@ void ServicePreMessage::paint(
p.translate(0, -top);
}
ClickHandlerPtr ServicePreMessage::textState(
QPoint point,
const StateRequest &request,
QRect g) const {
const auto top = g.top() - height - st::msgMargin.top();
const auto rect = QRect(0, top, width, height)
- st::msgServiceMargin;
const auto trect = rect - st::msgServicePadding;
if (trect.contains(point)) {
auto textRequest = request.forText();
textRequest.align = style::al_center;
return text.getState(
point - trect.topLeft(),
trect.width(),
textRequest).link;
}
return {};
}
void FakeBotAboutTop::init() {
if (!text.isEmpty()) {
return;
@ -1411,8 +1435,8 @@ void Element::setDisplayDate(bool displayDate) {
}
}
void Element::setServicePreMessage(TextWithEntities text) {
if (!text.empty()) {
void Element::setServicePreMessage(PreparedServiceText text) {
if (!text.text.empty()) {
AddComponents(ServicePreMessage::Bit());
const auto service = Get<ServicePreMessage>();
service->init(std::move(text));

View file

@ -16,6 +16,7 @@ class History;
class HistoryBlock;
class HistoryItem;
struct HistoryMessageReply;
struct PreparedServiceText;
namespace Data {
struct Reaction;
@ -260,7 +261,7 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
// displaying some text in layout of a service message above the message.
struct ServicePreMessage
: public RuntimeComponent<ServicePreMessage, Element> {
void init(TextWithEntities string);
void init(PreparedServiceText string);
int resizeToWidth(int newWidth, bool chatWide);
@ -269,6 +270,10 @@ struct ServicePreMessage
const PaintContext &context,
QRect g,
bool chatWide) const;
[[nodiscard]] ClickHandlerPtr textState(
QPoint point,
const StateRequest &request,
QRect g) const;
Ui::Text::String text;
int width = 0;
@ -403,7 +408,7 @@ public:
// For blocks context this should be called only from recountDisplayDate().
void setDisplayDate(bool displayDate);
void setServicePreMessage(TextWithEntities text);
void setServicePreMessage(PreparedServiceText text);
bool computeIsAttachToPrevious(not_null<Element*> previous);

View file

@ -431,6 +431,35 @@ Message::Message(
_rightAction->second->link = ReportSponsoredClickHandler(data);
}
}
if (const auto stars = data->starsPaid()) {
auto text = PreparedServiceText{
.text = data->out()
? tr::lng_action_paid_message_sent(
tr::now,
lt_count,
stars,
Ui::Text::WithEntities)
: history()->peer->isUser()
? tr::lng_action_paid_message_got(
tr::now,
lt_count,
stars,
lt_name,
Ui::Text::Link(data->from()->shortName(), 1),
Ui::Text::WithEntities)
: tr::lng_action_paid_message_group(
tr::now,
lt_count,
stars,
lt_from,
Ui::Text::Link(data->from()->shortName(), 1),
Ui::Text::WithEntities),
};
if (!data->out()) {
text.links.push_back(data->from()->createOpenLink());
}
setServicePreMessage(std::move(text));
}
}
Message::~Message() {
@ -2448,6 +2477,13 @@ TextState Message::textState(
return result;
}
if (const auto service = Get<ServicePreMessage>()) {
result.link = service->textState(point, request, g);
if (result.link) {
return result;
}
}
const auto bubble = drawBubble();
const auto reactionsInBubble = _reactions && embedReactionsInBubble();
const auto mediaDisplayed = media && media->isDisplayed();