diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7d6a1ff17d..062d20c49b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1321,6 +1321,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_gifts_types_about" = "Choose the types of gifts that you accept."; "lng_edit_privacy_gifts_show_icon" = "Show Gift Icon in Chats"; "lng_edit_privacy_gifts_show_icon_about" = "Display the {emoji}Gift icon in the message input field for both participants in all chats."; +"lng_edit_privacy_gifts_restricted" = "This user doesn't accept gifts."; "lng_edit_privacy_calls_title" = "Calls"; "lng_edit_privacy_calls_header" = "Who can call me"; diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 59abb78468..6a17b7f344 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "api/api_credits.h" +#include "api/api_global_privacy.h" #include "api/api_premium.h" #include "base/event_filter.h" #include "base/random.h" @@ -686,6 +687,24 @@ void PreviewWrap::paintEvent(QPaintEvent *e) { }; } +[[nodiscard]] bool AllowedToSend( + const GiftTypeStars &gift, + not_null peer) { + using Type = Api::DisallowedGiftType; + const auto user = peer->asUser(); + if (!user || user->isSelf()) { + return true; + } + const auto disallowedTypes = user ? user->disallowedGiftTypes() : Type(); + const auto allowLimited = !(disallowedTypes & Type::Limited); + const auto allowUnlimited = !(disallowedTypes & Type::Unlimited); + const auto allowUnique = !(disallowedTypes & Type::Unique); + if (!gift.info.limitedCount) { + return allowUnlimited; + } + return allowLimited || (gift.info.starsToUpgrade && allowUnique); +} + [[nodiscard]] rpl::producer> GiftsStars( not_null session, not_null peer) { @@ -694,6 +713,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) { }; static auto Map = base::flat_map, Session>(); + const auto filtered = [=](std::vector list) { + list.erase(ranges::remove_if(list, [&](const GiftTypeStars &gift) { + return !AllowedToSend(gift, peer); + }), end(list)); + return list; + }; return [=](auto consumer) { auto lifetime = rpl::lifetime(); @@ -703,7 +728,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) { session->lifetime().add([=] { Map.remove(session); }); } if (!i->second.last.empty()) { - consumer.put_next_copy(i->second.last); + consumer.put_next(filtered(i->second.last)); } using namespace Api; @@ -725,7 +750,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) { auto &map = Map[session]; if (map.last != list) { map.last = list; - consumer.put_next_copy(list); + consumer.put_next(filtered(std::move(list))); } }, lifetime); @@ -1897,6 +1922,7 @@ void AddBlock( ? IsSoldOut(gift.info) : (price && gift.info.stars != price); }), end(gifts)); + return GiftsDescriptor{ gifts | ranges::to>(), }; @@ -1924,6 +1950,24 @@ void GiftBox( AddSkip(content, st::defaultVerticalListSkip * 5); + // Check disallowed gift types + const auto user = peer->asUser(); + using Type = Api::DisallowedGiftType; + const auto disallowedTypes = user + ? user->disallowedGiftTypes() + : Type::Premium; + const auto premiumDisallowed = peer->isSelf() + || (disallowedTypes & Type::Premium); + const auto limitedDisallowed = !peer->isSelf() + && (disallowedTypes & Type::Limited); + const auto unlimitedDisallowed = !peer->isSelf() + && (disallowedTypes & Type::Unlimited); + const auto uniqueDisallowed = !peer->isSelf() + && (disallowedTypes & Type::Unique); + const auto allStarsDisallowed = limitedDisallowed + && unlimitedDisallowed + && uniqueDisallowed; + content->add( object_ptr>( content, @@ -1945,11 +1989,12 @@ void GiftBox( window->showSettings(Settings::CreditsId()); return false; }; - if (peer->isUser() && !peer->isSelf()) { + if (peer->isUser() && !peer->isSelf() && !premiumDisallowed) { const auto premiumClickHandlerFilter = [=](const auto &...) { Settings::ShowPremium(window, u"gift_send"_q); return false; }; + AddBlock(content, window, { .subtitle = tr::lng_gift_premium_subtitle(), .about = tr::lng_gift_premium_about( @@ -1962,28 +2007,32 @@ void GiftBox( .content = MakePremiumGifts(window, peer), }); } - AddBlock(content, window, { - .subtitle = (peer->isSelf() - ? tr::lng_gift_self_title() - : peer->isBroadcast() - ? tr::lng_gift_channel_title() - : tr::lng_gift_stars_subtitle()), - .about = (peer->isSelf() - ? tr::lng_gift_self_about(Text::WithEntities) - : peer->isBroadcast() - ? tr::lng_gift_channel_about( - lt_name, - rpl::single(Text::Bold(peer->name())), - Text::WithEntities) - : tr::lng_gift_stars_about( - lt_name, - rpl::single(Text::Bold(peer->shortName())), - lt_link, - tr::lng_gift_stars_link() | Text::ToLink(), - Text::WithEntities)), - .aboutFilter = starsClickHandlerFilter, - .content = MakeStarsGifts(window, peer), - }); + + // Only add star gifts if at least one type is allowed + if (!allStarsDisallowed) { + AddBlock(content, window, { + .subtitle = (peer->isSelf() + ? tr::lng_gift_self_title() + : peer->isBroadcast() + ? tr::lng_gift_channel_title() + : tr::lng_gift_stars_subtitle()), + .about = (peer->isSelf() + ? tr::lng_gift_self_about(Text::WithEntities) + : peer->isBroadcast() + ? tr::lng_gift_channel_about( + lt_name, + rpl::single(Text::Bold(peer->name())), + Text::WithEntities) + : tr::lng_gift_stars_about( + lt_name, + rpl::single(Text::Bold(peer->shortName())), + lt_link, + tr::lng_gift_stars_link() | Text::ToLink(), + Text::WithEntities)), + .aboutFilter = starsClickHandlerFilter, + .content = MakeStarsGifts(window, peer), + }); + } } struct SelfOption { @@ -2195,6 +2244,19 @@ void ShowStarGiftBox( const auto show = [=] { Map[session] = Session(); if (const auto strong = weak.get()) { + if (const auto user = peer->asUser()) { + using Type = Api::DisallowedGiftType; + const auto disallowedTypes = user->disallowedGiftTypes(); + const auto premium = (disallowedTypes & Type::Premium) + || peer->isSelf(); + const auto limited = (disallowedTypes & Type::Limited); + const auto unlimited = (disallowedTypes & Type::Unlimited); + const auto unique = (disallowedTypes & Type::Unique); + if (premium && limited && unlimited && unique) { + strong->showToast(tr::lng_edit_privacy_gifts_restricted(tr::now)); + return; + } + } strong->show(Box(GiftBox, strong, peer)); } }; diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index f44ae72bbf..39a654d3c0 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -96,27 +96,28 @@ struct PeerUpdate { PersonalChannel = (1ULL << 34), StarRefProgram = (1ULL << 35), PaysPerMessage = (1ULL << 36), + GiftSettings = (1ULL << 37), // For chats and channels - InviteLinks = (1ULL << 37), - Members = (1ULL << 38), - Admins = (1ULL << 39), - BannedUsers = (1ULL << 40), - Rights = (1ULL << 41), - PendingRequests = (1ULL << 42), - Reactions = (1ULL << 43), + InviteLinks = (1ULL << 38), + Members = (1ULL << 39), + Admins = (1ULL << 40), + BannedUsers = (1ULL << 41), + Rights = (1ULL << 42), + PendingRequests = (1ULL << 43), + Reactions = (1ULL << 44), // For channels - ChannelAmIn = (1ULL << 44), - StickersSet = (1ULL << 45), - EmojiSet = (1ULL << 46), - ChannelLinkedChat = (1ULL << 47), - ChannelLocation = (1ULL << 48), - Slowmode = (1ULL << 49), - GroupCall = (1ULL << 50), + ChannelAmIn = (1ULL << 45), + StickersSet = (1ULL << 46), + EmojiSet = (1ULL << 47), + ChannelLinkedChat = (1ULL << 48), + ChannelLocation = (1ULL << 49), + Slowmode = (1ULL << 50), + GroupCall = (1ULL << 51), // For iteration - LastUsedBit = (1ULL << 50), + LastUsedBit = (1ULL << 51), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 8c334421e3..46ad8bd514 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "api/api_credits.h" +#include "api/api_global_privacy.h" #include "api/api_sensitive_content.h" #include "api/api_statistics.h" #include "storage/localstorage.h" @@ -659,6 +660,13 @@ bool UserData::hasCalls() const { && (callsStatus() != CallsStatus::Unknown); } +void UserData::setDisallowedGiftTypes(Api::DisallowedGiftTypes types) { + if (_disallowedGiftTypes != types) { + _disallowedGiftTypes = types; + session().changes().peerUpdated(this, UpdateFlag::GiftSettings); + } +} + namespace Data { void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { @@ -815,6 +823,22 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { user->setBotVerifyDetails( ParseBotVerifyDetails(update.vbot_verification())); + if (const auto gifts = update.vdisallowed_stargifts()) { + const auto &data = gifts->data(); + user->setDisallowedGiftTypes(Api::DisallowedGiftType() + | ((data.is_disallow_unlimited_stargifts() + ? Api::DisallowedGiftType::Unlimited + : Api::DisallowedGiftType())) + | ((data.is_disallow_limited_stargifts() + ? Api::DisallowedGiftType::Limited + : Api::DisallowedGiftType())) + | ((data.is_disallow_unique_stargifts() + ? Api::DisallowedGiftType::Unique + : Api::DisallowedGiftType()))); + } else { + user->setDisallowedGiftTypes(Api::DisallowedGiftTypes()); + } + user->owner().stories().apply(user, update.vstories()); user->fullUpdated(); diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 515a333135..f816dd6988 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -15,12 +15,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_lastseen_status.h" #include "data/data_user_names.h" #include "dialogs/dialogs_key.h" +#include "base/flags.h" namespace Data { struct BotCommand; struct BusinessDetails; } // namespace Data +namespace Api { +enum class DisallowedGiftType; +using DisallowedGiftTypes = base::flags; +} // namespace Api + struct StarRefProgram { StarsAmount revenuePerUser; TimeId endDate = 0; @@ -262,6 +268,11 @@ public: std::unique_ptr botInfo; + [[nodiscard]] Api::DisallowedGiftTypes disallowedGiftTypes() const { + return _disallowedGiftTypes; + } + void setDisallowedGiftTypes(Api::DisallowedGiftTypes types); + private: auto unavailableReasons() const -> const std::vector & override; @@ -293,6 +304,8 @@ private: static constexpr auto kInaccessibleAccessHashOld = 0xFFFFFFFFFFFFFFFFULL; + Api::DisallowedGiftTypes _disallowedGiftTypes; + }; namespace Data {