Filter out allowed gift types.

This commit is contained in:
John Preston 2025-03-17 13:27:24 +04:00
parent b656e14453
commit c0fed4d2c3
5 changed files with 141 additions and 40 deletions

View file

@ -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_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" = "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_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_title" = "Calls";
"lng_edit_privacy_calls_header" = "Who can call me"; "lng_edit_privacy_calls_header" = "Who can call me";

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_credits.h" #include "api/api_credits.h"
#include "api/api_global_privacy.h"
#include "api/api_premium.h" #include "api/api_premium.h"
#include "base/event_filter.h" #include "base/event_filter.h"
#include "base/random.h" #include "base/random.h"
@ -686,6 +687,24 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
}; };
} }
[[nodiscard]] bool AllowedToSend(
const GiftTypeStars &gift,
not_null<PeerData*> 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<std::vector<GiftTypeStars>> GiftsStars( [[nodiscard]] rpl::producer<std::vector<GiftTypeStars>> GiftsStars(
not_null<Main::Session*> session, not_null<Main::Session*> session,
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
@ -694,6 +713,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
}; };
static auto Map = base::flat_map<not_null<Main::Session*>, Session>(); static auto Map = base::flat_map<not_null<Main::Session*>, Session>();
const auto filtered = [=](std::vector<GiftTypeStars> list) {
list.erase(ranges::remove_if(list, [&](const GiftTypeStars &gift) {
return !AllowedToSend(gift, peer);
}), end(list));
return list;
};
return [=](auto consumer) { return [=](auto consumer) {
auto lifetime = rpl::lifetime(); auto lifetime = rpl::lifetime();
@ -703,7 +728,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
session->lifetime().add([=] { Map.remove(session); }); session->lifetime().add([=] { Map.remove(session); });
} }
if (!i->second.last.empty()) { if (!i->second.last.empty()) {
consumer.put_next_copy(i->second.last); consumer.put_next(filtered(i->second.last));
} }
using namespace Api; using namespace Api;
@ -725,7 +750,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
auto &map = Map[session]; auto &map = Map[session];
if (map.last != list) { if (map.last != list) {
map.last = list; map.last = list;
consumer.put_next_copy(list); consumer.put_next(filtered(std::move(list)));
} }
}, lifetime); }, lifetime);
@ -1897,6 +1922,7 @@ void AddBlock(
? IsSoldOut(gift.info) ? IsSoldOut(gift.info)
: (price && gift.info.stars != price); : (price && gift.info.stars != price);
}), end(gifts)); }), end(gifts));
return GiftsDescriptor{ return GiftsDescriptor{
gifts | ranges::to<std::vector<GiftDescriptor>>(), gifts | ranges::to<std::vector<GiftDescriptor>>(),
}; };
@ -1924,6 +1950,24 @@ void GiftBox(
AddSkip(content, st::defaultVerticalListSkip * 5); 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( content->add(
object_ptr<CenterWrap<>>( object_ptr<CenterWrap<>>(
content, content,
@ -1945,11 +1989,12 @@ void GiftBox(
window->showSettings(Settings::CreditsId()); window->showSettings(Settings::CreditsId());
return false; return false;
}; };
if (peer->isUser() && !peer->isSelf()) { if (peer->isUser() && !peer->isSelf() && !premiumDisallowed) {
const auto premiumClickHandlerFilter = [=](const auto &...) { const auto premiumClickHandlerFilter = [=](const auto &...) {
Settings::ShowPremium(window, u"gift_send"_q); Settings::ShowPremium(window, u"gift_send"_q);
return false; return false;
}; };
AddBlock(content, window, { AddBlock(content, window, {
.subtitle = tr::lng_gift_premium_subtitle(), .subtitle = tr::lng_gift_premium_subtitle(),
.about = tr::lng_gift_premium_about( .about = tr::lng_gift_premium_about(
@ -1962,28 +2007,32 @@ void GiftBox(
.content = MakePremiumGifts(window, peer), .content = MakePremiumGifts(window, peer),
}); });
} }
AddBlock(content, window, {
.subtitle = (peer->isSelf() // Only add star gifts if at least one type is allowed
? tr::lng_gift_self_title() if (!allStarsDisallowed) {
: peer->isBroadcast() AddBlock(content, window, {
? tr::lng_gift_channel_title() .subtitle = (peer->isSelf()
: tr::lng_gift_stars_subtitle()), ? tr::lng_gift_self_title()
.about = (peer->isSelf() : peer->isBroadcast()
? tr::lng_gift_self_about(Text::WithEntities) ? tr::lng_gift_channel_title()
: peer->isBroadcast() : tr::lng_gift_stars_subtitle()),
? tr::lng_gift_channel_about( .about = (peer->isSelf()
lt_name, ? tr::lng_gift_self_about(Text::WithEntities)
rpl::single(Text::Bold(peer->name())), : peer->isBroadcast()
Text::WithEntities) ? tr::lng_gift_channel_about(
: tr::lng_gift_stars_about( lt_name,
lt_name, rpl::single(Text::Bold(peer->name())),
rpl::single(Text::Bold(peer->shortName())), Text::WithEntities)
lt_link, : tr::lng_gift_stars_about(
tr::lng_gift_stars_link() | Text::ToLink(), lt_name,
Text::WithEntities)), rpl::single(Text::Bold(peer->shortName())),
.aboutFilter = starsClickHandlerFilter, lt_link,
.content = MakeStarsGifts(window, peer), tr::lng_gift_stars_link() | Text::ToLink(),
}); Text::WithEntities)),
.aboutFilter = starsClickHandlerFilter,
.content = MakeStarsGifts(window, peer),
});
}
} }
struct SelfOption { struct SelfOption {
@ -2195,6 +2244,19 @@ void ShowStarGiftBox(
const auto show = [=] { const auto show = [=] {
Map[session] = Session(); Map[session] = Session();
if (const auto strong = weak.get()) { 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)); strong->show(Box(GiftBox, strong, peer));
} }
}; };

View file

@ -96,27 +96,28 @@ struct PeerUpdate {
PersonalChannel = (1ULL << 34), PersonalChannel = (1ULL << 34),
StarRefProgram = (1ULL << 35), StarRefProgram = (1ULL << 35),
PaysPerMessage = (1ULL << 36), PaysPerMessage = (1ULL << 36),
GiftSettings = (1ULL << 37),
// For chats and channels // For chats and channels
InviteLinks = (1ULL << 37), InviteLinks = (1ULL << 38),
Members = (1ULL << 38), Members = (1ULL << 39),
Admins = (1ULL << 39), Admins = (1ULL << 40),
BannedUsers = (1ULL << 40), BannedUsers = (1ULL << 41),
Rights = (1ULL << 41), Rights = (1ULL << 42),
PendingRequests = (1ULL << 42), PendingRequests = (1ULL << 43),
Reactions = (1ULL << 43), Reactions = (1ULL << 44),
// For channels // For channels
ChannelAmIn = (1ULL << 44), ChannelAmIn = (1ULL << 45),
StickersSet = (1ULL << 45), StickersSet = (1ULL << 46),
EmojiSet = (1ULL << 46), EmojiSet = (1ULL << 47),
ChannelLinkedChat = (1ULL << 47), ChannelLinkedChat = (1ULL << 48),
ChannelLocation = (1ULL << 48), ChannelLocation = (1ULL << 49),
Slowmode = (1ULL << 49), Slowmode = (1ULL << 50),
GroupCall = (1ULL << 50), GroupCall = (1ULL << 51),
// For iteration // For iteration
LastUsedBit = (1ULL << 50), LastUsedBit = (1ULL << 51),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "api/api_credits.h" #include "api/api_credits.h"
#include "api/api_global_privacy.h"
#include "api/api_sensitive_content.h" #include "api/api_sensitive_content.h"
#include "api/api_statistics.h" #include "api/api_statistics.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
@ -659,6 +660,13 @@ bool UserData::hasCalls() const {
&& (callsStatus() != CallsStatus::Unknown); && (callsStatus() != CallsStatus::Unknown);
} }
void UserData::setDisallowedGiftTypes(Api::DisallowedGiftTypes types) {
if (_disallowedGiftTypes != types) {
_disallowedGiftTypes = types;
session().changes().peerUpdated(this, UpdateFlag::GiftSettings);
}
}
namespace Data { namespace Data {
void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) { void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
@ -815,6 +823,22 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
user->setBotVerifyDetails( user->setBotVerifyDetails(
ParseBotVerifyDetails(update.vbot_verification())); 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->owner().stories().apply(user, update.vstories());
user->fullUpdated(); user->fullUpdated();

View file

@ -15,12 +15,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_lastseen_status.h" #include "data/data_lastseen_status.h"
#include "data/data_user_names.h" #include "data/data_user_names.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "base/flags.h"
namespace Data { namespace Data {
struct BotCommand; struct BotCommand;
struct BusinessDetails; struct BusinessDetails;
} // namespace Data } // namespace Data
namespace Api {
enum class DisallowedGiftType;
using DisallowedGiftTypes = base::flags<DisallowedGiftType>;
} // namespace Api
struct StarRefProgram { struct StarRefProgram {
StarsAmount revenuePerUser; StarsAmount revenuePerUser;
TimeId endDate = 0; TimeId endDate = 0;
@ -262,6 +268,11 @@ public:
std::unique_ptr<BotInfo> botInfo; std::unique_ptr<BotInfo> botInfo;
[[nodiscard]] Api::DisallowedGiftTypes disallowedGiftTypes() const {
return _disallowedGiftTypes;
}
void setDisallowedGiftTypes(Api::DisallowedGiftTypes types);
private: private:
auto unavailableReasons() const auto unavailableReasons() const
-> const std::vector<Data::UnavailableReason> & override; -> const std::vector<Data::UnavailableReason> & override;
@ -293,6 +304,8 @@ private:
static constexpr auto kInaccessibleAccessHashOld static constexpr auto kInaccessibleAccessHashOld
= 0xFFFFFFFFFFFFFFFFULL; = 0xFFFFFFFFFFFFFFFFULL;
Api::DisallowedGiftTypes _disallowedGiftTypes;
}; };
namespace Data { namespace Data {