diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0720de5c8..411b64a13 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 83ed31a6d..55daafd4d 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -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( diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index f10f68988..f571964b3 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -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 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()), diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 7393dee65..a62fa7b48 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -460,6 +460,9 @@ not_null History::createItem( }); if (newMessage && result->out() && result->isRegular()) { session().topPeers().increment(peer, result->date()); + if (result->starsPaid()) { + session().credits().load(true); + } } return result; } diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 8bd1c4f72..0798c1699 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -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); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 7ce520fc1..8649400bc 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -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 createView( not_null delegate, @@ -682,6 +684,7 @@ private: TimeId _date = 0; TimeId _ttlDestroyAt = 0; int _boostsApplied = 0; + int _starsPaid = 0; BusinessShortcutId _shortcutId = 0; MessageGroupId _groupId = MessageGroupId(); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index ef12b365f..4dddabdfb 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -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(); service->init(std::move(text)); diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index abb1b3ada..de00c0f48 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -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 { // displaying some text in layout of a service message above the message. struct ServicePreMessage : public RuntimeComponent { - 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 previous); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index ce8bdb90a..1dc5c6cae 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -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()) { + 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();