Block gift comments for paid-message recipients

Task: 2026/08/31/block-gift-comments-for-paid-messages
This commit is contained in:
John Preston
2026-09-01 11:50:55 +04:00
parent d9bed97a70
commit 7b96bda80f
5 changed files with 70 additions and 24 deletions

View File

@@ -4255,6 +4255,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
"lng_gift_send_anonymous_about_paid" = "You can hide your name from visitors to {user}'s profile. {recipient} will still see your name.";
"lng_gift_send_anonymous_about_channel" = "You can hide your name and message from all visitors of this channel except its admins.";
"lng_gift_send_message_restricted" = "This user doesn't accept gifts with comments. Please remove the comment and try again.";
"lng_gift_send_unique" = "Make Unique for {price}";
"lng_gift_send_unique_about" = "Enable this to let {user} turn your gift into a unique collectible. {link}";
"lng_gift_send_unique_about_channel" = "Enable this to let the admins of {name} turn your gift into a unique collectible. {link}";

View File

@@ -143,6 +143,7 @@ constexpr auto kPriceTabCollectibles = -2;
constexpr auto kSentToastDuration = 3 * crl::time(1000);
constexpr auto kSwitchUpgradeCoverInterval = 3 * crl::time(1000);
constexpr auto kUpgradeDoneToastDuration = 4 * crl::time(1000);
constexpr auto kMessageRestrictedToastDuration = 4 * crl::time(1000);
constexpr auto kResellPriceCacheLifetime = 60 * crl::time(1000);
using namespace HistoryView;
@@ -2274,6 +2275,16 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
} // namespace
rpl::producer<bool> StarGiftMessageAllowedValue(not_null<PeerData*> peer) {
peer->updateFull();
return peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::StarsPerMessage
) | rpl::map([=] {
return peer->starsPerMessageChecked() == 0;
});
}
not_null<InputField*> AddStarGiftMessageField(
std::shared_ptr<ChatHelpers::Show> show,
not_null<VerticalLayout*> container,
@@ -4560,14 +4571,25 @@ void ShowGiftTransferredToast(
bool ShowGiftErrorToast(
std::shared_ptr<Ui::Show> show,
const MTP::Error &error) {
if (error.type() == u"STARGIFT_ALREADY_BURNED"_q) {
const QString &type) {
if (type == u"STARGIFT_ALREADY_BURNED"_q) {
show->showToast(tr::lng_gift_burned_message(tr::now));
return true;
} else if (type == u"STARGIFT_MESSAGE_INVALID"_q) {
show->showToast(
tr::lng_gift_send_message_restricted(tr::now),
kMessageRestrictedToastDuration);
return true;
}
return false;
}
bool ShowGiftErrorToast(
std::shared_ptr<Ui::Show> show,
const MTP::Error &error) {
return ShowGiftErrorToast(show, error.type());
}
CreditsAmount StarsFromTon(
not_null<Main::Session*> session,
CreditsAmount ton) {
@@ -4997,13 +5019,7 @@ void SendGiftBox(
.randomId = base::RandomValue<uint64>(),
.upgraded = disallowLimited && (costToUpgrade > 0) && !disallowUnique,
};
peer->updateFull();
state->messageAllowed = peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::StarsPerMessage
) | rpl::map([=] {
return peer->starsPerMessageChecked() == 0;
});
state->messageAllowed = StarGiftMessageAllowedValue(peer);
auto cost = state->details.value(
) | rpl::map([](const GiftSendDetails &details) {

View File

@@ -69,6 +69,9 @@ class InputField;
class Show;
class VerticalLayout;
[[nodiscard]] rpl::producer<bool> StarGiftMessageAllowedValue(
not_null<PeerData*> peer);
[[nodiscard]] not_null<InputField*> AddStarGiftMessageField(
std::shared_ptr<ChatHelpers::Show> show,
not_null<VerticalLayout*> container,
@@ -174,6 +177,9 @@ void ShowGiftTransferredToast(
not_null<PeerData*> to,
const Data::UniqueGift &gift);
[[nodiscard]] bool ShowGiftErrorToast(
std::shared_ptr<Ui::Show> show,
const QString &type);
[[nodiscard]] bool ShowGiftErrorToast(
std::shared_ptr<Ui::Show> show,
const MTP::Error &error);

View File

@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/basic_click_handlers.h"
#include "ui/empty_userpic.h"
@@ -1164,11 +1165,13 @@ void ShowBuyResaleGiftBox(
struct State {
rpl::variable<TextWithEntities> message;
rpl::variable<bool> hideName;
rpl::variable<bool> messageAllowed;
std::shared_ptr<ResaleGiftAttemptState> attempt;
bool confirmationOpen = false;
};
const auto state = box->lifetime().make_state<State>();
state->hideName = to->isSelf();
state->messageAllowed = Ui::StarGiftMessageAllowedValue(to);
state->attempt = attempt;
box->setStyle(st::giftBox);
@@ -1187,14 +1190,16 @@ void ShowBuyResaleGiftBox(
auto message = rpl::combine(
state->message.value(),
tr::lng_gift_send_message_preview(),
state->hideName.value()
state->hideName.value(),
state->messageAllowed.value()
) | rpl::map([sender = show->session().user()](
TextWithEntities text,
QString placeholder,
bool hidden) {
TextWithEntities text,
QString placeholder,
bool hidden,
bool allowed) {
return Ui::UniqueGiftCoverMessage{
.text = std::move(text),
.placeholder = std::move(placeholder),
.text = allowed ? std::move(text) : tr::marked(),
.placeholder = allowed ? std::move(placeholder) : QString(),
.sender = sender,
.hidden = hidden,
};
@@ -1206,9 +1211,15 @@ void ShowBuyResaleGiftBox(
initialCost,
std::move(message)));
const auto messageWrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
messageWrap->toggleOn(state->messageAllowed.value());
messageWrap->finishAnimating();
const auto field = Ui::AddStarGiftMessageField(
show,
container,
messageWrap->entity(),
box->getDelegate()->outerContainer(),
tr::lng_gift_send_message(),
QString());
@@ -1220,7 +1231,9 @@ void ShowBuyResaleGiftBox(
};
}, field->lifetime());
box->setFocusCallback([=] {
field->setFocusFast();
if (state->messageAllowed.current()) {
field->setFocusFast();
}
});
Ui::AddDivider(container);
@@ -1239,11 +1252,18 @@ void ShowBuyResaleGiftBox(
? tr::lng_gift_send_anonymous_self()
: to->isBroadcast()
? tr::lng_gift_send_anonymous_about_channel()
: tr::lng_gift_send_anonymous_about(
lt_user,
rpl::single(to->shortName()),
lt_recipient,
rpl::single(to->shortName())));
: rpl::conditional(
state->messageAllowed.value(),
tr::lng_gift_send_anonymous_about(
lt_user,
rpl::single(to->shortName()),
lt_recipient,
rpl::single(to->shortName())),
tr::lng_gift_send_anonymous_about_paid(
lt_user,
rpl::single(to->shortName()),
lt_recipient,
rpl::single(to->shortName()))));
const auto button = box->addButton(rpl::single(QString()), [=] {
if (state->confirmationOpen || state->attempt->inFlight) {
@@ -1260,7 +1280,9 @@ void ShowBuyResaleGiftBox(
.slug = gift->slug,
.recipient = to,
.gift = gift,
.message = state->message.current(),
.message = state->messageAllowed.current()
? state->message.current()
: tr::marked(),
.hideName = state->hideName.current(),
.currency = initiallyTon
? CreditsType::Ton

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_credits.h"
#include "base/unixtime.h"
#include "boxes/send_credits_box.h"
#include "boxes/star_gift_box.h"
#include "data/components/credits.h"
#include "data/data_credits.h"
#include "data/data_photo.h"
@@ -90,7 +91,7 @@ void ProcessCreditsPayment(
std::max(form->starGiftPerUserLimit, 1),
tr::rich),
});
} else {
} else if (!Ui::ShowGiftErrorToast(show, *error)) {
show->showToast(*error);
}
if (onstack) {