Added api support for reports with server options.

This commit is contained in:
23rd 2024-09-26 23:30:33 +03:00 committed by John Preston
parent 00215622cc
commit 3edf8e10e2
24 changed files with 419 additions and 233 deletions

View file

@ -606,6 +606,7 @@ PRIVATE
data/data_replies_list.h data/data_replies_list.h
data/data_reply_preview.cpp data/data_reply_preview.cpp
data/data_reply_preview.h data/data_reply_preview.h
data/data_report.h
data/data_saved_messages.cpp data/data_saved_messages.cpp
data/data_saved_messages.h data/data_saved_messages.h
data/data_saved_sublist.cpp data/data_saved_sublist.cpp

View file

@ -1662,6 +1662,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_report_and_ban_button" = "Ban user"; "lng_report_and_ban_button" = "Ban user";
"lng_report_details_about" = "Please enter any additional details relevant to your report."; "lng_report_details_about" = "Please enter any additional details relevant to your report.";
"lng_report_details" = "Additional Details"; "lng_report_details" = "Additional Details";
"lng_report_details_optional" = "Add Comment (Optional)";
"lng_report_details_non_optional" = "Add Comment";
"lng_report_details_message_about" = "Please help us by telling what is wrong with the message you have selected";
"lng_report_reason_spam" = "Spam"; "lng_report_reason_spam" = "Spam";
"lng_report_reason_fake" = "Fake Account"; "lng_report_reason_fake" = "Fake Account";
"lng_report_reason_violence" = "Violence"; "lng_report_reason_violence" = "Violence";

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_report.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -40,15 +41,11 @@ MTPreportReason ReasonToTL(const Ui::ReportReason &reason) {
} // namespace } // namespace
void SendReport( void SendReport(
std::shared_ptr<Ui::Show> show, std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::ReportReason reason, Ui::ReportReason reason,
const QString &comment, const QString &comment,
std::variant< std::variant<v::null_t, not_null<PhotoData*>> data) {
v::null_t,
MessageIdsList,
not_null<PhotoData*>,
StoryId> data) {
auto done = [=] { auto done = [=] {
show->showToast(tr::lng_report_thanks(tr::now)); show->showToast(tr::lng_report_thanks(tr::now));
}; };
@ -58,18 +55,6 @@ void SendReport(
ReasonToTL(reason), ReasonToTL(reason),
MTP_string(comment) MTP_string(comment)
)).done(std::move(done)).send(); )).done(std::move(done)).send();
}, [&](const MessageIdsList &ids) {
auto apiIds = QVector<MTPint>();
apiIds.reserve(ids.size());
for (const auto &fullId : ids) {
apiIds.push_back(MTP_int(fullId.msg));
}
peer->session().api().request(MTPmessages_Report(
peer->input,
MTP_vector<MTPint>(apiIds),
ReasonToTL(reason),
MTP_string(comment)
)).done(std::move(done)).send();
}, [&](not_null<PhotoData*> photo) { }, [&](not_null<PhotoData*> photo) {
peer->session().api().request(MTPaccount_ReportProfilePhoto( peer->session().api().request(MTPaccount_ReportProfilePhoto(
peer->input, peer->input,
@ -77,14 +62,93 @@ void SendReport(
ReasonToTL(reason), ReasonToTL(reason),
MTP_string(comment) MTP_string(comment)
)).done(std::move(done)).send(); )).done(std::move(done)).send();
}, [&](StoryId id) {
peer->session().api().request(MTPstories_Report(
peer->input,
MTP_vector<MTPint>(1, MTP_int(id)),
ReasonToTL(reason),
MTP_string(comment)
)).done(std::move(done)).send();
}); });
} }
auto CreateReportMessagesOrStoriesCallback(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer)
-> Fn<void(Data::ReportInput, Fn<void(ReportResult)>)> {
using TLChoose = MTPDreportResultChooseOption;
using TLAddComment = MTPDreportResultAddComment;
using TLReported = MTPDreportResultReported;
using Result = ReportResult;
struct State final {
#ifdef _DEBUG
~State() {
qDebug() << "Messages or Stories Report ~State().";
}
#endif
mtpRequestId requestId = 0;
};
const auto state = std::make_shared<State>();
return [=](
Data::ReportInput reportInput,
Fn<void(Result)> done) {
auto apiIds = QVector<MTPint>();
apiIds.reserve(reportInput.ids.size() + reportInput.stories.size());
for (const auto &id : reportInput.ids) {
apiIds.push_back(MTP_int(id));
}
for (const auto &story : reportInput.stories) {
apiIds.push_back(MTP_int(story));
}
const auto received = [=](
const MTPReportResult &result,
mtpRequestId requestId) {
if (state->requestId != requestId) {
return;
}
state->requestId = 0;
done(result.match([&](const TLChoose &data) {
const auto t = qs(data.vtitle());
auto list = Result::Options();
list.reserve(data.voptions().v.size());
for (const auto &tl : data.voptions().v) {
list.emplace_back(Result::Option{
.id = tl.data().voption().v,
.text = qs(tl.data().vtext()),
});
}
return Result{ .options = std::move(list), .title = t };
}, [&](const TLAddComment &data) -> Result {
return {
.commentOption = ReportResult::CommentOption{
.optional = data.is_optional(),
.id = data.voption().v,
}
};
}, [&](const TLReported &data) -> Result {
return { .successful = true };
}));
};
const auto fail = [=](const MTP::Error &error) {
state->requestId = 0;
done({ .error = error.type() });
};
if (!reportInput.stories.empty()) {
state->requestId = peer->session().api().request(
MTPstories_Report(
peer->input,
MTP_vector<MTPint>(apiIds),
MTP_bytes(reportInput.optionId),
MTP_string(reportInput.comment))
).done(received).fail(fail).send();
} else {
state->requestId = peer->session().api().request(
MTPmessages_Report(
peer->input,
MTP_vector<MTPint>(apiIds),
MTP_bytes(reportInput.optionId),
MTP_string(reportInput.comment))
).done(received).fail(fail).send();
}
};
}
} // namespace Api } // namespace Api

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
class HistoryItem;
class PeerData; class PeerData;
class PhotoData; class PhotoData;
@ -15,17 +16,41 @@ class Show;
enum class ReportReason; enum class ReportReason;
} // namespace Ui } // namespace Ui
namespace Data {
struct ReportInput;
} // namespace Data
namespace Api { namespace Api {
struct ReportResult final {
using Id = QByteArray;
struct Option final {
Id id = 0;
QString text;
};
using Options = std::vector<Option>;
Options options;
QString title;
QString error;
QString comment;
struct CommentOption {
bool optional = false;
Id id = 0;
};
std::optional<CommentOption> commentOption;
bool successful = false;
};
void SendReport( void SendReport(
std::shared_ptr<Ui::Show> show, std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::ReportReason reason, Ui::ReportReason reason,
const QString &comment, const QString &comment,
std::variant< std::variant<v::null_t, not_null<PhotoData*>> data);
v::null_t,
MessageIdsList, [[nodiscard]] auto CreateReportMessagesOrStoriesCallback(
not_null<PhotoData*>, std::shared_ptr<Ui::Show> show,
StoryId> data); not_null<PeerData*> peer)
-> Fn<void(Data::ReportInput, Fn<void(ReportResult)>)>;
} // namespace Api } // namespace Api

View file

@ -8,23 +8,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/report_messages_box.h" #include "boxes/report_messages_box.h"
#include "api/api_report.h" #include "api/api_report.h"
#include "core/application.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/boxes/report_box.h" #include "ui/boxes/report_box.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/rect.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "styles/style_settings.h"
namespace { namespace {
[[nodiscard]] object_ptr<Ui::BoxContent> Report( [[nodiscard]] object_ptr<Ui::BoxContent> Report(
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::variant< std::variant<v::null_t, not_null<PhotoData*>> data,
v::null_t,
MessageIdsList,
not_null<PhotoData*>,
StoryId> data,
const style::ReportBox *stOverride) { const style::ReportBox *stOverride) {
const auto source = v::match(data, [](const MessageIdsList &ids) { const auto source = v::match(data, [](const MessageIdsList &ids) {
return Ui::ReportSource::Message; return Ui::ReportSource::Message;
@ -62,64 +66,125 @@ namespace {
} // namespace } // namespace
object_ptr<Ui::BoxContent> ReportItemsBox(
not_null<PeerData*> peer,
MessageIdsList ids) {
return Report(peer, ids, nullptr);
}
object_ptr<Ui::BoxContent> ReportProfilePhotoBox( object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<PhotoData*> photo) { not_null<PhotoData*> photo) {
return Report(peer, photo, nullptr); return Report(peer, photo, nullptr);
} }
void ShowReportPeerBox( void ShowReportMessageBox(
not_null<Window::SessionController*> window, std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer) { not_null<PeerData*> peer,
struct State { const std::vector<MsgId> &ids,
QPointer<Ui::BoxContent> reasonBox; const std::vector<StoryId> &stories) {
QPointer<Ui::BoxContent> detailsBox; const auto report = Api::CreateReportMessagesOrStoriesCallback(
MessageIdsList ids; show,
}; peer);
const auto state = std::make_shared<State>();
const auto chosen = [=](Ui::ReportReason reason) { auto performRequest = [=](
const auto send = [=](const QString &text) { const auto &repeatRequest,
window->clearChooseReportMessages(); Data::ReportInput reportInput) -> void {
Api::SendReport( constexpr auto kToastDuration = crl::time(4000);
window->uiShow(), report(reportInput, [=](const Api::ReportResult &result) {
peer, if (!result.error.isEmpty()) {
reason, if (result.error == u"MESSAGE_ID_REQUIRED"_q) {
text, const auto widget = show->toastParent();
std::move(state->ids)); const auto window = Core::App().findWindow(widget);
if (const auto strong = state->reasonBox.data()) { const auto controller = window
strong->closeBox(); ? window->sessionController()
: nullptr;
if (controller) {
const auto callback = [=](std::vector<MsgId> ids) {
auto copy = reportInput;
copy.ids = std::move(ids);
repeatRequest(repeatRequest, std::move(copy));
};
controller->showChooseReportMessages(
peer,
reportInput,
std::move(callback));
}
} else {
show->showToast(result.error);
}
return;
} }
if (const auto strong = state->detailsBox.data()) { if (!result.options.empty() || result.commentOption) {
strong->closeBox(); show->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setTitle(
rpl::single(
result.title.isEmpty()
? reportInput.optionText
: result.title));
for (const auto &option : result.options) {
const auto button = Ui::AddReportOptionButton(
box->verticalLayout(),
option.text);
button->setClickedCallback([=] {
auto copy = reportInput;
copy.optionId = option.id;
copy.optionText = option.text;
repeatRequest(repeatRequest, std::move(copy));
});
}
if (const auto commentOption = result.commentOption) {
constexpr auto kReportReasonLengthMax = 512;
const auto &st = st::defaultReportBox;
Ui::AddReportDetailsIconButton(box);
Ui::AddSkip(box->verticalLayout());
Ui::AddSkip(box->verticalLayout());
const auto details = box->addRow(
object_ptr<Ui::InputField>(
box,
st.field,
Ui::InputField::Mode::MultiLine,
commentOption->optional
? tr::lng_report_details_optional()
: tr::lng_report_details_non_optional(),
QString()));
Ui::AddSkip(box->verticalLayout());
Ui::AddSkip(box->verticalLayout());
Ui::AddDividerText(
box->verticalLayout(),
tr::lng_report_details_message_about());
details->setMaxLength(kReportReasonLengthMax);
box->setFocusCallback([=] {
details->setFocusFast();
});
const auto submit = [=] {
if (!commentOption->optional
&& details->empty()) {
details->showError();
details->setFocus();
return;
}
auto copy = reportInput;
copy.optionId = commentOption->id;
copy.comment = details->getLastText();
repeatRequest(repeatRequest, std::move(copy));
};
details->submits(
) | rpl::start_with_next(submit, details->lifetime());
box->addButton(tr::lng_report_button(), submit);
} else {
box->addButton(
tr::lng_close(),
[=] { show->hideLayer(); });
}
if (!reportInput.optionId.isNull()) {
box->addLeftButton(
tr::lng_create_group_back(),
[=] { box->closeBox(); });
}
}));
} else if (result.successful) {
show->showToast(
tr::lng_report_thanks(tr::now),
kToastDuration);
show->hideLayer();
} }
};
if (reason == Ui::ReportReason::Fake
|| reason == Ui::ReportReason::Other) {
state->ids = {};
state->detailsBox = window->show(
Box(Ui::ReportDetailsBox, st::defaultReportBox, send));
return;
}
window->showChooseReportMessages(peer, reason, [=](
MessageIdsList ids) {
state->ids = std::move(ids);
state->detailsBox = window->show(
Box(Ui::ReportDetailsBox, st::defaultReportBox, send));
}); });
}; };
state->reasonBox = window->show(Box( performRequest(performRequest, { .ids = ids, .stories = stories });
Ui::ReportReasonBox,
st::defaultReportBox,
(peer->isBroadcast()
? Ui::ReportSource::Channel
: peer->isUser()
? Ui::ReportSource::Bot
: Ui::ReportSource::Group),
chosen));
} }

View file

@ -12,20 +12,17 @@ class object_ptr;
namespace Ui { namespace Ui {
class BoxContent; class BoxContent;
class Show;
} // namespace Ui } // namespace Ui
namespace Window {
class SessionController;
} // namespace Main
class PeerData; class PeerData;
[[nodiscard]] object_ptr<Ui::BoxContent> ReportItemsBox(
not_null<PeerData*> peer,
MessageIdsList ids);
[[nodiscard]] object_ptr<Ui::BoxContent> ReportProfilePhotoBox( [[nodiscard]] object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<PhotoData*> photo); not_null<PhotoData*> photo);
void ShowReportPeerBox(
not_null<Window::SessionController*> window, void ShowReportMessageBox(
not_null<PeerData*> peer); std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
const std::vector<MsgId> &ids,
const std::vector<StoryId> &stories);

View file

@ -0,0 +1,24 @@
/*
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
namespace Data {
struct ReportInput final {
QByteArray optionId;
QString optionText;
QString comment;
std::vector<MsgId> ids;
std::vector<StoryId> stories;
inline bool operator==(const ReportInput &other) const {
return optionId == other.optionId && comment == other.comment;
}
};
} // namespace Data

View file

@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "data/data_stories.h" #include "data/data_stories.h"
#include "api/api_report.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "boxes/report_messages_box.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "core/application.h" #include "core/application.h"
#include "data/data_changes.h" #include "data/data_changes.h"
@ -1912,7 +1912,7 @@ void Stories::report(
QString text) { QString text) {
if (const auto maybeStory = lookup(id)) { if (const auto maybeStory = lookup(id)) {
const auto story = *maybeStory; const auto story = *maybeStory;
Api::SendReport(show, story->peer(), reason, text, story->id()); ShowReportMessageBox(show, story->peer(), {}, { story->id() });
} }
} }

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "history/history_item_helpers.h" #include "history/history_item_helpers.h"
#include "history/view/controls/history_view_forward_panel.h" #include "history/view/controls/history_view_forward_panel.h"
#include "api/api_report.h"
#include "history/view/controls/history_view_draft_options.h" #include "history/view/controls/history_view_draft_options.h"
#include "boxes/moderate_messages_box.h" #include "boxes/moderate_messages_box.h"
#include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_sticker.h"
@ -4215,8 +4216,8 @@ int HistoryInner::historyDrawTop() const {
return (top >= 0) ? (top + _historySkipHeight) : -1; return (top >= 0) ? (top + _historySkipHeight) : -1;
} }
void HistoryInner::setChooseReportReason(Ui::ReportReason reason) { void HistoryInner::setChooseReportReason(Data::ReportInput reportInput) {
_chooseForReportReason = reason; _chooseForReportReason = reportInput;
} }
void HistoryInner::clearChooseReportReason() { void HistoryInner::clearChooseReportReason() {
@ -4501,17 +4502,21 @@ void HistoryInner::deleteAsGroup(FullMsgId itemId) {
} }
void HistoryInner::reportItem(FullMsgId itemId) { void HistoryInner::reportItem(FullMsgId itemId) {
_controller->show(ReportItemsBox(_peer, { 1, itemId })); ShowReportMessageBox(_controller->uiShow(), _peer, { itemId.msg }, {});
} }
void HistoryInner::reportAsGroup(FullMsgId itemId) { void HistoryInner::reportAsGroup(FullMsgId itemId) {
if (const auto item = session().data().message(itemId)) { if (const auto item = session().data().message(itemId)) {
const auto group = session().data().groups().find(item); const auto group = session().data().groups().find(item);
_controller->show(ReportItemsBox( const auto ids = group
_peer, ? (ranges::views::all(
(group group->items
? session().data().itemsToIds(group->items) ) | ranges::views::transform([](const auto &i) {
: MessageIdsList{ 1, itemId }))); return i->fullId().msg;
}) | ranges::to_vector)
: std::vector<MsgId>{ 1, itemId.msg };
const auto peer = item->history()->peer;
ShowReportMessageBox(_controller->uiShow(), _peer, ids, {});
} }
} }

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "base/timer.h" #include "base/timer.h"
#include "data/data_report.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/dragging_scroll_manager.h" #include "ui/dragging_scroll_manager.h"
@ -54,7 +55,6 @@ namespace Ui {
class ChatTheme; class ChatTheme;
class ChatStyle; class ChatStyle;
class PopupMenu; class PopupMenu;
enum class ReportReason;
struct ChatPaintContext; struct ChatPaintContext;
class PathShiftGradient; class PathShiftGradient;
struct PeerUserpicView; struct PeerUserpicView;
@ -189,7 +189,7 @@ public:
int historyTop() const; int historyTop() const;
int historyDrawTop() const; int historyDrawTop() const;
void setChooseReportReason(Ui::ReportReason reason); void setChooseReportReason(Data::ReportInput reportInput);
void clearChooseReportReason(); void clearChooseReportReason();
// -1 if should not be visible, -2 if bad history() // -1 if should not be visible, -2 if bad history()
@ -468,7 +468,7 @@ private:
style::cursor _cursor = style::cur_default; style::cursor _cursor = style::cur_default;
SelectedItems _selected; SelectedItems _selected;
std::optional<Ui::ReportReason> _chooseForReportReason; std::optional<Data::ReportInput> _chooseForReportReason;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient; const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
QPainterPath _highlightPathCache; QPainterPath _highlightPathCache;

View file

@ -2134,7 +2134,8 @@ void HistoryWidget::showHistory(
if (_chooseForReport) { if (_chooseForReport) {
clearSelected(); clearSelected();
_chooseForReport->active = true; _chooseForReport->active = true;
_list->setChooseReportReason(_chooseForReport->reason); _list->setChooseReportReason(
_chooseForReport->reportInput);
updateControlsVisibility(); updateControlsVisibility();
updateControlsGeometry(); updateControlsGeometry();
updateTopBarChooseForReport(); updateTopBarChooseForReport();
@ -2412,7 +2413,7 @@ void HistoryWidget::showHistory(
}, _list->lifetime()); }, _list->lifetime());
if (_chooseForReport && _chooseForReport->active) { if (_chooseForReport && _chooseForReport->active) {
_list->setChooseReportReason(_chooseForReport->reason); _list->setChooseReportReason(_chooseForReport->reportInput);
} }
updateTopBarChooseForReport(); updateTopBarChooseForReport();
@ -4320,21 +4321,14 @@ void HistoryWidget::reportSelectedMessages() {
return; return;
} }
const auto ids = _list->getSelectedItems(); const auto ids = _list->getSelectedItems();
const auto peer = _peer; const auto done = _chooseForReport->callback;
const auto reason = _chooseForReport->reason; clearSelected();
const auto weak = Ui::MakeWeak(_list.data()); controller()->clearChooseReportMessages();
controller()->window().show(Box([=](not_null<Ui::GenericBox*> box) { if (done) {
const auto &st = st::defaultReportBox; done(ranges::views::all(
Ui::ReportDetailsBox(box, st, [=](const QString &text) { ids
if (weak) { ) | ranges::views::transform(&FullMsgId::msg) | ranges::to_vector);
clearSelected(); }
controller()->clearChooseReportMessages();
}
const auto show = controller()->uiShow();
Api::SendReport(show, peer, reason, text, ids);
box->closeBox();
});
}));
} }
History *HistoryWidget::history() const { History *HistoryWidget::history() const {
@ -7287,8 +7281,8 @@ void HistoryWidget::checkMessagesTTL() {
} }
void HistoryWidget::setChooseReportMessagesDetails( void HistoryWidget::setChooseReportMessagesDetails(
Ui::ReportReason reason, Data::ReportInput reportInput,
Fn<void(MessageIdsList)> callback) { Fn<void(std::vector<MsgId>)> callback) {
if (!callback) { if (!callback) {
const auto refresh = _chooseForReport && _chooseForReport->active; const auto refresh = _chooseForReport && _chooseForReport->active;
_chooseForReport = nullptr; _chooseForReport = nullptr;
@ -7304,7 +7298,7 @@ void HistoryWidget::setChooseReportMessagesDetails(
} else { } else {
_chooseForReport = std::make_unique<ChooseMessagesForReport>( _chooseForReport = std::make_unique<ChooseMessagesForReport>(
ChooseMessagesForReport{ ChooseMessagesForReport{
.reason = reason, .reportInput = reportInput,
.callback = std::move(callback) }); .callback = std::move(callback) });
} }
} }
@ -8198,7 +8192,7 @@ MessageIdsList HistoryWidget::getSelectedItems() const {
void HistoryWidget::updateTopBarChooseForReport() { void HistoryWidget::updateTopBarChooseForReport() {
if (_chooseForReport && _chooseForReport->active) { if (_chooseForReport && _chooseForReport->active) {
_topBar->showChooseMessagesForReport( _topBar->showChooseMessagesForReport(
_chooseForReport->reason); _chooseForReport->reportInput);
} else { } else {
_topBar->clearChooseMessagesForReport(); _topBar->clearChooseMessagesForReport();
} }

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_view_top_toast.h" #include "history/history_view_top_toast.h"
#include "history/history.h" #include "history/history.h"
#include "chat_helpers/field_characters_count_manager.h" #include "chat_helpers/field_characters_count_manager.h"
#include "data/data_report.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "ui/widgets/fields/input_field.h" #include "ui/widgets/fields/input_field.h"
#include "mtproto/sender.h" #include "mtproto/sender.h"
@ -69,7 +70,6 @@ struct PreparedList;
class SendFilesWay; class SendFilesWay;
class SendAsButton; class SendAsButton;
class SpoilerAnimation; class SpoilerAnimation;
enum class ReportReason;
class ChooseThemeController; class ChooseThemeController;
class ContinuousScroll; class ContinuousScroll;
struct ChatPaintHighlight; struct ChatPaintHighlight;
@ -238,8 +238,8 @@ public:
const TextWithEntities &highlightPart = {}, const TextWithEntities &highlightPart = {},
int highlightPartOffsetHint = 0); int highlightPartOffsetHint = 0);
void setChooseReportMessagesDetails( void setChooseReportMessagesDetails(
Ui::ReportReason reason, Data::ReportInput reportInput,
Fn<void(MessageIdsList)> callback); Fn<void(std::vector<MsgId>)> callback);
void clearAllLoadRequests(); void clearAllLoadRequests();
void clearSupportPreloadRequest(); void clearSupportPreloadRequest();
void clearDelayedShowAtRequest(); void clearDelayedShowAtRequest();
@ -328,8 +328,8 @@ private:
int value; int value;
}; };
struct ChooseMessagesForReport { struct ChooseMessagesForReport {
Ui::ReportReason reason = {}; Data::ReportInput reportInput;
Fn<void(MessageIdsList)> callback; Fn<void(std::vector<MsgId>)> callback;
bool active = false; bool active = false;
}; };
struct ItemRevealAnimation { struct ItemRevealAnimation {

View file

@ -930,11 +930,15 @@ void AddReportAction(
const auto callback = crl::guard(controller, [=] { const auto callback = crl::guard(controller, [=] {
if (const auto item = owner->message(itemId)) { if (const auto item = owner->message(itemId)) {
const auto group = owner->groups().find(item); const auto group = owner->groups().find(item);
controller->show(ReportItemsBox( const auto ids = group
item->history()->peer, ? (ranges::views::all(
(group group->items
? owner->itemsToIds(group->items) ) | ranges::views::transform([](const auto &i) {
: MessageIdsList{ 1, itemId }))); return i->fullId().msg;
}) | ranges::to_vector)
: std::vector<MsgId>{ 1, itemId.msg };
const auto peer = item->history()->peer;
ShowReportMessageBox(controller->uiShow(), peer, ids, {});
} }
}); });
menu->addAction( menu->addAction(

View file

@ -289,8 +289,8 @@ void TopBarWidget::groupCall() {
} }
} }
void TopBarWidget::showChooseMessagesForReport(Ui::ReportReason reason) { void TopBarWidget::showChooseMessagesForReport(Data::ReportInput input) {
setChooseForReportReason(reason); setChooseForReportReason(input);
} }
void TopBarWidget::clearChooseMessagesForReport() { void TopBarWidget::clearChooseMessagesForReport() {
@ -302,12 +302,12 @@ rpl::producer<> TopBarWidget::searchRequest() const {
} }
void TopBarWidget::setChooseForReportReason( void TopBarWidget::setChooseForReportReason(
std::optional<Ui::ReportReason> reason) { std::optional<Data::ReportInput> reportInput) {
if (_chooseForReportReason == reason) { if (_chooseForReportReason == reportInput) {
return; return;
} }
const auto wasNoReason = !_chooseForReportReason; const auto wasNoReason = !_chooseForReportReason;
_chooseForReportReason = reason; _chooseForReportReason = reportInput;
const auto nowNoReason = !_chooseForReportReason; const auto nowNoReason = !_chooseForReportReason;
updateControlsVisibility(); updateControlsVisibility();
updateControlsGeometry(); updateControlsGeometry();
@ -458,21 +458,7 @@ void TopBarWidget::paintTopBar(Painter &p) {
- st::topBarNameRightPadding; - st::topBarNameRightPadding;
if (_chooseForReportReason) { if (_chooseForReportReason) {
const auto text = [&] { const auto text = _chooseForReportReason->optionText;
using Reason = Ui::ReportReason;
switch (*_chooseForReportReason) {
case Reason::Spam: return tr::lng_report_reason_spam(tr::now);
case Reason::Violence:
return tr::lng_report_reason_violence(tr::now);
case Reason::ChildAbuse:
return tr::lng_report_reason_child_abuse(tr::now);
case Reason::Pornography:
return tr::lng_report_reason_pornography(tr::now);
case Reason::Copyright:
return tr::lng_report_reason_copyright(tr::now);
}
Unexpected("reason in TopBarWidget::paintTopBar.");
}();
p.setPen(st::dialogsNameFg); p.setPen(st::dialogsNameFg);
p.setFont(st::semiboldFont); p.setFont(st::semiboldFont);
p.drawTextLeft(nameleft, nametop, width(), text); p.drawTextLeft(nameleft, nametop, width(), text);

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "base/timer.h" #include "base/timer.h"
#include "base/object_ptr.h" #include "base/object_ptr.h"
#include "data/data_report.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
namespace Main { namespace Main {
@ -27,7 +28,6 @@ class UnreadBadge;
class InputField; class InputField;
class CrossButton; class CrossButton;
class InfiniteRadialAnimation; class InfiniteRadialAnimation;
enum class ReportReason;
template <typename Widget> template <typename Widget>
class FadeWrapScaled; class FadeWrapScaled;
} // namespace Ui } // namespace Ui
@ -75,7 +75,7 @@ public:
SendActionPainter *sendAction); SendActionPainter *sendAction);
void setCustomTitle(const QString &title); void setCustomTitle(const QString &title);
void showChooseMessagesForReport(Ui::ReportReason reason); void showChooseMessagesForReport(Data::ReportInput reportInput);
void clearChooseMessagesForReport(); void clearChooseMessagesForReport();
bool toggleSearch(bool shown, anim::type animated); bool toggleSearch(bool shown, anim::type animated);
@ -183,7 +183,7 @@ private:
void refreshUnreadBadge(); void refreshUnreadBadge();
void updateUnreadBadge(); void updateUnreadBadge();
void setChooseForReportReason(std::optional<Ui::ReportReason> reason); void setChooseForReportReason(std::optional<Data::ReportInput>);
void toggleSelectedControls(bool shown); void toggleSelectedControls(bool shown);
[[nodiscard]] bool showSelectedActions() const; [[nodiscard]] bool showSelectedActions() const;
@ -247,7 +247,7 @@ private:
std::unique_ptr<Ui::InfiniteRadialAnimation> _connecting; std::unique_ptr<Ui::InfiniteRadialAnimation> _connecting;
SendActionPainter *_sendAction = nullptr; SendActionPainter *_sendAction = nullptr;
std::optional<Ui::ReportReason> _chooseForReportReason; std::optional<Data::ReportInput> _chooseForReportReason;
base::Timer _onlineUpdater; base::Timer _onlineUpdater;

View file

@ -36,7 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_item_helpers.h" #include "history/history_item_helpers.h"
#include "history/view/history_view_context_menu.h" // HistoryView::ShowReportPeerBox
#include "history/view/history_view_item_preview.h" #include "history/view/history_view_item_preview.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "info/info_memento.h" #include "info/info_memento.h"
@ -1973,7 +1972,7 @@ void ActionsFiller::addReportAction() {
const auto peer = _peer; const auto peer = _peer;
const auto controller = _controller->parentController(); const auto controller = _controller->parentController();
const auto report = [=] { const auto report = [=] {
ShowReportPeerBox(controller, peer); ShowReportMessageBox(controller->uiShow(), peer, {}, {});
}; };
AddActionButton( AddActionButton(
_wrap, _wrap,

View file

@ -1218,9 +1218,9 @@ void MainWidget::setInnerFocus() {
void MainWidget::showChooseReportMessages( void MainWidget::showChooseReportMessages(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::ReportReason reason, Data::ReportInput reportInput,
Fn<void(MessageIdsList)> done) { Fn<void(std::vector<MsgId>)> done) {
_history->setChooseReportMessagesDetails(reason, std::move(done)); _history->setChooseReportMessagesDetails(reportInput, std::move(done));
_controller->showPeerHistory( _controller->showPeerHistory(
peer, peer,
SectionShow::Way::Forward, SectionShow::Way::Forward,

View file

@ -32,6 +32,7 @@ class Thread;
class WallPaper; class WallPaper;
struct ForwardDraft; struct ForwardDraft;
class Forum; class Forum;
struct ReportInput;
} // namespace Data } // namespace Data
namespace Dialogs { namespace Dialogs {
@ -61,7 +62,6 @@ namespace Ui {
class ChatTheme; class ChatTheme;
class ResizeArea; class ResizeArea;
class PlainShadow; class PlainShadow;
enum class ReportReason;
template <typename Widget> template <typename Widget>
class SlideWrap; class SlideWrap;
} // namespace Ui } // namespace Ui
@ -179,8 +179,8 @@ public:
void showChooseReportMessages( void showChooseReportMessages(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::ReportReason reason, Data::ReportInput reportInput,
Fn<void(MessageIdsList)> done); Fn<void(std::vector<MsgId>)> done);
void clearChooseReportMessages(); void clearChooseReportMessages();
void toggleChooseChatTheme( void toggleChooseChatTheme(

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "ui/boxes/report_box.h" // AddReportOptionButton.
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/rect.h" #include "ui/rect.h"
@ -238,45 +239,9 @@ void ShowReportSponsoredBox(
box->setTitle(rpl::single(result.title)); box->setTitle(rpl::single(result.title));
for (const auto &option : result.options) { for (const auto &option : result.options) {
const auto button = box->verticalLayout()->add( const auto button = Ui::AddReportOptionButton(
object_ptr<Ui::SettingsButton>( box->verticalLayout(),
box, option.text);
rpl::single(QString()),
st::settingsButtonNoIcon));
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button,
rpl::single(option.text),
st::sponsoredReportLabel);
const auto icon = Ui::CreateChild<Ui::RpWidget>(
button);
icon->resize(st::settingsPremiumArrow.size());
icon->paintRequest(
) | rpl::start_with_next([=, w = icon->width()] {
auto p = Painter(icon);
st::settingsPremiumArrow.paint(p, 0, 0, w);
}, icon->lifetime());
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
const auto left = button->st().padding.left();
const auto right = button->st().padding.right();
icon->moveToRight(
right,
(size.height() - icon->height()) / 2);
label->resizeToWidth(size.width()
- icon->width()
- left
- st::settingsButtonRightSkip
- right);
label->moveToLeft(
left,
(size.height() - label->height()) / 2);
button->resize(
button->width(),
rect::m::sum::v(button->st().padding)
+ label->height());
}, button->lifetime());
label->setAttribute(Qt::WA_TransparentForMouseEvents);
icon->setAttribute(Qt::WA_TransparentForMouseEvents);
button->setClickedCallback([=] { button->setClickedCallback([=] {
repeatRequest(repeatRequest, option.id); repeatRequest(repeatRequest, option.id);
}); });

View file

@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_icon.h" #include "lottie/lottie_icon.h"
#include "settings/settings_common.h" #include "settings/settings_common.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/rect.h" #include "ui/rect.h"
#include "ui/vertical_list.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h" #include "ui/widgets/fields/input_field.h"
@ -20,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_info.h" #include "styles/style_info.h"
#include "styles/style_channel_earn.h"
#include "styles/style_settings.h" #include "styles/style_settings.h"
namespace Ui { namespace Ui {
@ -124,19 +127,10 @@ void ReportDetailsBox(
const style::ReportBox &st, const style::ReportBox &st,
Fn<void(QString)> done) { Fn<void(QString)> done) {
box->setTitle(tr::lng_profile_report()); box->setTitle(tr::lng_profile_report());
{ AddReportDetailsIconButton(box);
auto icon = Settings::CreateLottieIcon( Ui::AddSkip(
box->verticalLayout(), box->verticalLayout(),
{ st::settingsBlockedListIconPadding.bottom());
.name = u"blocked_peers_empty"_q,
.sizeOverride = Size(st::changePhoneIconSize),
},
st::settingsBlockedListIconPadding);
box->setShowFinishedCallback([animate = std::move(icon.animate)] {
animate(anim::repeat::once);
});
box->addRow(std::move(icon.widget));
}
box->addRow( box->addRow(
object_ptr<FlatLabel>( object_ptr<FlatLabel>(
@ -170,4 +164,55 @@ void ReportDetailsBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
} }
not_null<Ui::AbstractButton*> AddReportOptionButton(
not_null<Ui::VerticalLayout*> container,
const QString &text) {
const auto button = container->add(
object_ptr<Ui::SettingsButton>(
container,
rpl::single(QString()),
st::settingsButtonNoIcon));
const auto label = Ui::CreateChild<Ui::FlatLabel>(
button,
rpl::single(text),
st::sponsoredReportLabel);
const auto icon = Ui::CreateChild<Ui::RpWidget>(button);
icon->resize(st::settingsPremiumArrow.size());
icon->paintRequest() | rpl::start_with_next([=, w = icon->width()] {
auto p = Painter(icon);
st::settingsPremiumArrow.paint(p, 0, 0, w);
}, icon->lifetime());
button->sizeValue() | rpl::start_with_next([=](const QSize &size) {
const auto left = button->st().padding.left();
const auto right = button->st().padding.right();
icon->moveToRight(right, (size.height() - icon->height()) / 2);
label->resizeToWidth(size.width()
- icon->width()
- left
- st::settingsButtonRightSkip
- right);
label->moveToLeft(left, (size.height() - label->height()) / 2);
button->resize(
button->width(),
rect::m::sum::v(button->st().padding) + label->height());
}, button->lifetime());
label->setAttribute(Qt::WA_TransparentForMouseEvents);
icon->setAttribute(Qt::WA_TransparentForMouseEvents);
return button;
}
void AddReportDetailsIconButton(not_null<GenericBox*> box) {
auto icon = Settings::CreateLottieIcon(
box->verticalLayout(),
{
.name = u"blocked_peers_empty"_q,
.sizeOverride = Size(st::changePhoneIconSize),
},
{});
box->setShowFinishedCallback([animate = std::move(icon.animate)] {
animate(anim::repeat::once);
});
box->addRow(std::move(icon.widget));
}
} // namespace Ui } // namespace Ui

View file

@ -13,7 +13,9 @@ struct ReportBox;
namespace Ui { namespace Ui {
class AbstractButton;
class GenericBox; class GenericBox;
class VerticalLayout;
enum class ReportSource { enum class ReportSource {
Message, Message,
@ -52,4 +54,10 @@ void ReportDetailsBox(
const style::ReportBox &st, const style::ReportBox &st,
Fn<void(QString)> done); Fn<void(QString)> done);
[[nodiscard]] not_null<Ui::AbstractButton*> AddReportOptionButton(
not_null<Ui::VerticalLayout*> container,
const QString &text);
void AddReportDetailsIconButton(not_null<GenericBox*> box);
} // namespace Ui } // namespace Ui

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "window/window_peer_menu.h" #include "window/window_peer_menu.h"
#include "api/api_report.h"
#include "menu/menu_check_item.h" #include "menu/menu_check_item.h"
#include "boxes/share_box.h" #include "boxes/share_box.h"
#include "chat_helpers/compose/compose_show.h" #include "chat_helpers/compose/compose_show.h"
@ -913,7 +914,7 @@ void Filler::addReport() {
const auto peer = _peer; const auto peer = _peer;
const auto navigation = _controller; const auto navigation = _controller;
_addAction(tr::lng_profile_report(tr::now), [=] { _addAction(tr::lng_profile_report(tr::now), [=] {
ShowReportPeerBox(navigation, peer); ShowReportMessageBox(navigation->uiShow(), peer, {}, {});
}, &st::menuIconReport); }, &st::menuIconReport);
} }

View file

@ -2412,9 +2412,9 @@ void SessionController::clearPassportForm() {
void SessionController::showChooseReportMessages( void SessionController::showChooseReportMessages(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::ReportReason reason, Data::ReportInput reportInput,
Fn<void(MessageIdsList)> done) const { Fn<void(std::vector<MsgId>)> done) const {
content()->showChooseReportMessages(peer, reason, std::move(done)); content()->showChooseReportMessages(peer, reportInput, std::move(done));
} }
void SessionController::clearChooseReportMessages() const { void SessionController::clearChooseReportMessages() const {

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/gift_premium_box.h" // GiftPremiumValidator. #include "boxes/gift_premium_box.h" // GiftPremiumValidator.
#include "chat_helpers/compose/compose_show.h" #include "chat_helpers/compose/compose_show.h"
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.h"
#include "data/data_report.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "settings/settings_type.h" #include "settings/settings_type.h"
#include "window/window_adaptive.h" #include "window/window_adaptive.h"
@ -55,7 +56,6 @@ class FormController;
namespace Ui { namespace Ui {
class LayerWidget; class LayerWidget;
enum class ReportReason;
class ChatStyle; class ChatStyle;
class ChatTheme; class ChatTheme;
struct ChatThemeKey; struct ChatThemeKey;
@ -509,8 +509,8 @@ public:
void showChooseReportMessages( void showChooseReportMessages(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::ReportReason reason, Data::ReportInput reportInput,
Fn<void(MessageIdsList)> done) const; Fn<void(std::vector<MsgId>)> done) const;
void clearChooseReportMessages() const; void clearChooseReportMessages() const;
void showInNewWindow( void showInNewWindow(