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_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_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" = "{user} suggests you to use this profile 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#other" = "Confirm and Pay {emoji} {count} 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_text#one" = "**{count} Star** 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_anonymous" = "Unknown User";
"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_in_about" = "Use Stars to unlock content and services on Telegram. {link}";
"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) {
AddTableRow(
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
? tr::lng_credits_box_history_entry_anonymous
: tr::lng_credits_box_history_entry_fragment)(

View file

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

View file

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

View file

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

View file

@ -466,84 +466,81 @@ void ReceiptCreditsBox(
content->add(object_ptr<Ui::CenterWrap<>>(
content,
object_ptr<Ui::UserpicButton>(content, peer, stUser)));
} else {
if (e.gift) {
using PlayerPtr = std::unique_ptr<Lottie::SinglePlayer>;
struct State final {
DocumentData *sticker = nullptr;
std::shared_ptr<Data::DocumentMedia> media;
std::unique_ptr<Lottie::SinglePlayer> lottie;
rpl::lifetime downloadLifetime;
};
Ui::AddSkip(
content,
st::creditsHistoryEntryGiftStickerSpace);
const auto icon = Ui::CreateChild<Ui::RpWidget>(content);
icon->resize(Size(st::creditsHistoryEntryGiftStickerSize));
const auto state = icon->lifetime().make_state<State>();
auto &packs = session->giftBoxStickersPacks();
const auto document = packs.lookup(1);
if (document && document->sticker()) {
state->sticker = document;
state->media = document->createMediaView();
state->media->thumbnailWanted(packs.origin());
state->media->automaticLoad(packs.origin(), nullptr);
session->downloaderTaskFinished(
) | rpl::start_with_next([=] {
if (state->media->loaded()) {
state->lottie = ChatHelpers::LottiePlayerFromDocument(
state->media.get(),
ChatHelpers::StickerLottieSize::MessageHistory,
icon->size(),
Lottie::Quality::High);
state->lottie->updates() | rpl::start_with_next([=] {
icon->update();
}, icon->lifetime());
state->downloadLifetime.destroy();
}
}, 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());
} else if (e.gift) {
struct State final {
DocumentData *sticker = nullptr;
std::shared_ptr<Data::DocumentMedia> media;
std::unique_ptr<Lottie::SinglePlayer> lottie;
rpl::lifetime downloadLifetime;
};
Ui::AddSkip(
content,
st::creditsHistoryEntryGiftStickerSpace);
const auto icon = Ui::CreateChild<Ui::RpWidget>(content);
icon->resize(Size(st::creditsHistoryEntryGiftStickerSize));
const auto state = icon->lifetime().make_state<State>();
auto &packs = session->giftBoxStickersPacks();
const auto document = packs.lookup(packs.monthsForStars(e.credits));
if (document && document->sticker()) {
state->sticker = document;
state->media = document->createMediaView();
state->media->thumbnailWanted(packs.origin());
state->media->automaticLoad(packs.origin(), nullptr);
rpl::single() | rpl::then(
session->downloaderTaskFinished()
) | rpl::filter([=] {
return state->media->loaded();
}) | rpl::start_with_next([=] {
state->lottie = ChatHelpers::LottiePlayerFromDocument(
state->media.get(),
ChatHelpers::StickerLottieSize::MessageHistory,
icon->size(),
Lottie::Quality::High);
state->lottie->updates() | rpl::start_with_next([=] {
icon->update();
}, icon->lifetime());
state->downloadLifetime.destroy();
}, 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());
}
Ui::AddSkip(content);
Ui::AddSkip(content);
box->addRow(object_ptr<Ui::CenterWrap<>>(
box,
object_ptr<Ui::FlatLabel>(
@ -565,7 +562,7 @@ void ReceiptCreditsBox(
auto &lifetime = content->lifetime();
const auto text = lifetime.make_state<Ui::Text::String>(
st::semiboldTextStyle,
(e.in ? QChar('+') : kMinus)
(e.in ? u"+"_q : e.gift ? QString() : QString(kMinus))
+ Lang::FormatCountDecimal(std::abs(int64(e.credits))));
const auto roundedText = e.refunded
? tr::lng_channel_earn_history_return(tr::now)
@ -605,6 +602,8 @@ void ReceiptCreditsBox(
? st::creditsStroke
: e.in
? st::boxTextFgGood
: e.gift
? st::windowBoldFg
: st::menuIconAttentionColor);
const auto x = (amount->width() - fullWidth) / 2;
text->draw(p, Ui::Text::PaintContext{
@ -709,10 +708,9 @@ void ReceiptCreditsBox(
tr::lng_credits_box_out_about(
lt_link,
tr::lng_payments_terms_link(
) | rpl::map([](const QString &t) {
using namespace Ui::Text;
return Link(t, u"https://telegram.org/tos"_q);
}),
) | Ui::Text::ToLink(
tr::lng_credits_box_out_about_link(tr::now)
),
Ui::Text::WithEntities),
st::creditsBoxAboutDivider)));
@ -757,6 +755,32 @@ void ReceiptCreditsBox(
}, 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(
not_null<Window::SessionController*> controller,
FullMsgId refundItemId) {

View file

@ -59,6 +59,13 @@ void ReceiptCreditsBox(
not_null<Window::SessionController*> controller,
PeerData *premiumBot,
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(
not_null<Window::SessionController*> controller,
FullMsgId refundItemId);

View file

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