Implement Stars gift view from service messages.

This commit is contained in:
John Preston 2024-07-25 11:40:22 +02:00
parent 992c876930
commit 4b09050061
8 changed files with 154 additions and 97 deletions

View file

@ -1845,6 +1845,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot."; "lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
"lng_action_gift_received" = "{user} sent you a gift for {cost}"; "lng_action_gift_received" = "{user} sent you a gift for {cost}";
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}"; "lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
"lng_action_suggested_photo_me" = "You suggested {user} to use this profile photo."; "lng_action_suggested_photo_me" = "You suggested {user} to use this profile photo.";
"lng_action_suggested_photo" = "{user} suggests you to use this profile photo."; "lng_action_suggested_photo" = "{user} suggests you to use this profile photo.";
"lng_action_suggested_photo_button" = "View Photo"; "lng_action_suggested_photo_button" = "View Photo";
@ -2358,6 +2359,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_box_out_confirm#one" = "Confirm and Pay {emoji} {count} Star"; "lng_credits_box_out_confirm#one" = "Confirm and Pay {emoji} {count} Star";
"lng_credits_box_out_confirm#other" = "Confirm and Pay {emoji} {count} Stars"; "lng_credits_box_out_confirm#other" = "Confirm and Pay {emoji} {count} Stars";
"lng_credits_box_out_about" = "Review the {link} for Stars."; "lng_credits_box_out_about" = "Review the {link} for Stars.";
"lng_credits_box_out_about_link" = "https://telegram.org/tos/stars";
"lng_credits_media_done_title" = "Media Unlocked"; "lng_credits_media_done_title" = "Media Unlocked";
"lng_credits_media_done_text#one" = "**{count} Star** transferred to {chat}."; "lng_credits_media_done_text#one" = "**{count} Star** transferred to {chat}.";
"lng_credits_media_done_text#other" = "**{count} Stars** transferred to {chat}."; "lng_credits_media_done_text#other" = "**{count} Stars** transferred to {chat}.";
@ -2372,6 +2374,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_box_history_entry_fragment" = "Fragment"; "lng_credits_box_history_entry_fragment" = "Fragment";
"lng_credits_box_history_entry_anonymous" = "Unknown User"; "lng_credits_box_history_entry_anonymous" = "Unknown User";
"lng_credits_box_history_entry_gift_name" = "Received Gift"; "lng_credits_box_history_entry_gift_name" = "Received Gift";
"lng_credits_box_history_entry_gift_sent" = "Sent Gift";
"lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}"; "lng_credits_box_history_entry_gift_out_about" = "With Stars, **{user}** will be able to unlock content and services on Telegram.\n{link}";
"lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}"; "lng_credits_box_history_entry_gift_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
"lng_credits_box_history_entry_gift_about_link" = "See Examples {emoji}"; "lng_credits_box_history_entry_gift_about_link" = "See Examples {emoji}";

View file

@ -1694,7 +1694,9 @@ void AddCreditsHistoryEntryTable(
} else if (entry.peerType == Type::Fragment) { } else if (entry.peerType == Type::Fragment) {
AddTableRow( AddTableRow(
table, table,
tr::lng_credits_box_history_entry_via(), (entry.gift
? tr::lng_credits_box_history_entry_peer_in
: tr::lng_credits_box_history_entry_via)(),
(entry.gift (entry.gift
? tr::lng_credits_box_history_entry_anonymous ? tr::lng_credits_box_history_entry_anonymous
: tr::lng_credits_box_history_entry_fragment)( : tr::lng_credits_box_history_entry_fragment)(

View file

@ -276,8 +276,8 @@ void Panel::initControls() {
_layerBg->showBox(std::move(box)); _layerBg->showBox(std::move(box));
} }
} else if (const auto source = env->uniqueDesktopCaptureSource()) { } else if (const auto source = env->uniqueDesktopCaptureSource()) {
if (_call->isSharingScreen()) { if (!chooseSourceActiveDeviceId().isEmpty()) {
_call->toggleScreenSharing(std::nullopt); chooseSourceStop();
} else { } else {
chooseSourceAccepted(*source, false); chooseSourceAccepted(*source, false);
} }

View file

@ -4694,21 +4694,32 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
auto prepareGiftPremium = [&]( auto prepareGiftPremium = [&](
const MTPDmessageActionGiftPremium &action) { const MTPDmessageActionGiftPremium &action) {
auto result = PreparedServiceText(); auto result = PreparedServiceText();
const auto isSelf = (_from->id == _from->session().userPeerId()); const auto session = &_history->session();
const auto isSelf = _from->isSelf();
const auto peer = isSelf ? _history->peer : _from; const auto peer = isSelf ? _history->peer : _from;
_history->session().giftBoxStickersPacks().load(); session->giftBoxStickersPacks().load();
const auto amount = action.vamount().v; const auto amount = action.vamount().v;
const auto currency = qs(action.vcurrency()); const auto currency = qs(action.vcurrency());
result.links.push_back(peer->createOpenLink()); const auto cost = AmountAndStarCurrency(session, amount, currency);
result.text = (isSelf const auto anonymous = _from->isServiceUser();
? tr::lng_action_gift_received_me if (anonymous) {
: tr::lng_action_gift_received)( result.text = tr::lng_action_gift_received_anonymous(
tr::now, tr::now,
lt_user,
Ui::Text::Link(peer->name(), 1), // Link 1.
lt_cost, lt_cost,
AmountAndStarCurrency(&peer->session(), amount, currency), cost,
Ui::Text::WithEntities); Ui::Text::WithEntities);
} else {
result.links.push_back(peer->createOpenLink());
result.text = (isSelf
? tr::lng_action_gift_received_me
: tr::lng_action_gift_received)(
tr::now,
lt_user,
Ui::Text::Link(peer->name(), 1), // Link 1.
lt_cost,
cost,
Ui::Text::WithEntities);
}
return result; return result;
}; };

View file

@ -12,13 +12,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h" // ClickHandlerContext #include "core/click_handler_types.h" // ClickHandlerContext
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_user.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "settings/settings_credits.h" // Settings::CreditsId #include "settings/settings_credits.h" // Settings::CreditsId
#include "settings/settings_credits_graphics.h" // GiftedCreditsBox
#include "settings/settings_premium.h" // Settings::ShowGiftPremium #include "settings/settings_premium.h" // Settings::ShowGiftPremium
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
@ -99,22 +102,29 @@ rpl::producer<QString> PremiumGift::button() {
ClickHandlerPtr PremiumGift::createViewLink() { ClickHandlerPtr PremiumGift::createViewLink() {
const auto from = _gift->from(); const auto from = _gift->from();
const auto to = _parent->history()->peer; const auto peer = _parent->history()->peer;
const auto date = _parent->data()->date();
const auto data = _gift->data(); const auto data = _gift->data();
return std::make_shared<LambdaClickHandler>([=](ClickContext context) { return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>(); const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) { if (const auto controller = my.sessionWindow.get()) {
const auto selfId = controller->session().userPeerId(); const auto selfId = controller->session().userPeerId();
const auto self = (from->id == selfId); const auto sent = (from->id == selfId);
if (data.type == Data::GiftType::Stars) { if (data.type == Data::GiftType::Stars) {
controller->showSettings(Settings::CreditsId()); const auto to = sent ? peer : peer->session().user();
controller->show(Box(
Settings::GiftedCreditsBox,
controller,
from,
to,
data.count,
date));
} else if (data.slug.isEmpty()) { } else if (data.slug.isEmpty()) {
const auto peer = self ? to : from;
const auto months = data.count; const auto months = data.count;
Settings::ShowGiftPremium(controller, peer, months, self); Settings::ShowGiftPremium(controller, peer, months, sent);
} else { } else {
const auto fromId = from->id; const auto fromId = from->id;
const auto toId = self ? to->id : selfId; const auto toId = sent ? peer->id : selfId;
ResolveGiftCode(controller, data.slug, fromId, toId); ResolveGiftCode(controller, data.slug, fromId, toId);
} }
} }

View file

@ -466,84 +466,81 @@ void ReceiptCreditsBox(
content->add(object_ptr<Ui::CenterWrap<>>( content->add(object_ptr<Ui::CenterWrap<>>(
content, content,
object_ptr<Ui::UserpicButton>(content, peer, stUser))); object_ptr<Ui::UserpicButton>(content, peer, stUser)));
} else { } else if (e.gift) {
if (e.gift) { struct State final {
using PlayerPtr = std::unique_ptr<Lottie::SinglePlayer>; DocumentData *sticker = nullptr;
struct State final { std::shared_ptr<Data::DocumentMedia> media;
DocumentData *sticker = nullptr; std::unique_ptr<Lottie::SinglePlayer> lottie;
std::shared_ptr<Data::DocumentMedia> media; rpl::lifetime downloadLifetime;
std::unique_ptr<Lottie::SinglePlayer> lottie; };
rpl::lifetime downloadLifetime; Ui::AddSkip(
}; content,
Ui::AddSkip( st::creditsHistoryEntryGiftStickerSpace);
content, const auto icon = Ui::CreateChild<Ui::RpWidget>(content);
st::creditsHistoryEntryGiftStickerSpace); icon->resize(Size(st::creditsHistoryEntryGiftStickerSize));
const auto icon = Ui::CreateChild<Ui::RpWidget>(content); const auto state = icon->lifetime().make_state<State>();
icon->resize(Size(st::creditsHistoryEntryGiftStickerSize)); auto &packs = session->giftBoxStickersPacks();
const auto state = icon->lifetime().make_state<State>(); const auto document = packs.lookup(packs.monthsForStars(e.credits));
auto &packs = session->giftBoxStickersPacks(); if (document && document->sticker()) {
const auto document = packs.lookup(1); state->sticker = document;
if (document && document->sticker()) { state->media = document->createMediaView();
state->sticker = document; state->media->thumbnailWanted(packs.origin());
state->media = document->createMediaView(); state->media->automaticLoad(packs.origin(), nullptr);
state->media->thumbnailWanted(packs.origin()); rpl::single() | rpl::then(
state->media->automaticLoad(packs.origin(), nullptr); session->downloaderTaskFinished()
session->downloaderTaskFinished( ) | rpl::filter([=] {
) | rpl::start_with_next([=] { return state->media->loaded();
if (state->media->loaded()) { }) | rpl::start_with_next([=] {
state->lottie = ChatHelpers::LottiePlayerFromDocument( state->lottie = ChatHelpers::LottiePlayerFromDocument(
state->media.get(), state->media.get(),
ChatHelpers::StickerLottieSize::MessageHistory, ChatHelpers::StickerLottieSize::MessageHistory,
icon->size(), icon->size(),
Lottie::Quality::High); Lottie::Quality::High);
state->lottie->updates() | rpl::start_with_next([=] { state->lottie->updates() | rpl::start_with_next([=] {
icon->update(); icon->update();
}, icon->lifetime()); }, icon->lifetime());
state->downloadLifetime.destroy(); state->downloadLifetime.destroy();
} }, state->downloadLifetime);
}, state->downloadLifetime);
}
icon->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(icon);
const auto &lottie = state->lottie;
const auto frame = (lottie && lottie->ready())
? lottie->frameInfo({ .box = icon->size() })
: Lottie::Animation::FrameInfo();
if (!frame.image.isNull()) {
p.drawImage(0, 0, frame.image);
if (lottie->frameIndex() < lottie->framesCount() - 1) {
lottie->markFrameShown();
}
}
}, icon->lifetime());
content->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
icon->move(
(size.width() - icon->width()) / 2,
st::creditsHistoryEntryGiftStickerSkip);
}, icon->lifetime());
} else {
const auto widget = content->add(
object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::RpWidget>(content)))->entity();
using Draw = Fn<void(Painter &, int, int, int, int)>;
const auto draw = widget->lifetime().make_state<Draw>(
Ui::GenerateCreditsPaintUserpicCallback(e));
widget->resize(Size(stUser.photoSize));
widget->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(widget);
(*draw)(p, 0, 0, stUser.photoSize, stUser.photoSize);
}, widget->lifetime());
} }
icon->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(icon);
const auto &lottie = state->lottie;
const auto frame = (lottie && lottie->ready())
? lottie->frameInfo({ .box = icon->size() })
: Lottie::Animation::FrameInfo();
if (!frame.image.isNull()) {
p.drawImage(0, 0, frame.image);
if (lottie->frameIndex() < lottie->framesCount() - 1) {
lottie->markFrameShown();
}
}
}, icon->lifetime());
content->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
icon->move(
(size.width() - icon->width()) / 2,
st::creditsHistoryEntryGiftStickerSkip);
}, icon->lifetime());
} else {
const auto widget = content->add(
object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::RpWidget>(content)))->entity();
using Draw = Fn<void(Painter &, int, int, int, int)>;
const auto draw = widget->lifetime().make_state<Draw>(
Ui::GenerateCreditsPaintUserpicCallback(e));
widget->resize(Size(stUser.photoSize));
widget->paintRequest(
) | rpl::start_with_next([=] {
auto p = Painter(widget);
(*draw)(p, 0, 0, stUser.photoSize, stUser.photoSize);
}, widget->lifetime());
} }
Ui::AddSkip(content); Ui::AddSkip(content);
Ui::AddSkip(content); Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>( box->addRow(object_ptr<Ui::CenterWrap<>>(
box, box,
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
@ -565,7 +562,7 @@ void ReceiptCreditsBox(
auto &lifetime = content->lifetime(); auto &lifetime = content->lifetime();
const auto text = lifetime.make_state<Ui::Text::String>( const auto text = lifetime.make_state<Ui::Text::String>(
st::semiboldTextStyle, st::semiboldTextStyle,
(e.in ? QChar('+') : kMinus) (e.in ? u"+"_q : e.gift ? QString() : QString(kMinus))
+ Lang::FormatCountDecimal(std::abs(int64(e.credits)))); + Lang::FormatCountDecimal(std::abs(int64(e.credits))));
const auto roundedText = e.refunded const auto roundedText = e.refunded
? tr::lng_channel_earn_history_return(tr::now) ? tr::lng_channel_earn_history_return(tr::now)
@ -605,6 +602,8 @@ void ReceiptCreditsBox(
? st::creditsStroke ? st::creditsStroke
: e.in : e.in
? st::boxTextFgGood ? st::boxTextFgGood
: e.gift
? st::windowBoldFg
: st::menuIconAttentionColor); : st::menuIconAttentionColor);
const auto x = (amount->width() - fullWidth) / 2; const auto x = (amount->width() - fullWidth) / 2;
text->draw(p, Ui::Text::PaintContext{ text->draw(p, Ui::Text::PaintContext{
@ -709,10 +708,9 @@ void ReceiptCreditsBox(
tr::lng_credits_box_out_about( tr::lng_credits_box_out_about(
lt_link, lt_link,
tr::lng_payments_terms_link( tr::lng_payments_terms_link(
) | rpl::map([](const QString &t) { ) | Ui::Text::ToLink(
using namespace Ui::Text; tr::lng_credits_box_out_about_link(tr::now)
return Link(t, u"https://telegram.org/tos"_q); ),
}),
Ui::Text::WithEntities), Ui::Text::WithEntities),
st::creditsBoxAboutDivider))); st::creditsBoxAboutDivider)));
@ -757,6 +755,32 @@ void ReceiptCreditsBox(
}, button->lifetime()); }, button->lifetime());
} }
void GiftedCreditsBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
not_null<PeerData*> from,
not_null<PeerData*> to,
int count,
TimeId date) {
const auto received = to->isSelf();
const auto anonymous = from->isServiceUser();
const auto peer = received ? from : to;
using PeerType = Data::CreditsHistoryEntry::PeerType;
Settings::ReceiptCreditsBox(box, controller, nullptr, {
.id = QString(),
.title = (received
? tr::lng_credits_box_history_entry_gift_name
: tr::lng_credits_box_history_entry_gift_sent)(tr::now),
.date = base::unixtime::parse(date),
.credits = uint64(count),
.bareMsgId = uint64(),
.barePeerId = (anonymous ? uint64() : peer->id.value),
.peerType = (anonymous ? PeerType::Fragment : PeerType::Peer),
.in = received,
.gift = true,
});
}
void ShowRefundInfoBox( void ShowRefundInfoBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
FullMsgId refundItemId) { FullMsgId refundItemId) {

View file

@ -59,6 +59,13 @@ void ReceiptCreditsBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PeerData *premiumBot, PeerData *premiumBot,
const Data::CreditsHistoryEntry &e); const Data::CreditsHistoryEntry &e);
void GiftedCreditsBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
not_null<PeerData*> from,
not_null<PeerData*> to,
int count,
TimeId date);
void ShowRefundInfoBox( void ShowRefundInfoBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
FullMsgId refundItemId); FullMsgId refundItemId);

View file

@ -217,7 +217,7 @@ PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
case Data::CreditsHistoryEntry::PeerType::PlayMarket: case Data::CreditsHistoryEntry::PeerType::PlayMarket:
return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 }; return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 };
case Data::CreditsHistoryEntry::PeerType::Fragment: case Data::CreditsHistoryEntry::PeerType::Fragment:
return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 }; return { st::windowSubTextFg, st::imageBg };
case Data::CreditsHistoryEntry::PeerType::PremiumBot: case Data::CreditsHistoryEntry::PeerType::PremiumBot:
return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 }; return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 };
case Data::CreditsHistoryEntry::PeerType::Ads: case Data::CreditsHistoryEntry::PeerType::Ads:
@ -458,7 +458,7 @@ TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) {
return (entry.gift return (entry.gift
? tr::lng_credits_box_history_entry_gift_name ? tr::lng_credits_box_history_entry_gift_name
: (entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment) : (entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment)
? tr::lng_bot_username_description1_link ? tr::lng_credits_box_history_entry_fragment
: (entry.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot) : (entry.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot)
? tr::lng_credits_box_history_entry_premium_bot ? tr::lng_credits_box_history_entry_premium_bot
: (entry.peerType == Data::CreditsHistoryEntry::PeerType::Ads) : (entry.peerType == Data::CreditsHistoryEntry::PeerType::Ads)