diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 64077c69a..f017398da 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2024,7 +2024,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_proximity_distance_km#other" = "{count} km"; "lng_action_webview_data_done" = "Data from the \"{text}\" button was transferred to the bot."; "lng_action_gift_received" = "{user} sent you a gift for {cost}"; +"lng_action_gift_unique_received" = "{user} sent you a unique collectible item"; "lng_action_gift_sent" = "You sent a gift for {cost}"; +"lng_action_gift_unique_sent" = "You sent a unique collectible item"; "lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}"; "lng_action_gift_for_stars#one" = "{count} Star"; "lng_action_gift_for_stars#other" = "{count} Stars"; @@ -3233,6 +3235,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_sent_about#other" = "You spent **{count}** Stars from your balance."; "lng_gift_limited_of_one" = "unique"; "lng_gift_limited_of_count" = "1 of {amount}"; +"lng_gift_price_unique" = "Unique"; "lng_gift_anonymous_hint" = "Only you can see the sender's name."; "lng_gift_hidden_hint" = "This gift is hidden. Only you can see it."; "lng_gift_visible_hint" = "This gift is visible to visitors of your page."; @@ -3243,6 +3246,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_visibility_hidden" = "Not visible on your page"; "lng_gift_visibility_show" = "show"; "lng_gift_visibility_hide" = "hide"; +"lng_gift_unique_owner" = "Owner"; +"lng_gift_unique_owner_change" = "change"; +"lng_gift_unique_status" = "Status"; +"lng_gift_unique_status_non" = "Non-Unique"; +"lng_gift_unique_status_upgrade" = "upgrade"; +"lng_gift_unique_number" = "Collectible #{index}"; +"lng_gift_unique_model" = "Model"; +"lng_gift_unique_backdrop" = "Backdrop"; +"lng_gift_unique_symbol" = "Symbol"; +"lng_gift_unique_availability#one" = "{count} of {amount} issued"; +"lng_gift_unique_availability#other" = "{count} of {amount} issued"; +"lng_gift_unique_info" = "Gifted to {recipient} on {date}."; +"lng_gift_unique_info_sender" = "Gifted by {from} to {recipient} on {date}."; +"lng_gift_unique_info_comment" = "Gifted by {from} to {recipient} on {date} with the comment \"{text}\"."; "lng_gift_availability_left#one" = "{count} of {amount} left"; "lng_gift_availability_left#other" = "{count} of {amount} left"; "lng_gift_availability_none" = "None of {amount} left"; @@ -3265,6 +3282,37 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_send_small" = "send a gift"; "lng_gift_sell_small#one" = "sell for {count} Star"; "lng_gift_sell_small#other" = "sell for {count} Stars"; +"lng_gift_upgrade_title" = "Upgrade Gift"; +"lng_gift_upgrade_about" = "Turn your gift into a unique collectible that you can transfer or auction."; +"lng_gift_upgrade_unique_title" = "Unique"; +"lng_gift_upgrade_unique_about" = "Get a unique number, model, backdrop and symbol for your gift."; +"lng_gift_upgrade_transferable_title" = "Transferable"; +"lng_gift_upgrade_transferable_about" = "Send your upgraded gift to any of your friends on Telegram."; +"lng_gift_upgrade_tradable_title" = "Tradable"; +"lng_gift_upgrade_tradable_about" = "Sell or auction your gift on third-party NFT marketplaces."; +"lng_gift_upgrade_button" = "Upgrade for {price}"; +"lng_gift_upgrade_add_sender" = "Add sender's name"; +"lng_gift_upgrade_add_comment" = "Add sender's name and comment"; +"lng_gift_upgraded_title" = "Gift Upgraded"; +"lng_gift_upgraded_about" = "Your gift {name} now has unique attributes and can be transferred to others"; +"lng_gift_transfer_title" = "Transfer {name}"; +"lng_gift_transfer_via_blockchain" = "Send via Blockchain"; +"lng_gift_transfer_unlocks_days#one" = "unlocks in {count} day"; +"lng_gift_transfer_unlocks_days#other" = "unlocks in {count} days"; +"lng_gift_transfer_unlocks_hours#one" = "unlocks in {count} hour"; +"lng_gift_transfer_unlocks_hours#other" = "unlocks in {count} hours"; +"lng_gift_transfer_unlocks_title" = "Unlocking in progress"; +"lng_gift_transfer_unlocks_about" = "{when}, you'll be able to send this collectible to any TON blockchain address outside Telegram for sale or auction."; +"lng_gift_transfer_unlocks_when_days#one" = "In {count} day"; +"lng_gift_transfer_unlocks_when_days#other" = "In {count} days"; +"lng_gift_transfer_unlocks_when_hours#one" = "In {count} hour"; +"lng_gift_transfer_unlocks_when_hours#other" = "In {count} hours"; +"lng_gift_transfer_unlocks_update_title" = "Update required"; +"lng_gift_transfer_unlocks_update_about" = "Please update your Telegram application to the latest version."; +"lng_gift_transfer_sure" = "Do you want to transfer ownership of {name} to {recipient}?"; +"lng_gift_transfer_sure_for" = "Do you want to transfer ownership of {name} to {recipient} for {price}?"; +"lng_gift_transfer_button" = "Transfer"; +"lng_gift_transfer_button_for" = "Transfer for {price}"; "lng_accounts_limit_title" = "Limit Reached"; "lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account."; diff --git a/Telegram/SourceFiles/api/api_credits.cpp b/Telegram/SourceFiles/api/api_credits.cpp index 0e8ef86b2..11ded38e0 100644 --- a/Telegram/SourceFiles/api/api_credits.cpp +++ b/Telegram/SourceFiles/api/api_credits.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "api/api_credits.h" +#include "api/api_premium.h" #include "api/api_statistics_data_deserialize.h" #include "api/api_updates.h" #include "apiwrap.h" @@ -90,25 +91,10 @@ constexpr auto kTransactionsLimit = 100; : 0; const auto incoming = (amount >= StarsAmount()); const auto saveActorId = (reaction || !extended.empty()) && incoming; - const auto giftStickerId = [&] { - if (!stargift) { - return DocumentId(); - } - return stargift->match([&](const MTPDstarGift &data) { - return owner->processDocument(data.vsticker())->id; - }, [&](const MTPDstarGiftUnique &data) { - for (const auto &attribute : data.vattributes().v) { - const auto result = attribute.match([&]( - const MTPDstarGiftAttributeModel &data) { - return DocumentId(data.vdocument_id().v); - }, [](const auto &) { return DocumentId(); }); - if (result) { - return result; - } - } - return DocumentId(); - }); - }(); + const auto parsedGift = stargift + ? FromTL(&peer->session(), *stargift) + : std::optional(); + const auto giftStickerId = parsedGift ? parsedGift->stickerId : 0; return Data::CreditsHistoryEntry{ .id = qs(tl.data().vid()), .title = qs(tl.data().vtitle().value_or_empty()), @@ -123,6 +109,7 @@ constexpr auto kTransactionsLimit = 100; tl.data().vgiveaway_post_id().value_or_empty()), .bareGiftStickerId = giftStickerId, .bareActorId = saveActorId ? barePeerId : uint64(0), + .uniqueGift = parsedGift ? parsedGift->unique : nullptr, .starrefAmount = starrefAmount, .starrefCommission = starrefCommission, .starrefRecipientId = starrefBarePeerId, diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 67c7d5f26..b4a7cb2d3 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -371,17 +371,20 @@ void PreviewWrap::prepare(rpl::producer details) { const auto cost = v::match(descriptor, [&](GiftTypePremium data) { return FillAmountAndCurrency(data.cost, data.currency, true); }, [&](GiftTypeStars data) { - return tr::lng_gift_stars_title( - tr::now, - lt_count, - data.info.stars); + const auto stars = data.info.stars; + return stars + ? tr::lng_gift_stars_title(tr::now, lt_count, stars) + : QString(); }); - const auto text = tr::lng_action_gift_received( - tr::now, - lt_user, - _history->session().user()->shortName(), - lt_cost, - cost); + const auto name = _history->session().user()->shortName(); + const auto text = cost.isEmpty() + ? tr::lng_action_gift_unique_received(tr::now, lt_user, name) + : tr::lng_action_gift_received( + tr::now, + lt_user, + name, + lt_cost, + cost); const auto item = _history->makeMessage({ .id = _history->nextNonHistoryEntryId(), .flags = (MessageFlag::FakeAboutView diff --git a/Telegram/SourceFiles/data/data_credits.h b/Telegram/SourceFiles/data/data_credits.h index f0cbccad2..2e3ed7ab8 100644 --- a/Telegram/SourceFiles/data/data_credits.h +++ b/Telegram/SourceFiles/data/data_credits.h @@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { +struct UniqueGift; + struct CreditTopupOption final { uint64 credits = 0; QString product; @@ -63,6 +65,7 @@ struct CreditsHistoryEntry final { uint64 bareGiveawayMsgId = 0; uint64 bareGiftStickerId = 0; uint64 bareActorId = 0; + std::shared_ptr uniqueGift; StarsAmount starrefAmount; int starrefCommission = 0; uint64 starrefRecipientId = 0; diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 57e684ee9..0d9d91a6a 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -39,6 +39,7 @@ namespace Data { class CloudImage; class WallPaper; class Session; +struct UniqueGift; enum class CallFinishReason : char { Missed, @@ -135,7 +136,8 @@ enum class GiftType : uchar { struct GiftCode { QString slug; - DocumentData *document = nullptr; + DocumentId stickerId = 0; + std::shared_ptr unique; TextWithEntities message; ChannelData *channel = nullptr; MsgId giveawayMsgId = 0; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 816f69e64..648306f92 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/history_item.h" +#include "api/api_premium.h" #include "api/api_sensitive_content.h" #include "lang/lang_keys.h" #include "mainwidget.h" @@ -5410,6 +5411,20 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { }, [](const MTPDstarGiftUnique &) { return uint64(); }); + if (!stars) { + if (!isSelf) { + result.links.push_back(peer->createOpenLink()); + } + result.text = isSelf + ? tr::lng_action_gift_unique_sent( + tr::now, + Ui::Text::WithEntities) + : tr::lng_action_gift_unique_received( + tr::now, + lt_user, + Ui::Text::Link(peer->shortName(), 1), // Link 1. + Ui::Text::WithEntities); + } const auto cost = TextWithEntities{ tr::lng_action_gift_for_stars(tr::now, lt_count, stars), }; @@ -5421,7 +5436,9 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) { cost, Ui::Text::WithEntities); } else { - result.links.push_back(peer->createOpenLink()); + if (!isSelf) { + result.links.push_back(peer->createOpenLink()); + } result.text = isSelf ? tr::lng_action_gift_sent(tr::now, lt_cost, @@ -5611,32 +5628,29 @@ void HistoryItem::applyAction(const MTPMessageAction &action) { .unclaimed = data.is_unclaimed(), }); }, [&](const MTPDmessageActionStarGift &data) { - data.vgift().match([&](const MTPDstarGift &gift) { - const auto document = history()->owner().processDocument( - gift.vsticker()); - using Fields = Data::GiftCode; - _media = std::make_unique(this, _from, Fields{ - .document = document->sticker() ? document.get() : nullptr, - .message = (data.vmessage() - ? TextWithEntities{ - .text = qs(data.vmessage()->data().vtext()), - .entities = Api::EntitiesFromMTP( - &history()->session(), - data.vmessage()->data().ventities().v), - } - : TextWithEntities()), - .starsConverted = int(data.vconvert_stars().value_or_empty()), - .limitedCount = gift.vavailability_total().value_or_empty(), - .limitedLeft = gift.vavailability_remains().value_or_empty(), - .count = int(gift.vstars().v), - .type = Data::GiftType::StarGift, - .anonymous = data.is_name_hidden(), - .converted = data.is_converted(), - .saved = data.is_saved(), - }); - }, [&](const MTPDstarGiftUnique &gift) { - - }); + using Fields = Data::GiftCode; + auto fields = Fields{ + .message = (data.vmessage() + ? TextWithEntities{ + .text = qs(data.vmessage()->data().vtext()), + .entities = Api::EntitiesFromMTP( + &history()->session(), + data.vmessage()->data().ventities().v), + } + : TextWithEntities()), + .starsConverted = int(data.vconvert_stars().value_or_empty()), + .type = Data::GiftType::StarGift, + .anonymous = data.is_name_hidden(), + .converted = data.is_converted(), + .saved = data.is_saved(), + }; + if (auto gift = Api::FromTL(&history()->session(), data.vgift())) { + fields.stickerId = gift->stickerId; + fields.limitedCount = gift->limitedCount; + fields.limitedLeft = gift->limitedLeft; + fields.count = gift->stars; + fields.unique = gift->unique; + } }, [](const auto &) { }); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp index ba1c2ceeb..ecd51900c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/gift_premium_box.h" // ResolveGiftCode #include "chat_helpers/stickers_gift_box_pack.h" #include "core/click_handler_types.h" // ClickHandlerContext +#include "data/stickers/data_custom_emoji.h" #include "data/data_channel.h" #include "data/data_credits.h" #include "data/data_document.h" @@ -221,7 +222,9 @@ void PremiumGift::draw( } QString PremiumGift::cornerTagText() { - if (const auto count = _data.limitedCount) { + if (_data.unique) { + return tr::lng_gift_limited_of_one(tr::now); + } else if (const auto count = _data.limitedCount) { return (count == 1) ? tr::lng_gift_limited_of_one(tr::now) : tr::lng_gift_limited_of_count( @@ -291,14 +294,21 @@ int PremiumGift::credits() const { void PremiumGift::ensureStickerCreated() const { if (_sticker) { return; - } else if (const auto document = _data.document) { - if (const auto sticker = document->sticker()) { - const auto skipPremiumEffect = false; - _sticker.emplace(_parent, document, skipPremiumEffect, _parent); - _sticker->setPlayingOnce(true); - _sticker->initSize(st::msgServiceGiftBoxStickerSize); - return; + } else if (const auto stickerId = _data.stickerId) { + if (!_lifetime) { + const auto owner = &_parent->history()->owner(); + _lifetime = owner->customEmojiManager().resolve( + stickerId + ) | rpl::start_with_next([=](not_null document) { + const auto sticker = document->sticker(); + Assert(sticker != nullptr); + _sticker.emplace(_parent, document, false, _parent); + _sticker->setPlayingOnce(true); + _sticker->initSize(st::msgServiceGiftBoxStickerSize); + _parent->repaint(); + }); } + return; } const auto &session = _parent->history()->session(); auto &packs = session.giftBoxStickersPacks(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h index db976370e..f070c54a6 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h +++ b/Telegram/SourceFiles/history/view/media/history_view_premium_gift.h @@ -60,6 +60,7 @@ private: const not_null _gift; const Data::GiftCode &_data; mutable std::optional _sticker; + mutable rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 51a898b07..f984ab874 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1615,7 +1615,7 @@ void StarGiftViewBox( .credits = StarsAmount(data.count), .bareMsgId = uint64(item->id.bare), .barePeerId = item->history()->peer->id.value, - .bareGiftStickerId = data.document ? data.document->id : 0, + .bareGiftStickerId = data.stickerId, .peerType = Data::CreditsHistoryEntry::PeerType::Peer, .limitedCount = data.limitedCount, .limitedLeft = data.limitedLeft,