mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-28 00:13:04 +02:00
Show approve/decline service messages.
This commit is contained in:
parent
cb987c1baf
commit
bf9492e083
17 changed files with 576 additions and 67 deletions
|
@ -818,6 +818,8 @@ PRIVATE
|
|||
history/view/media/history_view_sticker_player_abstract.h
|
||||
history/view/media/history_view_story_mention.cpp
|
||||
history/view/media/history_view_story_mention.h
|
||||
history/view/media/history_view_suggest_decision.cpp
|
||||
history/view/media/history_view_suggest_decision.h
|
||||
history/view/media/history_view_theme_document.cpp
|
||||
history/view/media/history_view_theme_document.h
|
||||
history/view/media/history_view_todo_list.cpp
|
||||
|
|
|
@ -4421,12 +4421,45 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_suggest_bar_priced_dated" = "{amount} {date}";
|
||||
"lng_suggest_bar_dated" = "Publish on {date}";
|
||||
"lng_suggest_options_title" = "Suggest a Message";
|
||||
"lng_suggest_options_change" = "Suggest Changes";
|
||||
"lng_suggest_options_price" = "Enter Price in Stars";
|
||||
"lng_suggest_options_price_about" = "Choose how many Stars you want to offer {channel} to publish this message.";
|
||||
"lng_suggest_options_price_about" = "Choose how many Stars to pay to publish this message.";
|
||||
"lng_suggest_options_date" = "Time";
|
||||
"lng_suggest_options_date_any" = "Anytime";
|
||||
"lng_suggest_options_date_about" = "Select the date and time you want the message to be published.";
|
||||
"lng_suggest_options_offer" = "Offer {amount}";
|
||||
"lng_suggest_options_update" = "Update Terms";
|
||||
|
||||
"lng_suggest_action_decline" = "Decline";
|
||||
"lng_suggest_action_accept" = "Accept";
|
||||
"lng_suggest_action_change" = "Suggest Changes";
|
||||
"lng_suggest_action_your" = "You suggest to post this message.";
|
||||
"lng_suggest_action_his" = "{from} suggests to post this message.";
|
||||
"lng_suggest_action_price_label" = "Price";
|
||||
"lng_suggest_action_time_label" = "Time";
|
||||
"lng_suggest_action_agreement" = "Agreement reached!";
|
||||
"lng_suggest_action_agree_date" = "The post will be automatically published on {channel} {date}.";
|
||||
"lng_suggest_action_your_charged" = "You have been charged {amount}.";
|
||||
"lng_suggest_action_his_charged" = "{from} have been charged {amount}.";
|
||||
"lng_suggest_action_agree_receive" = "{channel} will receive the Stars once the post has been live for 24 hours.";
|
||||
"lng_suggest_action_agree_removed" = "If {channel} removes the post before it has been live for 24 hours, the Stars will be refunded.";
|
||||
"lng_suggest_action_your_not_enough" = "**Transaction failed** because you didn't have enough Stars.";
|
||||
"lng_suggest_action_his_not_enough" = "**Transaction failed** because the user didn't have enough Stars.";
|
||||
"lng_suggest_action_declined" = "{from} rejected the message.";
|
||||
"lng_suggest_action_declined_reason" = "{from} rejected the message with the comment.";
|
||||
"lng_suggest_change_price" = "{from} suggests a new price for the message.";
|
||||
"lng_suggest_change_time" = "{from} suggests a new time for the message.";
|
||||
"lng_suggest_change_price_time" = "{from} suggests a new price and time for the message.";
|
||||
"lng_suggest_change_content" = "{from} suggests changes for the message.";
|
||||
"lng_suggest_change_price_label" = "New Price";
|
||||
"lng_suggest_change_time_label" = "New Time";
|
||||
"lng_suggest_change_text_label" = "Check the suggested message below";
|
||||
"lng_suggest_menu_edit_message" = "Edit Message";
|
||||
"lng_suggest_menu_edit_price" = "Edit Price";
|
||||
"lng_suggest_menu_edit_time" = "Edit Time";
|
||||
"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_reply_in_another_title" = "Reply in...";
|
||||
"lng_reply_in_another_chat" = "Reply in Another Chat";
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "apiwrap.h"
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "api/api_send_progress.h"
|
||||
#include "api/api_suggest_post.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "boxes/url_auth_box.h"
|
||||
|
@ -521,6 +522,27 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
|||
controller->showToast(tr::lng_text_copied(tr::now));
|
||||
}
|
||||
} break;
|
||||
|
||||
case ButtonType::SuggestAccept: {
|
||||
Api::AcceptClickHandler(item)->onClick(ClickContext{
|
||||
Qt::LeftButton,
|
||||
QVariant::fromValue(context),
|
||||
});
|
||||
} break;
|
||||
|
||||
case ButtonType::SuggestDecline: {
|
||||
Api::DeclineClickHandler(item)->onClick(ClickContext{
|
||||
Qt::LeftButton,
|
||||
QVariant::fromValue(context),
|
||||
});
|
||||
} break;
|
||||
|
||||
case ButtonType::SuggestChange: {
|
||||
Api::SuggestChangesClickHandler(item)->onClick(ClickContext{
|
||||
Qt::LeftButton,
|
||||
QVariant::fromValue(context),
|
||||
});
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/choose_date_time.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
@ -128,6 +134,49 @@ void RequestApprovalDate(
|
|||
controller->uiShow()->show(std::move(dateBox));
|
||||
}
|
||||
|
||||
void RequestDeclineComment(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto id = item->fullId();
|
||||
controller->uiShow()->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto callback = std::make_shared<Fn<void()>>();
|
||||
Ui::ConfirmBox(box, {
|
||||
.text = tr::lng_suggest_decline_text(
|
||||
lt_from,
|
||||
rpl::single(Ui::Text::Bold(item->from()->shortName())),
|
||||
Ui::Text::WithEntities),
|
||||
.confirmed = [=](Fn<void()> close) { (*callback)(); close(); },
|
||||
.confirmText = tr::lng_suggest_action_decline(),
|
||||
.confirmStyle = &st::attentionBoxButton,
|
||||
.title = tr::lng_suggest_decline_title(),
|
||||
});
|
||||
const auto reason = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::factcheckField,
|
||||
Ui::InputField::Mode::NoNewlines,
|
||||
tr::lng_suggest_decline_reason()));
|
||||
box->setFocusCallback([=] {
|
||||
reason->setFocusFast();
|
||||
});
|
||||
*callback = [=, weak = Ui::MakeWeak(box)] {
|
||||
const auto item = controller->session().data().message(id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
SendDecline(controller, item, reason->getLastText().trimmed());
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
reason->submits(
|
||||
) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) {
|
||||
if (!(modifiers & Qt::ShiftModifier)) {
|
||||
(*callback)();
|
||||
}
|
||||
}, box->lifetime());
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<ClickHandler> AcceptClickHandler(
|
||||
|
@ -157,24 +206,14 @@ std::shared_ptr<ClickHandler> AcceptClickHandler(
|
|||
|
||||
std::shared_ptr<ClickHandler> DeclineClickHandler(
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto session = &item->history()->session();
|
||||
const auto id = item->fullId();
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
if (!controller || &controller->session() != session) {
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
const auto item = session->data().message(id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto suggestion = item->Get<HistoryMessageSuggestedPost>();
|
||||
if (!suggestion) {
|
||||
return;
|
||||
} else {
|
||||
SendDecline(controller, item, "sorry, bro..");
|
||||
}
|
||||
RequestDeclineComment(controller, item);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -828,6 +828,8 @@ HistoryServiceDependentData *HistoryItem::GetServiceDependentData() {
|
|||
return done;
|
||||
} else if (const auto append = Get<HistoryServiceTodoAppendTasks>()) {
|
||||
return append;
|
||||
} else if (const auto decision = Get<HistoryServiceSuggestDecision>()) {
|
||||
return decision;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1068,8 +1070,58 @@ bool HistoryItem::checkDiscussionLink(ChannelId id) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void HistoryItem::setReplyMarkup(HistoryMessageMarkupData &&markup) {
|
||||
SuggestionActions HistoryItem::computeSuggestionActions() const {
|
||||
return computeSuggestionActions(Get<HistoryMessageSuggestedPost>());
|
||||
}
|
||||
|
||||
SuggestionActions HistoryItem::computeSuggestionActions(
|
||||
const HistoryMessageSuggestedPost *suggest) const {
|
||||
return suggest
|
||||
? computeSuggestionActions(suggest->accepted, suggest->rejected)
|
||||
: SuggestionActions::None;
|
||||
}
|
||||
|
||||
SuggestionActions HistoryItem::computeSuggestionActions(
|
||||
bool accepted,
|
||||
bool rejected) const {
|
||||
const auto channelIsAuthor = from()->isChannel();
|
||||
const auto amMonoforumAdmin = history()->peer->amMonoforumAdmin();
|
||||
const auto broadcast = history()->peer->monoforumBroadcast();
|
||||
const auto canDecline = isRegular()
|
||||
&& !(accepted || rejected)
|
||||
&& (channelIsAuthor ? !amMonoforumAdmin : amMonoforumAdmin);
|
||||
const auto canAccept = canDecline
|
||||
&& (channelIsAuthor
|
||||
? !amMonoforumAdmin
|
||||
: (amMonoforumAdmin
|
||||
&& broadcast
|
||||
&& broadcast->canPostMessages()));
|
||||
return canAccept
|
||||
? SuggestionActions::AcceptAndDecline
|
||||
: canDecline
|
||||
? SuggestionActions::Decline
|
||||
: SuggestionActions::None;
|
||||
}
|
||||
|
||||
void HistoryItem::updateSuggestControls(
|
||||
const HistoryMessageSuggestedPost *suggest) {
|
||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||
markup->updateSuggestControls(computeSuggestionActions(suggest));
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::setReplyMarkup(
|
||||
HistoryMessageMarkupData &&markup,
|
||||
bool ignoreSuggestButtons) {
|
||||
const auto requestUpdate = [&] {
|
||||
const auto actions = computeSuggestionActions();
|
||||
if (actions != SuggestionActions::None
|
||||
&& !Has<HistoryMessageReplyMarkup>()) {
|
||||
AddComponents(HistoryMessageReplyMarkup::Bit());
|
||||
}
|
||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||
markup->updateSuggestControls(actions);
|
||||
}
|
||||
history()->owner().requestItemResize(this);
|
||||
history()->session().changes().messageUpdated(
|
||||
this,
|
||||
|
@ -1901,8 +1953,10 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
|
|||
suggest->date = edition.suggest.date;
|
||||
suggest->accepted = edition.suggest.accepted;
|
||||
suggest->rejected = edition.suggest.rejected;
|
||||
updateSuggestControls(suggest);
|
||||
} else {
|
||||
RemoveComponents(HistoryMessageSuggestedPost::Bit());
|
||||
updateSuggestControls(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1942,7 +1996,7 @@ void HistoryItem::applyEdition(const MTPDmessageService &message) {
|
|||
const auto wasSublist = savedSublist();
|
||||
if (message.vaction().type() == mtpc_messageActionHistoryClear) {
|
||||
const auto wasGrouped = history()->owner().groups().isGrouped(this);
|
||||
setReplyMarkup({});
|
||||
setReplyMarkup({}, true);
|
||||
removeFromSharedMediaIndex();
|
||||
refreshMedia(nullptr);
|
||||
setTextValue({});
|
||||
|
@ -2144,8 +2198,10 @@ void HistoryItem::applyEditionToHistoryCleared() {
|
|||
).c_messageService());
|
||||
}
|
||||
|
||||
void HistoryItem::updateReplyMarkup(HistoryMessageMarkupData &&markup) {
|
||||
setReplyMarkup(std::move(markup));
|
||||
void HistoryItem::updateReplyMarkup(
|
||||
HistoryMessageMarkupData &&markup,
|
||||
bool ignoreSuggestButtons) {
|
||||
setReplyMarkup(std::move(markup), ignoreSuggestButtons);
|
||||
}
|
||||
|
||||
void HistoryItem::contributeToSlowmode(TimeId realDate) {
|
||||
|
@ -3865,6 +3921,12 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
|||
}
|
||||
if (config.suggest.exists) {
|
||||
mask |= HistoryMessageSuggestedPost::Bit();
|
||||
if (computeSuggestionActions(
|
||||
config.suggest.accepted,
|
||||
config.suggest.rejected
|
||||
) != SuggestionActions::None) {
|
||||
mask |= HistoryMessageReplyMarkup::Bit();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateComponents(mask);
|
||||
|
@ -3965,6 +4027,7 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
|||
suggest->date = config.suggest.date;
|
||||
suggest->accepted = config.suggest.accepted;
|
||||
suggest->rejected = config.suggest.rejected;
|
||||
updateSuggestControls(suggest);
|
||||
}
|
||||
|
||||
if (out() && isSending()) {
|
||||
|
@ -4601,6 +4664,15 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) {
|
|||
) | ranges::views::transform([&](const MTPTodoItem &item) {
|
||||
return TodoListItemFromMTP(session, item);
|
||||
}) | ranges::to_vector;
|
||||
} else if (type == mtpc_messageActionSuggestedPostApproval) {
|
||||
const auto &data = action.c_messageActionSuggestedPostApproval();
|
||||
UpdateComponents(HistoryServiceSuggestDecision::Bit());
|
||||
const auto decision = Get<HistoryServiceSuggestDecision>();
|
||||
decision->stars = data.vstars_amount().value_or_empty();
|
||||
decision->balanceTooLow = data.is_balance_too_low();
|
||||
decision->rejected = data.is_rejected();
|
||||
decision->rejectComment = qs(data.vreject_comment().value_or_empty());
|
||||
decision->date = data.vschedule_date().value_or_empty();
|
||||
}
|
||||
if (const auto replyTo = message.vreply_to()) {
|
||||
replyTo->match([&](const MTPDmessageReplyHeader &data) {
|
||||
|
@ -4629,7 +4701,7 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) {
|
|||
: PeerId();
|
||||
const auto requiresMonoforumPeer = _history->peer->amMonoforumAdmin();
|
||||
if (savedSublistPeer || requiresMonoforumPeer) {
|
||||
UpdateComponents(HistoryMessageSaved::Bit());
|
||||
AddComponents(HistoryMessageSaved::Bit());
|
||||
const auto saved = Get<HistoryMessageSaved>();
|
||||
saved->sublistPeerId = savedSublistPeer
|
||||
? savedSublistPeer
|
||||
|
@ -5950,14 +6022,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
};
|
||||
|
||||
auto prepareSuggestedPostApproval = [&](const MTPDmessageActionSuggestedPostApproval &data) {
|
||||
if (data.is_balance_too_low()) {
|
||||
return PreparedServiceText{ { u"balance too low :( need %1 stars"_q.arg(data.vstars_amount().value_or_empty()) } };
|
||||
} else if (data.is_rejected()) {
|
||||
return PreparedServiceText{ { u"rejected :( comment: %1"_q.arg(qs(data.vreject_comment().value_or_empty())) } };
|
||||
} else if (const auto date = data.vschedule_date().value_or_empty()) {
|
||||
return PreparedServiceText{ { u"approved!! for date: %1"_q.arg(langDateTime(base::unixtime::parse(date))) } };
|
||||
}
|
||||
return PreparedServiceText{ { "approved!!" } }; AssertIsDebug();
|
||||
return PreparedServiceText{ { u"hello"_q } };
|
||||
};
|
||||
|
||||
auto prepareConferenceCall = [&](const MTPDmessageActionConferenceCall &) -> PreparedServiceText {
|
||||
|
|
|
@ -22,6 +22,7 @@ struct HistoryMessageMarkupData;
|
|||
struct HistoryMessageReplyMarkup;
|
||||
struct HistoryMessageTranslation;
|
||||
struct HistoryMessageForwarded;
|
||||
struct HistoryMessageSuggestedPost;
|
||||
struct HistoryServiceDependentData;
|
||||
struct HistoryServiceTodoCompletions;
|
||||
enum class HistorySelfDestructType;
|
||||
|
@ -29,6 +30,7 @@ struct PreparedServiceText;
|
|||
struct MessageFactcheck;
|
||||
class ReplyKeyboard;
|
||||
struct LanguageId;
|
||||
enum class SuggestionActions : uchar;
|
||||
|
||||
namespace base {
|
||||
template <typename Enum>
|
||||
|
@ -353,7 +355,9 @@ public:
|
|||
void overrideMedia(std::unique_ptr<Data::Media> media);
|
||||
|
||||
void applyEditionToHistoryCleared();
|
||||
void updateReplyMarkup(HistoryMessageMarkupData &&markup);
|
||||
void updateReplyMarkup(
|
||||
HistoryMessageMarkupData &&markup,
|
||||
bool ignoreSuggestButtons = false);
|
||||
void contributeToSlowmode(TimeId realDate = 0);
|
||||
|
||||
void clearMediaAsExpired();
|
||||
|
@ -575,7 +579,16 @@ private:
|
|||
|
||||
[[nodiscard]] bool checkDiscussionLink(ChannelId id) const;
|
||||
|
||||
void setReplyMarkup(HistoryMessageMarkupData &&markup);
|
||||
void setReplyMarkup(
|
||||
HistoryMessageMarkupData &&markup,
|
||||
bool ignoreSuggestButtons = false);
|
||||
[[nodiscard]] SuggestionActions computeSuggestionActions() const;
|
||||
[[nodiscard]] SuggestionActions computeSuggestionActions(
|
||||
const HistoryMessageSuggestedPost *suggest) const;
|
||||
[[nodiscard]] SuggestionActions computeSuggestionActions(
|
||||
bool accepted,
|
||||
bool rejected) const;
|
||||
void updateSuggestControls(const HistoryMessageSuggestedPost *suggest);
|
||||
|
||||
void changeReplyToTopCounter(
|
||||
not_null<HistoryMessageReply*> reply,
|
||||
|
|
|
@ -1214,6 +1214,95 @@ bool HistoryMessageReplyMarkup::hiddenBy(Data::Media *media) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void HistoryMessageReplyMarkup::updateSuggestControls(
|
||||
SuggestionActions actions) {
|
||||
if (actions == SuggestionActions::AcceptAndDecline) {
|
||||
data.flags |= ReplyMarkupFlag::SuggestionAccept;
|
||||
} else {
|
||||
data.flags &= ~ReplyMarkupFlag::SuggestionAccept;
|
||||
}
|
||||
if (actions == SuggestionActions::None) {
|
||||
data.flags &= ~ReplyMarkupFlag::SuggestionDecline;
|
||||
} else {
|
||||
data.flags |= ReplyMarkupFlag::Inline
|
||||
| ReplyMarkupFlag::SuggestionDecline;
|
||||
}
|
||||
using Type = HistoryMessageMarkupButton::Type;
|
||||
const auto has = [&](Type type) {
|
||||
return !data.rows.empty()
|
||||
&& ranges::contains(
|
||||
data.rows.back(),
|
||||
type,
|
||||
&HistoryMessageMarkupButton::type);
|
||||
};
|
||||
if (actions == SuggestionActions::AcceptAndDecline) {
|
||||
// ... rows ...
|
||||
// [decline] | [accept]
|
||||
// [suggestchanges]
|
||||
if (has(Type::SuggestChange)) {
|
||||
// Nothing changed.
|
||||
} else {
|
||||
if (has(Type::SuggestDecline)) {
|
||||
data.rows.pop_back();
|
||||
}
|
||||
data.rows.push_back({
|
||||
{
|
||||
Type::SuggestDecline,
|
||||
tr::lng_suggest_action_decline(tr::now),
|
||||
},
|
||||
{
|
||||
Type::SuggestAccept,
|
||||
tr::lng_suggest_action_accept(tr::now),
|
||||
},
|
||||
});
|
||||
data.rows.push_back({ {
|
||||
Type::SuggestChange,
|
||||
tr::lng_suggest_action_change(tr::now),
|
||||
} });
|
||||
data.flags |= ReplyMarkupFlag::SuggestionAccept
|
||||
| ReplyMarkupFlag::SuggestionDecline;
|
||||
}
|
||||
if (data.rows.size() > 2) {
|
||||
data.flags |= ReplyMarkupFlag::SuggestionSeparator;
|
||||
} else {
|
||||
data.flags &= ~ReplyMarkupFlag::SuggestionSeparator;
|
||||
}
|
||||
} else {
|
||||
while (!data.rows.empty()) {
|
||||
if (has(Type::SuggestChange) || has(Type::SuggestAccept)) {
|
||||
data.rows.pop_back();
|
||||
} else if (has(Type::SuggestDecline)
|
||||
&& actions == SuggestionActions::None) {
|
||||
data.rows.pop_back();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
data.flags &= ~ReplyMarkupFlag::SuggestionAccept;
|
||||
if (actions == SuggestionActions::None) {
|
||||
data.flags &= ReplyMarkupFlag::SuggestionDecline;
|
||||
data.flags &= ~ReplyMarkupFlag::SuggestionSeparator;
|
||||
} else {
|
||||
if (!has(Type::SuggestDecline)) {
|
||||
// ... rows ...
|
||||
// [decline]
|
||||
data.rows.push_back({ {
|
||||
Type::SuggestDecline,
|
||||
tr::lng_suggest_action_decline(tr::now),
|
||||
} });
|
||||
data.flags |= ReplyMarkupFlag::SuggestionDecline;
|
||||
}
|
||||
if (data.rows.size() > 1) {
|
||||
data.flags |= ReplyMarkupFlag::SuggestionSeparator;
|
||||
} else {
|
||||
data.flags &= ~ReplyMarkupFlag::SuggestionSeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inlineKeyboard = nullptr;
|
||||
}
|
||||
|
||||
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default;
|
||||
|
||||
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal(
|
||||
|
|
|
@ -58,6 +58,12 @@ struct BotKeyboardButton;
|
|||
extern const char kOptionFastButtonsMode[];
|
||||
[[nodiscard]] bool FastButtonsMode();
|
||||
|
||||
enum class SuggestionActions : uchar {
|
||||
None,
|
||||
Decline,
|
||||
AcceptAndDecline,
|
||||
};
|
||||
|
||||
struct HistoryMessageVia : RuntimeComponent<HistoryMessageVia, HistoryItem> {
|
||||
void create(not_null<Data::Session*> owner, UserId userId);
|
||||
void resize(int32 availw) const;
|
||||
|
@ -383,6 +389,7 @@ struct HistoryMessageReplyMarkup
|
|||
|
||||
void createForwarded(const HistoryMessageReplyMarkup &original);
|
||||
void updateData(HistoryMessageMarkupData &&markup);
|
||||
void updateSuggestControls(SuggestionActions actions);
|
||||
|
||||
[[nodiscard]] bool hiddenBy(Data::Media *media) const;
|
||||
|
||||
|
@ -691,6 +698,16 @@ struct HistoryServiceTodoAppendTasks
|
|||
[[nodiscard]] TextWithEntities ComposeTodoTasksList(
|
||||
not_null<HistoryServiceTodoAppendTasks*> append);
|
||||
|
||||
struct HistoryServiceSuggestDecision
|
||||
: RuntimeComponent<HistoryServiceSuggestDecision, HistoryItem>
|
||||
, HistoryServiceDependentData {
|
||||
int stars = 0;
|
||||
TimeId date = 0;
|
||||
QString rejectComment;
|
||||
bool rejected = false;
|
||||
bool balanceTooLow = false;
|
||||
};
|
||||
|
||||
struct HistoryServiceGameScore
|
||||
: RuntimeComponent<HistoryServiceGameScore, HistoryItem>
|
||||
, HistoryServiceDependentData {
|
||||
|
|
|
@ -37,6 +37,9 @@ enum class ReplyMarkupFlag : uint32 {
|
|||
IsNull = (1U << 7),
|
||||
OnlyBuyButton = (1U << 8),
|
||||
Persistent = (1U << 9),
|
||||
SuggestionDecline = (1U << 10),
|
||||
SuggestionAccept = (1U << 11),
|
||||
SuggestionSeparator = (1U << 12),
|
||||
};
|
||||
inline constexpr bool is_flag_type(ReplyMarkupFlag) { return true; }
|
||||
using ReplyMarkupFlags = base::flags<ReplyMarkupFlag>;
|
||||
|
@ -85,6 +88,10 @@ struct HistoryMessageMarkupButton {
|
|||
WebView,
|
||||
SimpleWebView,
|
||||
CopyText,
|
||||
|
||||
SuggestDecline,
|
||||
SuggestAccept,
|
||||
SuggestChange,
|
||||
};
|
||||
|
||||
HistoryMessageMarkupButton(
|
||||
|
|
|
@ -9,11 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "history/view/media/history_view_media_generic.h"
|
||||
#include "history/view/media/history_view_media_grouped.h"
|
||||
#include "history/view/media/history_view_similar_channels.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/media/history_view_large_emoji.h"
|
||||
#include "history/view/media/history_view_custom_emoji.h"
|
||||
#include "history/view/media/history_view_suggest_decision.h"
|
||||
#include "history/view/reactions/history_view_reactions_button.h"
|
||||
#include "history/view/reactions/history_view_reactions.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
|
@ -1072,6 +1074,16 @@ void Element::refreshMedia(Element *replacing) {
|
|||
this,
|
||||
std::make_unique<LargeEmoji>(this, emoji));
|
||||
}
|
||||
} else if (const auto decision = item->Get<HistoryServiceSuggestDecision>()) {
|
||||
_media = std::make_unique<MediaGeneric>(
|
||||
this,
|
||||
GenerateSuggestDecisionMedia(this, decision),
|
||||
MediaGenericDescriptor{
|
||||
.maxWidth = st::chatSuggestInfoWidth,
|
||||
.serviceLink = decision->lnk,
|
||||
.service = true,
|
||||
.hideServiceText = true,
|
||||
});
|
||||
} else {
|
||||
_media = nullptr;
|
||||
}
|
||||
|
|
|
@ -468,33 +468,6 @@ void Message::initPaidInformation() {
|
|||
} else {
|
||||
text = { { u"suggestion to publish for %1 stars %2"_q.arg(suggest->stars).arg(langDateTime(base::unixtime::parse(suggest->date))) } };
|
||||
}
|
||||
const auto channelIsAuthor = item->from()->isChannel();
|
||||
const auto amMonoforumAdmin = item->history()->peer->amMonoforumAdmin();
|
||||
const auto broadcast = item->history()->peer->monoforumBroadcast();
|
||||
const auto canDecline = item->isRegular()
|
||||
&& !(suggest->accepted || suggest->rejected)
|
||||
&& (channelIsAuthor ? !amMonoforumAdmin : amMonoforumAdmin);
|
||||
const auto canAccept = canDecline
|
||||
&& (channelIsAuthor
|
||||
? !amMonoforumAdmin
|
||||
: (amMonoforumAdmin
|
||||
&& broadcast
|
||||
&& broadcast->canPostMessages()));
|
||||
if (canDecline) {
|
||||
text.links.push_back(Api::DeclineClickHandler(item));
|
||||
text.text.append(", ").append(Ui::Text::Link("[Decline]", text.links.size()));
|
||||
if (canAccept) {
|
||||
text.links.push_back(Api::AcceptClickHandler(item));
|
||||
text.text.append(", ").append(Ui::Text::Link("[Accept]", text.links.size()));
|
||||
|
||||
text.links.push_back(Api::SuggestChangesClickHandler(item));
|
||||
text.text.append(", ").append(Ui::Text::Link("[SuggestChanges]", text.links.size()));
|
||||
}
|
||||
} else if (suggest->accepted) {
|
||||
text.text.append(", accepted!");
|
||||
} else if (suggest->rejected) {
|
||||
text.text.append(", rejected :(");
|
||||
}
|
||||
setServicePreMessage(std::move(text));
|
||||
}
|
||||
|
||||
|
|
|
@ -78,9 +78,7 @@ void EditOptionsBox(
|
|||
Ui::AddSkip(container);
|
||||
Ui::AddDividerText(
|
||||
container,
|
||||
tr::lng_suggest_options_price_about(
|
||||
lt_channel,
|
||||
rpl::single(args.channelName)));
|
||||
tr::lng_suggest_options_price_about());
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto time = Settings::AddButtonWithLabel(
|
||||
|
|
|
@ -236,9 +236,11 @@ MediaGenericTextPart::MediaGenericTextPart(
|
|||
QMargins margins,
|
||||
const style::TextStyle &st,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links,
|
||||
const Ui::Text::MarkedContext &context)
|
||||
const Ui::Text::MarkedContext &context,
|
||||
style::align align)
|
||||
: _text(st::msgMinWidth)
|
||||
, _margins(margins) {
|
||||
, _margins(margins)
|
||||
, _align(align) {
|
||||
_text.setMarkedText(
|
||||
st,
|
||||
text,
|
||||
|
@ -254,12 +256,18 @@ void MediaGenericTextPart::draw(
|
|||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context,
|
||||
int outerWidth) const {
|
||||
const auto use = (width() - _margins.left() - _margins.right());
|
||||
setupPen(p, owner, context);
|
||||
_text.draw(p, {
|
||||
.position = { (outerWidth - width()) / 2, _margins.top() },
|
||||
.position = {
|
||||
((_align == style::al_top)
|
||||
? ((outerWidth - use) / 2)
|
||||
: _margins.left()),
|
||||
_margins.top(),
|
||||
},
|
||||
.outerWidth = outerWidth,
|
||||
.availableWidth = width(),
|
||||
.align = style::al_top,
|
||||
.availableWidth = use,
|
||||
.align = _align,
|
||||
.palette = &(owner->service()
|
||||
? context.st->serviceTextPalette()
|
||||
: context.messageStyle()->textPalette),
|
||||
|
@ -284,11 +292,17 @@ TextState MediaGenericTextPart::textState(
|
|||
QPoint point,
|
||||
StateRequest request,
|
||||
int outerWidth) const {
|
||||
point -= QPoint{ (outerWidth - width()) / 2, _margins.top() };
|
||||
const auto use = (width() - _margins.left() - _margins.right());
|
||||
point -= QPoint{
|
||||
((_align == style::al_top)
|
||||
? ((outerWidth - use) / 2)
|
||||
: _margins.left()),
|
||||
_margins.top(),
|
||||
};
|
||||
auto result = TextState();
|
||||
auto forText = request.forText();
|
||||
forText.align = style::al_top;
|
||||
result.link = _text.getState(point, width(), forText).link;
|
||||
forText.align = _align;
|
||||
result.link = _text.getState(point, use, forText).link;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,8 @@ public:
|
|||
QMargins margins,
|
||||
const style::TextStyle &st = st::defaultTextStyle,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links = {},
|
||||
const Ui::Text::MarkedContext &context = {});
|
||||
const Ui::Text::MarkedContext &context = {},
|
||||
style::align align = style::al_top);
|
||||
|
||||
void draw(
|
||||
Painter &p,
|
||||
|
@ -165,6 +166,7 @@ protected:
|
|||
private:
|
||||
Ui::Text::String _text;
|
||||
QMargins _margins;
|
||||
style::align _align = {};
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "history/view/media/history_view_suggest_decision.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/view/media/history_view_media_generic.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_credits.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
enum EmojiType {
|
||||
kAgreement,
|
||||
kCalendar,
|
||||
kMoney,
|
||||
kHourglass,
|
||||
kReload,
|
||||
kDecline,
|
||||
kDiscard,
|
||||
kWarning,
|
||||
};
|
||||
|
||||
[[nodiscard]] const char *Raw(EmojiType type) {
|
||||
switch (type) {
|
||||
case EmojiType::kAgreement: return "\xf0\x9f\xa4\x9d";
|
||||
case EmojiType::kCalendar: return "\xf0\x9f\x93\x86";
|
||||
case EmojiType::kMoney: return "\xf0\x9f\x92\xb0";
|
||||
case EmojiType::kHourglass: return "\xe2\x8c\x9b\xef\xb8\x8f";
|
||||
case EmojiType::kReload: return "\xf0\x9f\x94\x84";
|
||||
case EmojiType::kDecline: return "\xe2\x9d\x8c";
|
||||
case EmojiType::kDiscard: return "\xf0\x9f\x9a\xab";
|
||||
case EmojiType::kWarning: return "\xe2\x9a\xa0\xef\xb8\x8f";
|
||||
}
|
||||
Unexpected("EmojiType in Raw.");
|
||||
}
|
||||
|
||||
[[nodiscard]] QString Emoji(EmojiType type) {
|
||||
return QString::fromUtf8(Raw(type));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto GenerateSuggestDecisionMedia(
|
||||
not_null<Element*> parent,
|
||||
not_null<const HistoryServiceSuggestDecision*> decision)
|
||||
-> Fn<void(
|
||||
not_null<MediaGeneric*>,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||
return [=](
|
||||
not_null<MediaGeneric*> media,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
|
||||
const auto peer = parent->history()->peer;
|
||||
const auto broadcast = peer->monoforumBroadcast();
|
||||
if (!broadcast) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto sublistPeerId = parent->data()->sublistPeerId();
|
||||
const auto sublistPeer = peer->owner().peer(sublistPeerId);
|
||||
|
||||
auto pushText = [&](
|
||||
TextWithEntities text,
|
||||
QMargins margins = {},
|
||||
style::align align = style::al_left,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links = {}) {
|
||||
push(std::make_unique<MediaGenericTextPart>(
|
||||
std::move(text),
|
||||
margins,
|
||||
st::defaultTextStyle,
|
||||
links,
|
||||
Ui::Text::MarkedContext(),
|
||||
align));
|
||||
};
|
||||
|
||||
if (decision->balanceTooLow) {
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kWarning)).append(' ').append(
|
||||
(sublistPeer->isSelf()
|
||||
? tr::lng_suggest_action_your_not_enough
|
||||
: tr::lng_suggest_action_his_not_enough)(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)),
|
||||
st::chatSuggestInfoFullMargin,
|
||||
style::al_top);
|
||||
} else if (decision->rejected) {
|
||||
const auto withComment = !decision->rejectComment.isEmpty();
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kDecline)).append(' ').append(
|
||||
(withComment
|
||||
? tr::lng_suggest_action_declined_reason
|
||||
: tr::lng_suggest_action_declined)(
|
||||
tr::now,
|
||||
lt_from,
|
||||
Ui::Text::Bold(broadcast->name()),
|
||||
Ui::Text::WithEntities)),
|
||||
(withComment
|
||||
? st::chatSuggestInfoTitleMargin
|
||||
: st::chatSuggestInfoFullMargin));
|
||||
if (withComment) {
|
||||
pushText(
|
||||
TextWithEntities().append('"').append(
|
||||
decision->rejectComment
|
||||
).append('"'),
|
||||
st::chatSuggestInfoLastMargin,
|
||||
style::al_top);
|
||||
}
|
||||
} else {
|
||||
const auto stars = decision->stars;
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kAgreement)).append(' ').append(
|
||||
Ui::Text::Bold(tr::lng_suggest_action_agreement(tr::now))
|
||||
),
|
||||
st::chatSuggestInfoTitleMargin,
|
||||
style::al_top);
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kCalendar)).append(' ').append(
|
||||
tr::lng_suggest_action_agree_date(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
Ui::Text::Bold(broadcast->name()),
|
||||
lt_date,
|
||||
Ui::Text::Bold(Ui::FormatDateTime(
|
||||
base::unixtime::parse(decision->date))),
|
||||
Ui::Text::WithEntities)),
|
||||
(stars
|
||||
? st::chatSuggestInfoMiddleMargin
|
||||
: st::chatSuggestInfoLastMargin));
|
||||
if (stars) {
|
||||
const auto amount = Ui::Text::Bold(
|
||||
tr::lng_prize_credits_amount(tr::now, lt_count, stars));
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kMoney)).append(' ').append(
|
||||
(sublistPeer->isSelf()
|
||||
? tr::lng_suggest_action_your_charged(
|
||||
tr::now,
|
||||
lt_amount,
|
||||
amount,
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_suggest_action_his_charged(
|
||||
tr::now,
|
||||
lt_from,
|
||||
Ui::Text::Bold(sublistPeer->shortName()),
|
||||
lt_amount,
|
||||
amount,
|
||||
Ui::Text::WithEntities))),
|
||||
st::chatSuggestInfoMiddleMargin);
|
||||
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kHourglass)).append(' ').append(
|
||||
tr::lng_suggest_action_agree_receive(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
Ui::Text::Bold(broadcast->name()),
|
||||
Ui::Text::WithEntities)),
|
||||
st::chatSuggestInfoMiddleMargin);
|
||||
|
||||
pushText(
|
||||
TextWithEntities(
|
||||
).append(Emoji(kReload)).append(' ').append(
|
||||
tr::lng_suggest_action_agree_removed(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
Ui::Text::Bold(broadcast->name()),
|
||||
Ui::Text::WithEntities)),
|
||||
st::chatSuggestInfoLastMargin);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
struct HistoryServiceSuggestDecision;
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class Element;
|
||||
class MediaGeneric;
|
||||
class MediaGenericPart;
|
||||
|
||||
auto GenerateSuggestDecisionMedia(
|
||||
not_null<Element*> parent,
|
||||
not_null<const HistoryServiceSuggestDecision*> decision
|
||||
) -> Fn<void(
|
||||
not_null<MediaGeneric*>,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)>)>;
|
||||
|
||||
} // namespace HistoryView
|
|
@ -1052,6 +1052,12 @@ chatSimilarName: TextStyle(defaultTextStyle) {
|
|||
chatSimilarWidthMax: 424px;
|
||||
chatSimilarSkip: 12px;
|
||||
|
||||
chatSuggestInfoWidth: 272px;
|
||||
chatSuggestInfoTitleMargin: margins(16px, 16px, 16px, 6px);
|
||||
chatSuggestInfoMiddleMargin: margins(16px, 4px, 16px, 4px);
|
||||
chatSuggestInfoLastMargin: margins(16px, 4px, 16px, 16px);
|
||||
chatSuggestInfoFullMargin: margins(16px, 16px, 16px, 16px);
|
||||
|
||||
premiumRequiredWidth: 186px;
|
||||
premiumRequiredIcon: icon{{ "chat/large_lockedchat", msgServiceFg }};
|
||||
premiumRequiredCircle: 60px;
|
||||
|
|
Loading…
Add table
Reference in a new issue