Make channel gifts wearing / transferring.

This commit is contained in:
John Preston 2025-01-21 20:48:29 +04:00
parent 9dc947ecb6
commit addd37fb1f
6 changed files with 100 additions and 44 deletions

View file

@ -3301,7 +3301,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_channel_title" = "Send a Gift";
"lng_gift_channel_about" = "Select a gift to show appreciation for {name}.";
"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";
@ -3332,7 +3331,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_convert_sure_caution" = "This action cannot be undone. This will permanently destroy the gift.";
"lng_gift_convert_sure" = "Convert";
"lng_gift_display_done" = "The gift is now shown on your profile page.";
"lng_gift_display_done_channel" = "The gift is now shown in channel's Gifts.";
"lng_gift_display_done_hide" = "The gift is now hidden from your profile page.";
"lng_gift_display_done_hide_channel" = "The gift is now hidden from channel's Gifts.";
"lng_gift_got_stars#one" = "You got **{count} Star** for this gift.";
"lng_gift_got_stars#other" = "You got **{count} Stars** for this gift.";
"lng_gift_sold_out_title" = "Sold Out!";
@ -3390,8 +3391,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_wear_about" = "and get these benefits:";
"lng_gift_wear_badge_title" = "Radiant Badge";
"lng_gift_wear_badge_about" = "The glittering icon of this item will be displayed next to your name.";
"lng_gift_wear_badge_about_channel" = "The glittering icon of this item will be displayed next to channel's name.";
"lng_gift_wear_proof_title" = "Proof of Ownership";
"lng_gift_wear_proof_about" = "Clicking the icon of this item next to your name will show its info and owner.";
"lng_gift_wear_proof_about_channel" = "Clicking the icon of this item next to channel's name will show its info and owner.";
"lng_gift_wear_start" = "Start Wearing";
"lng_gift_wear_subscribe" = "Subscribe to {link} to wear collectibles.";
"lng_gift_wear_start_toast" = "You put on {name}";

View file

@ -218,7 +218,8 @@ constexpr auto kRarityTooltipDuration = 3 * crl::time(1000);
state->content = EmojiStatusIdValue(
peer
) | rpl::map([=](EmojiStatusId emojiStatusId) {
if (!peer->session().premium()) {
if (!peer->session().premium()
|| (!peer->isSelf() && !emojiStatusId)) {
return Badge::Content();
}
return Badge::Content{

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer_rpl.h"
#include "base/unixtime.h"
#include "boxes/filters/edit_filter_chats_list.h"
#include "boxes/peers/edit_peer_color_box.h"
#include "boxes/gift_premium_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/premium_preview_box.h"
@ -26,12 +27,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/ui_integration.h"
#include "data/data_channel.h"
#include "data/data_credits.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_emoji_statuses.h"
#include "data/data_file_origin.h"
#include "data/data_peer_values.h"
#include "data/data_premium_limits.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
@ -55,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_credits.h"
#include "settings/settings_credits_graphics.h"
#include "settings/settings_premium.h"
#include "ui/boxes/boost_box.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
#include "ui/controls/emoji_button.h"
@ -2238,7 +2242,11 @@ void AddWearGiftCover(
title->setTextColorOverride(QColor(255, 255, 255));
const auto subtitle = CreateChild<FlatLabel>(
cover,
tr::lng_status_online(),
(peer->isChannel()
? tr::lng_chat_status_subscribers(
lt_count,
rpl::single(peer->asChannel()->membersCount() * 1.))
: tr::lng_status_online()),
st::uniqueGiftSubtitle);
subtitle->setTextColorOverride(data.backdrop.textColor);
@ -2304,15 +2312,18 @@ void AddWearGiftCover(
void ShowUniqueGiftWearBox(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
const Data::UniqueGift &gift,
Settings::GiftWearBoxStyleOverride st) {
show->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setNoContentMargin(true);
box->setWidth((st::boxWidth + st::boxWideWidth) / 2); // =)
box->setStyle(st.box ? *st.box : st::upgradeGiftBox);
const auto channel = peer->isChannel();
const auto content = box->verticalLayout();
AddWearGiftCover(content, gift, show->session().user());
AddWearGiftCover(content, gift, peer);
AddSkip(content, st::defaultVerticalListSkip * 2);
@ -2356,7 +2367,9 @@ void ShowUniqueGiftWearBox(
st::settingsPremiumRowAboutPadding);
infoRow(
tr::lng_gift_wear_badge_title(),
tr::lng_gift_wear_badge_about(),
(channel
? tr::lng_gift_wear_badge_about_channel()
: tr::lng_gift_wear_badge_about()),
st.radiantIcon ? st.radiantIcon : &st::menuIconUnique);
//infoRow(
// tr::lng_gift_wear_design_title(),
@ -2364,16 +2377,42 @@ void ShowUniqueGiftWearBox(
// &st::menuIconUniqueProfile);
infoRow(
tr::lng_gift_wear_proof_title(),
tr::lng_gift_wear_proof_about(),
(channel
? tr::lng_gift_wear_proof_about_channel()
: tr::lng_gift_wear_proof_about()),
st.proofIcon ? st.proofIcon : &st::menuIconFactcheck);
const auto session = &show->session();
const auto checking = std::make_shared<bool>();
const auto button = box->addButton(rpl::single(QString()), [=] {
if (session->premium()) {
const auto emojiStatuses = &session->data().emojiStatuses();
const auto id = emojiStatuses->fromUniqueGift(gift);
if (!peer->isSelf()) {
if (*checking) {
return;
}
*checking = true;
const auto weak = Ui::MakeWeak(box);
CheckBoostLevel(show, peer, [=](int level) {
const auto limits = Data::LevelLimits(&peer->session());
const auto wanted = limits.channelEmojiStatusLevelMin();
if (level >= wanted) {
if (const auto strong = weak.data()) {
strong->closeBox();
}
emojiStatuses->set(peer, id);
return std::optional<Ui::AskBoostReason>();
}
const auto reason = [&]() -> Ui::AskBoostReason {
return { Ui::AskBoostWearCollectible{
wanted
} };
}();
return std::make_optional(reason);
}, [=] { *checking = false; });
} else if (session->premium()) {
box->closeBox();
session->data().emojiStatuses().set(
session->user(),
session->data().emojiStatuses().fromUniqueGift(gift));
emojiStatuses->set(peer, id);
} else {
const auto link = Ui::Text::Bold(
tr::lng_send_as_premium_required_link(tr::now));
@ -2397,7 +2436,7 @@ void ShowUniqueGiftWearBox(
Data::AmPremiumValue(&show->session())
) | rpl::map([=](const QString &text, bool premium) {
auto result = TextWithEntities();
if (!premium) {
if (!premium && peer->isSelf()) {
result.append(lock);
}
result.append(text);

View file

@ -60,6 +60,7 @@ void AddWearGiftCover(
void ShowUniqueGiftWearBox(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
const Data::UniqueGift &gift,
Settings::GiftWearBoxStyleOverride st);

View file

@ -462,11 +462,15 @@ void Widget::setupNotifyCheckbox(bool enabled) {
notify->checkedChanges() | rpl::start_with_next([=](bool checked) {
const auto api = &controller()->session().api();
const auto show = controller()->uiShow();
using Flag = MTPpayments_ToggleChatStarGiftNotifications::Flag;
api->request(MTPpayments_ToggleChatStarGiftNotifications(
MTP_flags(checked ? Flag::f_enabled : Flag()),
_inner->peer()->input
)).send();
if (checked) {
show->showToast(tr::lng_peer_gifts_notify_enabled(tr::now));
}
}, notify->lifetime());
const auto &checkSt = st::defaultCheckbox;

View file

@ -190,14 +190,19 @@ void ToggleStarGiftSaved(
Fn<void(bool)> done) {
using Flag = MTPpayments_SaveStarGift::Flag;
const auto api = &show->session().api();
const auto channelGift = savedId.chat();
api->request(MTPpayments_SaveStarGift(
MTP_flags(save ? Flag(0) : Flag::f_unsave),
Api::InputSavedStarGiftId(savedId)
)).done([=] {
done(true);
show->showToast((save
? tr::lng_gift_display_done
: tr::lng_gift_display_done_hide)(tr::now));
? (channelGift
? tr::lng_gift_display_done_channel
: tr::lng_gift_display_done)
: (channelGift
? tr::lng_gift_display_done_hide_channel
: tr::lng_gift_display_done_hide))(tr::now));
}).fail([=](const MTP::Error &error) {
done(false);
show->showToast(error.type());
@ -860,9 +865,8 @@ void FillUniqueGiftMenu(
}, st.share ? st.share : &st::menuIconShare);
const auto savedId = EntryToSavedStarGiftId(&show->session(), e);
const auto transfer = e.in
&& savedId
&& (savedId.isUser() ? e.in : savedId.chat()->canManageGifts())
const auto transfer = savedId
&& (savedId.isUser() ? e.in : savedId.chat()->canTransferGifts())
&& (unique->starsForTransfer >= 0);
if (transfer) {
menu->addAction(tr::lng_gift_transfer_button(tr::now), [=] {
@ -871,19 +875,20 @@ void FillUniqueGiftMenu(
}
}, st.transfer ? st.transfer : &st::menuIconReplace);
}
const auto wear = e.in
&& (unique->ownerId == show->session().userPeerId());
const auto owner = show->session().data().peer(unique->ownerId);
const auto wear = owner->isSelf()
? e.in
: (owner->isChannel() && owner->asChannel()->canEditEmoji());
if (wear) {
const auto peer = show->session().user();
const auto name = UniqueGiftName(*unique);
const auto now = peer->emojiStatusId().collectible;
const auto now = owner->emojiStatusId().collectible;
if (now && unique->slug == now->slug) {
menu->addAction(tr::lng_gift_transfer_take_off(tr::now), [=] {
show->session().data().emojiStatuses().set(peer, {});
show->session().data().emojiStatuses().set(owner, {});
}, st.takeoff ? st.takeoff : &st::menuIconNftTakeOff);
} else {
menu->addAction(tr::lng_gift_transfer_wear(tr::now), [=] {
ShowUniqueGiftWearBox(show, *unique, st.giftWearBox
ShowUniqueGiftWearBox(show, owner, *unique, st.giftWearBox
? *st.giftWearBox
: GiftWearBoxStyleOverride());
}, st.wear ? st.wear : &st::menuIconNftWear);
@ -948,9 +953,14 @@ void GenericCreditsEntryBox(
const auto giftToChannel = (giftChannel != nullptr);
const auto giftToChannelCanManage = giftToChannel
&& giftChannel->canManageGifts();
const auto gotStarGift = isStarGift
const auto giftToChannelCanTransfer = giftToChannel
&& giftChannel->canTransferGifts();
const auto starGiftCanManage = isStarGift
&& !creditsHistoryStarGift
&& (e.in || giftToChannelCanManage);
const auto starGiftCanTransfer = isStarGift
&& !creditsHistoryStarGift
&& (e.in || giftToChannelCanTransfer);
const auto starGiftSender = (isStarGift && item)
? item->history()->peer->asUser()
: (isStarGift && e.in)
@ -963,13 +973,11 @@ void GenericCreditsEntryBox(
const auto timeLeft = int64(convertLast) - int64(base::unixtime::now());
const auto timeExceeded = (timeLeft <= 0);
const auto uniqueGift = e.uniqueGift.get();
const auto forConvert = gotStarGift
const auto forConvert = starGiftCanTransfer
&& e.starsConverted
&& !e.converted
&& starGiftSender;
const auto canConvert = forConvert && !timeExceeded;
const auto couldConvert = forConvert && timeExceeded;
const auto nonConvertible = (gotStarGift && !e.starsConverted);
box->setStyle(st.box ? *st.box : st::giveawayGiftCodeBox);
box->setWidth(st::boxWideWidth);
@ -1151,7 +1159,7 @@ void GenericCreditsEntryBox(
? tr::lng_credits_box_history_entry_gift_sent(tr::now)
: convertedStarGift
? tr::lng_credits_box_history_entry_gift_converted(tr::now)
: (isStarGift && !gotStarGift)
: (isStarGift && !starGiftCanManage)
? tr::lng_gift_link_label_gift(tr::now)
: giftToSelf
? tr::lng_action_gift_self_subtitle(tr::now)
@ -1294,7 +1302,7 @@ void GenericCreditsEntryBox(
rpl::single(e.description),
st::creditsBoxAbout)));
}
if (!uniqueGift && gotStarGift) {
if (!uniqueGift && starGiftCanManage) {
Ui::AddSkip(content);
const auto about = box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
@ -1309,28 +1317,20 @@ void GenericCreditsEntryBox(
Ui::Text::RichLangValue)
: (e.starsToUpgrade
&& giftToSelf
&& !(couldConvert || nonConvertible))
&& !e.giftTransferred)
? tr::lng_action_gift_self_about_unique(
Ui::Text::WithEntities)
: (e.starsToUpgrade
&& giftToChannelCanManage
&& !(couldConvert || nonConvertible))
&& !e.giftTransferred)
? tr::lng_action_gift_channel_about_unique(
Ui::Text::WithEntities)
: ((couldConvert || nonConvertible)
? (e.savedToProfile
? (giftToChannel
? tr::lng_action_gift_can_remove_channel
: tr::lng_action_gift_can_remove_text)
: (giftToChannel
? tr::lng_action_gift_got_gift_channel
: tr::lng_action_gift_got_gift_text))(
Ui::Text::WithEntities)
: rpl::combine(
: ((canConvert || e.converted)
? rpl::combine(
(canConvert
? (giftToSelf
? tr::lng_action_gift_self_about
: giftToChannelCanManage
: giftToChannelCanTransfer
? tr::lng_action_gift_channel_about
: tr::lng_action_gift_got_stars_text)
: tr::lng_gift_got_stars)(
@ -1343,7 +1343,15 @@ void GenericCreditsEntryBox(
QString link) {
return text.append(' ').append(
Ui::Text::Link(link));
}))),
})
: (e.savedToProfile
? (giftToChannel
? tr::lng_action_gift_can_remove_channel
: tr::lng_action_gift_can_remove_text)
: (giftToChannel
? tr::lng_action_gift_got_gift_channel
: tr::lng_action_gift_got_gift_text))(
Ui::Text::WithEntities))),
st::creditsBoxAbout)))->entity();
about->setClickHandlerFilter([=](const auto &...) {
Core::App().iv().openWithIvPreferred(
@ -1399,7 +1407,7 @@ void GenericCreditsEntryBox(
};
const auto state = box->lifetime().make_state<State>();
const auto canToggle = (canConvert || couldConvert || nonConvertible)
const auto canToggle = starGiftCanManage
&& !e.giftTransferred
&& !e.giftRefunded;
const auto toggleVisibility = [=, weak = Ui::MakeWeak(box)](bool save) {
@ -1541,7 +1549,7 @@ void GenericCreditsEntryBox(
tr::lng_credits_box_out_about_link(tr::now)),
Ui::Text::WithEntities),
st::creditsBoxAboutDivider)));
} else if (gotStarGift || giftToChannelCanManage) {
} else if (starGiftCanManage) {
const auto hiddenPhrase = giftToChannelCanManage
? tr::lng_gift_hidden_hint_channel
: tr::lng_gift_hidden_hint;