Update API scheme on layer 160, show correct warnings.

This commit is contained in:
John Preston 2025-06-26 13:55:20 +04:00
parent 1ecd7aa7cf
commit b929e2a7b2
13 changed files with 132 additions and 21 deletions

View file

@ -4472,6 +4472,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_suggest_decline_title" = "Decline";
"lng_suggest_decline_text" = "Do you want to decline publishing this post from {from}?";
"lng_suggest_decline_reason" = "Add a reason (optional)";
"lng_suggest_warn_title_stars" = "Stars will be lost";
"lng_suggest_warn_title_ton" = "TON will be lost";
"lng_suggest_warn_text_stars" = "You won't receive **Stars** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published.";
"lng_suggest_warn_text_ton" = "You won't receive **TON** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published.";
"lng_suggest_warn_delete_anyway" = "Delete Anyway";
"lng_reply_in_another_title" = "Reply in...";
"lng_reply_in_another_chat" = "Reply in Another Chat";

View file

@ -499,23 +499,32 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) {
}
}
bool DeleteMessagesBox::hasPaidSuggestedPosts() const {
PaidPostType DeleteMessagesBox::paidPostType() const {
auto result = PaidPostType::None;
const auto now = base::unixtime::now();
for (const auto &id : _ids) {
if (const auto item = _session->data().message(id)) {
if (item->isPaidSuggestedPost()) {
const auto type = item->paidType();
if (type != PaidPostType::None) {
const auto date = item->date();
if (now < date || now - date <= kPaidShowLive) {
return true;
if (type == PaidPostType::Ton) {
return type;
} else if (type == PaidPostType::Stars) {
result = type;
}
}
}
}
}
return false;
return result;
}
void DeleteMessagesBox::deleteAndClear() {
if (hasPaidSuggestedPosts() && !_confirmedDeletePaidSuggestedPosts) {
const auto warnPaidType = _confirmedDeletePaidSuggestedPosts
? PaidPostType::None
: paidPostType();
if (warnPaidType != PaidPostType::None) {
const auto weak = Ui::MakeWeak(this);
const auto callback = [=](Fn<void()> close) {
close();
@ -524,13 +533,19 @@ void DeleteMessagesBox::deleteAndClear() {
strong->deleteAndClear();
}
};
AssertIsDebug();
const auto ton = (warnPaidType == PaidPostType::Ton);
uiShow()->show(Ui::MakeConfirmBox({
.text = u"You won't receive Stars for this post if you delete it now. The post must remain visible for at least 24 hours after it was published."_q,
.text = (ton
? tr::lng_suggest_warn_text_ton
: tr::lng_suggest_warn_text_stars)(
tr::now,
Ui::Text::RichLangValue),
.confirmed = callback,
.confirmText = u"Delete Anyway"_q,
.confirmText = tr::lng_suggest_warn_delete_anyway(tr::now),
.confirmStyle = &st::attentionBoxButton,
.title = u"Stars will be lost"_q,
.title = (ton
? tr::lng_suggest_warn_title_ton
: tr::lng_suggest_warn_title_stars)(tr::now),
}));
return;
}

View file

@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/box_content.h"
enum class PaidPostType : uchar;
namespace Main {
class Session;
} // namespace Main
@ -58,7 +60,7 @@ private:
[[nodiscard]] bool hasScheduledMessages() const;
[[nodiscard]] std::optional<RevokeConfig> revokeText(
not_null<PeerData*> peer) const;
[[nodiscard]] bool hasPaidSuggestedPosts() const;
[[nodiscard]] PaidPostType paidPostType() const;
const not_null<Main::Session*> _session;

View file

@ -354,7 +354,8 @@ enum class MessageFlag : uint64 {
HideDisplayDate = (1ULL << 51),
PaidSuggestedPost = (1ULL << 52),
StarsPaidSuggested = (1ULL << 52),
TonPaidSuggested = (1ULL << 53),
};
inline constexpr bool is_flag_type(MessageFlag) { return true; }
using MessageFlags = base::flags<MessageFlag>;

View file

@ -1775,6 +1775,14 @@ ServiceAction ParseServiceAction(
.rejected = data.is_rejected(),
.balanceTooLow = data.is_balance_too_low(),
};
}, [&](const MTPDmessageActionSuggestedPostSuccess &data) {
result.content = ActionSuggestedPostSuccess{
.price = CreditsAmountFromTL(data.vprice()),
};
}, [&](const MTPDmessageActionSuggestedPostRefund &data) {
result.content = ActionSuggestedPostRefund{
.payerInitiated = data.is_payer_initiated(),
};
}, [&](const MTPDmessageActionConferenceCall &data) {
auto content = ActionPhoneCall();
using State = ActionPhoneCall::State;

View file

@ -707,6 +707,14 @@ struct ActionSuggestedPostApproval {
bool balanceTooLow = false;
};
struct ActionSuggestedPostSuccess {
CreditsAmount price;
};
struct ActionSuggestedPostRefund {
bool payerInitiated = false;
};
struct ServiceAction {
std::variant<
v::null_t,
@ -757,7 +765,9 @@ struct ServiceAction {
ActionPaidMessagesPrice,
ActionTodoCompletions,
ActionTodoAppendTasks,
ActionSuggestedPostApproval> content;
ActionSuggestedPostApproval,
ActionSuggestedPostSuccess,
ActionSuggestedPostRefund> content;
};
ServiceAction ParseServiceAction(

View file

@ -1466,6 +1466,15 @@ auto HtmlWriter::Wrap::pushMessage(
: (", with comment: &quot;"
+ SerializeString(data.rejectComment)
+ "&quot;"));
}, [&](const ActionSuggestedPostSuccess &data) {
return "The paid post was shown for 24 hours and "
+ QString::number(data.price.value()).toUtf8()
+ (data.price.ton() ? " TON" : " stars")
+ " were transferred to the channel.";
}, [&](const ActionSuggestedPostRefund &data) {
return QByteArray() + (data.payerInitiated
? "The user refunded the payment, post was deleted."
: "The admin deleted the post early, the payment was refunded.");
}, [](v::null_t) { return QByteArray(); });
if (!serviceText.isEmpty()) {

View file

@ -725,6 +725,16 @@ QByteArray SerializeMessage(
push("price_currency", data.price.ton() ? "TON" : "Stars");
push("scheduled_date", data.scheduleDate);
}
}, [&](const ActionSuggestedPostSuccess &data) {
pushActor();
pushAction("suggested_post_success");
push("price_amount_whole", NumberToString(data.price.whole()));
push("price_amount_nano", NumberToString(data.price.nano()));
push("price_currency", data.price.ton() ? "TON" : "Stars");
}, [&](const ActionSuggestedPostRefund &data) {
pushActor();
pushAction("suggested_post_refund");
push("user_initiated", data.payerInitiated);
}, [](v::null_t) {});
if (v::is_null(message.action.content)) {

View file

@ -830,6 +830,8 @@ HistoryServiceDependentData *HistoryItem::GetServiceDependentData() {
return append;
} else if (const auto decision = Get<HistoryServiceSuggestDecision>()) {
return decision;
} else if (const auto finish = Get<HistoryServiceSuggestFinish>()) {
return finish;
}
return nullptr;
}
@ -1645,8 +1647,12 @@ bool HistoryItem::isEditingMedia() const {
return Has<HistoryMessageSavedMediaData>();
}
bool HistoryItem::isPaidSuggestedPost() const {
return _flags & MessageFlag::PaidSuggestedPost;
PaidPostType HistoryItem::paidType() const {
return (_flags & MessageFlag::StarsPaidSuggested)
? PaidPostType::Stars
: (_flags & MessageFlag::TonPaidSuggested)
? PaidPostType::Ton
: PaidPostType::None;
}
void HistoryItem::clearSavedMedia() {
@ -2474,7 +2480,7 @@ bool HistoryItem::allowsSendNow() const {
&& !isSending()
&& !hasFailed()
&& !isEditingMedia()
&& !isPaidSuggestedPost();
&& (paidType() == PaidPostType::None);
}
bool HistoryItem::allowsReschedule() const {
@ -2502,7 +2508,7 @@ bool HistoryItem::allowsEdit(TimeId now) const {
&& (!_media || _media->allowsEdit())
&& !isLegacyMessage()
&& !isEditingMedia()
&& !isPaidSuggestedPost();
&& (paidType() == PaidPostType::None);
}
bool HistoryItem::allowsEditMedia() const {
@ -4673,6 +4679,18 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) {
decision->rejected = data.is_rejected();
decision->rejectComment = qs(data.vreject_comment().value_or_empty());
decision->date = data.vschedule_date().value_or_empty();
} else if (type == mtpc_messageActionSuggestedPostSuccess) {
const auto &data = action.c_messageActionSuggestedPostSuccess();
UpdateComponents(HistoryServiceSuggestFinish::Bit());
const auto finish = Get<HistoryServiceSuggestFinish>();
finish->successPrice = CreditsAmountFromTL(data.vprice());
} else if (type == mtpc_messageActionSuggestedPostRefund) {
const auto &data = action.c_messageActionSuggestedPostRefund();
UpdateComponents(HistoryServiceSuggestFinish::Bit());
const auto finish = Get<HistoryServiceSuggestFinish>();
finish->refundType = data.is_payer_initiated()
? SuggestRefundType::User
: SuggestRefundType::Admin;
}
if (const auto replyTo = message.vreply_to()) {
replyTo->match([&](const MTPDmessageReplyHeader &data) {
@ -6058,7 +6076,15 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
};
auto prepareSuggestedPostApproval = [&](const MTPDmessageActionSuggestedPostApproval &data) {
return PreparedServiceText{ { u"hello"_q } };
return PreparedServiceText{ { tr::lng_suggest_action_agreement(tr::now) } };
};
auto prepareSuggestedPostSuccess = [&](const MTPDmessageActionSuggestedPostSuccess &data) {
return PreparedServiceText{ { u"hello"_q } }; AssertIsDebug();
};
auto prepareSuggestedPostRefund = [&](const MTPDmessageActionSuggestedPostRefund &data) {
return PreparedServiceText{ { u"hello"_q } }; AssertIsDebug();
};
auto prepareConferenceCall = [&](const MTPDmessageActionConferenceCall &) -> PreparedServiceText {
@ -6119,6 +6145,8 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
prepareTodoCompletions,
prepareTodoAppendTasks,
prepareSuggestedPostApproval,
prepareSuggestedPostSuccess,
prepareSuggestedPostRefund,
PrepareEmptyText<MTPDmessageActionRequestedPeerSentMe>,
PrepareErrorText<MTPDmessageActionEmpty>));

View file

@ -95,6 +95,12 @@ enum class HistoryReactionSource : char {
Existing,
};
enum class PaidPostType : uchar {
None,
Stars,
Ton,
};
class HistoryItem final : public RuntimeComposer<HistoryItem> {
public:
[[nodiscard]] static std::unique_ptr<Data::Media> CreateMedia(
@ -314,7 +320,6 @@ public:
[[nodiscard]] bool hasRealFromId() const;
[[nodiscard]] bool isPostHidingAuthor() const;
[[nodiscard]] bool isPostShowingAuthor() const;
[[nodiscard]] bool isPaidSuggestedPost() const;
[[nodiscard]] bool isRegular() const;
[[nodiscard]] bool isUploading() const;
void sendFailed();
@ -325,6 +330,7 @@ public:
[[nodiscard]] bool hasUnpaidContent() const;
[[nodiscard]] bool inHighlightProcess() const;
void highlightProcessDone();
[[nodiscard]] PaidPostType paidType() const;
void setCommentsInboxReadTill(MsgId readTillId);
void setCommentsMaxId(MsgId maxId);

View file

@ -708,6 +708,19 @@ struct HistoryServiceSuggestDecision
bool balanceTooLow = false;
};
enum class SuggestRefundType {
None,
User,
Admin,
};
struct HistoryServiceSuggestFinish
: RuntimeComponent<HistoryServiceSuggestFinish, HistoryItem>
, HistoryServiceDependentData {
CreditsAmount successPrice;
SuggestRefundType refundType = SuggestRefundType::None;
};
struct HistoryServiceGameScore
: RuntimeComponent<HistoryServiceGameScore, HistoryItem>
, HistoryServiceDependentData {

View file

@ -763,8 +763,10 @@ MessageFlags FlagsFromMTP(
| ((flags & MTP::f_video_processing_pending)
? Flag::EstimatedDate
: Flag())
| ((flags & MTP::f_paid_suggested_post)
? Flag::PaidSuggestedPost
| ((flags & MTP::f_paid_suggested_post_ton)
? Flag::TonPaidSuggested
: (flags & MTP::f_paid_suggested_post_stars)
? Flag::StarsPaidSuggested
: Flag());
}

View file

@ -117,7 +117,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
message#9815cec8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post:flags2.8?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost = Message;
message#9815cec8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost = Message;
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
@ -193,6 +193,8 @@ messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
messageActionSuggestedPostApproval#ee7a1596 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int price:flags.4?StarsAmount = MessageAction;
messageActionSuggestedPostSuccess#95ddcf69 price:StarsAmount = MessageAction;
messageActionSuggestedPostRefund#69f916f8 flags:# payer_initiated:flags.0?true = MessageAction;
messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;