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_reply_preview.cpp
data/data_reply_preview.h
data/data_report.h
data/data_saved_messages.cpp
data/data_saved_messages.h
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_details_about" = "Please enter any additional details relevant to your report.";
"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_fake" = "Fake Account";
"lng_report_reason_violence" = "Violence";

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_report.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
@ -40,15 +41,11 @@ MTPreportReason ReasonToTL(const Ui::ReportReason &reason) {
} // namespace
void SendReport(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
Ui::ReportReason reason,
const QString &comment,
std::variant<
v::null_t,
MessageIdsList,
not_null<PhotoData*>,
StoryId> data) {
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
Ui::ReportReason reason,
const QString &comment,
std::variant<v::null_t, not_null<PhotoData*>> data) {
auto done = [=] {
show->showToast(tr::lng_report_thanks(tr::now));
};
@ -58,18 +55,6 @@ void SendReport(
ReasonToTL(reason),
MTP_string(comment)
)).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) {
peer->session().api().request(MTPaccount_ReportProfilePhoto(
peer->input,
@ -77,14 +62,93 @@ void SendReport(
ReasonToTL(reason),
MTP_string(comment)
)).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

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class HistoryItem;
class PeerData;
class PhotoData;
@ -15,17 +16,41 @@ class Show;
enum class ReportReason;
} // namespace Ui
namespace Data {
struct ReportInput;
} // namespace Data
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(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
Ui::ReportReason reason,
const QString &comment,
std::variant<
v::null_t,
MessageIdsList,
not_null<PhotoData*>,
StoryId> data);
std::variant<v::null_t, not_null<PhotoData*>> data);
[[nodiscard]] auto CreateReportMessagesOrStoriesCallback(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer)
-> Fn<void(Data::ReportInput, Fn<void(ReportResult)>)>;
} // namespace Api

View file

@ -8,23 +8,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/report_messages_box.h"
#include "api/api_report.h"
#include "core/application.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "lang/lang_keys.h"
#include "ui/boxes/report_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 "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_settings.h"
namespace {
[[nodiscard]] object_ptr<Ui::BoxContent> Report(
not_null<PeerData*> peer,
std::variant<
v::null_t,
MessageIdsList,
not_null<PhotoData*>,
StoryId> data,
std::variant<v::null_t, not_null<PhotoData*>> data,
const style::ReportBox *stOverride) {
const auto source = v::match(data, [](const MessageIdsList &ids) {
return Ui::ReportSource::Message;
@ -62,64 +66,125 @@ namespace {
} // namespace
object_ptr<Ui::BoxContent> ReportItemsBox(
not_null<PeerData*> peer,
MessageIdsList ids) {
return Report(peer, ids, nullptr);
}
object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
not_null<PeerData*> peer,
not_null<PhotoData*> photo) {
return Report(peer, photo, nullptr);
}
void ShowReportPeerBox(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer) {
struct State {
QPointer<Ui::BoxContent> reasonBox;
QPointer<Ui::BoxContent> detailsBox;
MessageIdsList ids;
};
const auto state = std::make_shared<State>();
const auto chosen = [=](Ui::ReportReason reason) {
const auto send = [=](const QString &text) {
window->clearChooseReportMessages();
Api::SendReport(
window->uiShow(),
peer,
reason,
text,
std::move(state->ids));
if (const auto strong = state->reasonBox.data()) {
strong->closeBox();
void ShowReportMessageBox(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
const std::vector<MsgId> &ids,
const std::vector<StoryId> &stories) {
const auto report = Api::CreateReportMessagesOrStoriesCallback(
show,
peer);
auto performRequest = [=](
const auto &repeatRequest,
Data::ReportInput reportInput) -> void {
constexpr auto kToastDuration = crl::time(4000);
report(reportInput, [=](const Api::ReportResult &result) {
if (!result.error.isEmpty()) {
if (result.error == u"MESSAGE_ID_REQUIRED"_q) {
const auto widget = show->toastParent();
const auto window = Core::App().findWindow(widget);
const auto controller = window
? 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()) {
strong->closeBox();
if (!result.options.empty() || result.commentOption) {
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(
Ui::ReportReasonBox,
st::defaultReportBox,
(peer->isBroadcast()
? Ui::ReportSource::Channel
: peer->isUser()
? Ui::ReportSource::Bot
: Ui::ReportSource::Group),
chosen));
performRequest(performRequest, { .ids = ids, .stories = stories });
}

View file

@ -12,20 +12,17 @@ class object_ptr;
namespace Ui {
class BoxContent;
class Show;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Main
class PeerData;
[[nodiscard]] object_ptr<Ui::BoxContent> ReportItemsBox(
not_null<PeerData*> peer,
MessageIdsList ids);
[[nodiscard]] object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
not_null<PeerData*> peer,
not_null<PhotoData*> photo);
void ShowReportPeerBox(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer);
void ShowReportMessageBox(
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 "api/api_report.h"
#include "base/unixtime.h"
#include "boxes/report_messages_box.h"
#include "apiwrap.h"
#include "core/application.h"
#include "data/data_changes.h"
@ -1912,7 +1912,7 @@ void Stories::report(
QString text) {
if (const auto maybeStory = lookup(id)) {
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 "history/history_item_helpers.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 "boxes/moderate_messages_box.h"
#include "history/view/media/history_view_sticker.h"
@ -4215,8 +4216,8 @@ int HistoryInner::historyDrawTop() const {
return (top >= 0) ? (top + _historySkipHeight) : -1;
}
void HistoryInner::setChooseReportReason(Ui::ReportReason reason) {
_chooseForReportReason = reason;
void HistoryInner::setChooseReportReason(Data::ReportInput reportInput) {
_chooseForReportReason = reportInput;
}
void HistoryInner::clearChooseReportReason() {
@ -4501,17 +4502,21 @@ void HistoryInner::deleteAsGroup(FullMsgId itemId) {
}
void HistoryInner::reportItem(FullMsgId itemId) {
_controller->show(ReportItemsBox(_peer, { 1, itemId }));
ShowReportMessageBox(_controller->uiShow(), _peer, { itemId.msg }, {});
}
void HistoryInner::reportAsGroup(FullMsgId itemId) {
if (const auto item = session().data().message(itemId)) {
const auto group = session().data().groups().find(item);
_controller->show(ReportItemsBox(
_peer,
(group
? session().data().itemsToIds(group->items)
: MessageIdsList{ 1, itemId })));
const auto ids = group
? (ranges::views::all(
group->items
) | ranges::views::transform([](const auto &i) {
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
#include "base/timer.h"
#include "data/data_report.h"
#include "ui/rp_widget.h"
#include "ui/effects/animations.h"
#include "ui/dragging_scroll_manager.h"
@ -54,7 +55,6 @@ namespace Ui {
class ChatTheme;
class ChatStyle;
class PopupMenu;
enum class ReportReason;
struct ChatPaintContext;
class PathShiftGradient;
struct PeerUserpicView;
@ -189,7 +189,7 @@ public:
int historyTop() const;
int historyDrawTop() const;
void setChooseReportReason(Ui::ReportReason reason);
void setChooseReportReason(Data::ReportInput reportInput);
void clearChooseReportReason();
// -1 if should not be visible, -2 if bad history()
@ -468,7 +468,7 @@ private:
style::cursor _cursor = style::cur_default;
SelectedItems _selected;
std::optional<Ui::ReportReason> _chooseForReportReason;
std::optional<Data::ReportInput> _chooseForReportReason;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
QPainterPath _highlightPathCache;

View file

@ -2134,7 +2134,8 @@ void HistoryWidget::showHistory(
if (_chooseForReport) {
clearSelected();
_chooseForReport->active = true;
_list->setChooseReportReason(_chooseForReport->reason);
_list->setChooseReportReason(
_chooseForReport->reportInput);
updateControlsVisibility();
updateControlsGeometry();
updateTopBarChooseForReport();
@ -2412,7 +2413,7 @@ void HistoryWidget::showHistory(
}, _list->lifetime());
if (_chooseForReport && _chooseForReport->active) {
_list->setChooseReportReason(_chooseForReport->reason);
_list->setChooseReportReason(_chooseForReport->reportInput);
}
updateTopBarChooseForReport();
@ -4320,21 +4321,14 @@ void HistoryWidget::reportSelectedMessages() {
return;
}
const auto ids = _list->getSelectedItems();
const auto peer = _peer;
const auto reason = _chooseForReport->reason;
const auto weak = Ui::MakeWeak(_list.data());
controller()->window().show(Box([=](not_null<Ui::GenericBox*> box) {
const auto &st = st::defaultReportBox;
Ui::ReportDetailsBox(box, st, [=](const QString &text) {
if (weak) {
clearSelected();
controller()->clearChooseReportMessages();
}
const auto show = controller()->uiShow();
Api::SendReport(show, peer, reason, text, ids);
box->closeBox();
});
}));
const auto done = _chooseForReport->callback;
clearSelected();
controller()->clearChooseReportMessages();
if (done) {
done(ranges::views::all(
ids
) | ranges::views::transform(&FullMsgId::msg) | ranges::to_vector);
}
}
History *HistoryWidget::history() const {
@ -7287,8 +7281,8 @@ void HistoryWidget::checkMessagesTTL() {
}
void HistoryWidget::setChooseReportMessagesDetails(
Ui::ReportReason reason,
Fn<void(MessageIdsList)> callback) {
Data::ReportInput reportInput,
Fn<void(std::vector<MsgId>)> callback) {
if (!callback) {
const auto refresh = _chooseForReport && _chooseForReport->active;
_chooseForReport = nullptr;
@ -7304,7 +7298,7 @@ void HistoryWidget::setChooseReportMessagesDetails(
} else {
_chooseForReport = std::make_unique<ChooseMessagesForReport>(
ChooseMessagesForReport{
.reason = reason,
.reportInput = reportInput,
.callback = std::move(callback) });
}
}
@ -8198,7 +8192,7 @@ MessageIdsList HistoryWidget::getSelectedItems() const {
void HistoryWidget::updateTopBarChooseForReport() {
if (_chooseForReport && _chooseForReport->active) {
_topBar->showChooseMessagesForReport(
_chooseForReport->reason);
_chooseForReport->reportInput);
} else {
_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.h"
#include "chat_helpers/field_characters_count_manager.h"
#include "data/data_report.h"
#include "window/section_widget.h"
#include "ui/widgets/fields/input_field.h"
#include "mtproto/sender.h"
@ -69,7 +70,6 @@ struct PreparedList;
class SendFilesWay;
class SendAsButton;
class SpoilerAnimation;
enum class ReportReason;
class ChooseThemeController;
class ContinuousScroll;
struct ChatPaintHighlight;
@ -238,8 +238,8 @@ public:
const TextWithEntities &highlightPart = {},
int highlightPartOffsetHint = 0);
void setChooseReportMessagesDetails(
Ui::ReportReason reason,
Fn<void(MessageIdsList)> callback);
Data::ReportInput reportInput,
Fn<void(std::vector<MsgId>)> callback);
void clearAllLoadRequests();
void clearSupportPreloadRequest();
void clearDelayedShowAtRequest();
@ -328,8 +328,8 @@ private:
int value;
};
struct ChooseMessagesForReport {
Ui::ReportReason reason = {};
Fn<void(MessageIdsList)> callback;
Data::ReportInput reportInput;
Fn<void(std::vector<MsgId>)> callback;
bool active = false;
};
struct ItemRevealAnimation {

View file

@ -930,11 +930,15 @@ void AddReportAction(
const auto callback = crl::guard(controller, [=] {
if (const auto item = owner->message(itemId)) {
const auto group = owner->groups().find(item);
controller->show(ReportItemsBox(
item->history()->peer,
(group
? owner->itemsToIds(group->items)
: MessageIdsList{ 1, itemId })));
const auto ids = group
? (ranges::views::all(
group->items
) | ranges::views::transform([](const auto &i) {
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(

View file

@ -289,8 +289,8 @@ void TopBarWidget::groupCall() {
}
}
void TopBarWidget::showChooseMessagesForReport(Ui::ReportReason reason) {
setChooseForReportReason(reason);
void TopBarWidget::showChooseMessagesForReport(Data::ReportInput input) {
setChooseForReportReason(input);
}
void TopBarWidget::clearChooseMessagesForReport() {
@ -302,12 +302,12 @@ rpl::producer<> TopBarWidget::searchRequest() const {
}
void TopBarWidget::setChooseForReportReason(
std::optional<Ui::ReportReason> reason) {
if (_chooseForReportReason == reason) {
std::optional<Data::ReportInput> reportInput) {
if (_chooseForReportReason == reportInput) {
return;
}
const auto wasNoReason = !_chooseForReportReason;
_chooseForReportReason = reason;
_chooseForReportReason = reportInput;
const auto nowNoReason = !_chooseForReportReason;
updateControlsVisibility();
updateControlsGeometry();
@ -458,21 +458,7 @@ void TopBarWidget::paintTopBar(Painter &p) {
- st::topBarNameRightPadding;
if (_chooseForReportReason) {
const auto text = [&] {
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.");
}();
const auto text = _chooseForReportReason->optionText;
p.setPen(st::dialogsNameFg);
p.setFont(st::semiboldFont);
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 "base/timer.h"
#include "base/object_ptr.h"
#include "data/data_report.h"
#include "dialogs/dialogs_key.h"
namespace Main {
@ -27,7 +28,6 @@ class UnreadBadge;
class InputField;
class CrossButton;
class InfiniteRadialAnimation;
enum class ReportReason;
template <typename Widget>
class FadeWrapScaled;
} // namespace Ui
@ -75,7 +75,7 @@ public:
SendActionPainter *sendAction);
void setCustomTitle(const QString &title);
void showChooseMessagesForReport(Ui::ReportReason reason);
void showChooseMessagesForReport(Data::ReportInput reportInput);
void clearChooseMessagesForReport();
bool toggleSearch(bool shown, anim::type animated);
@ -183,7 +183,7 @@ private:
void refreshUnreadBadge();
void updateUnreadBadge();
void setChooseForReportReason(std::optional<Ui::ReportReason> reason);
void setChooseForReportReason(std::optional<Data::ReportInput>);
void toggleSelectedControls(bool shown);
[[nodiscard]] bool showSelectedActions() const;
@ -247,7 +247,7 @@ private:
std::unique_ptr<Ui::InfiniteRadialAnimation> _connecting;
SendActionPainter *_sendAction = nullptr;
std::optional<Ui::ReportReason> _chooseForReportReason;
std::optional<Data::ReportInput> _chooseForReportReason;
base::Timer _onlineUpdater;

View file

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

View file

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

View file

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

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/report_box.h" // AddReportOptionButton.
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/rect.h"
@ -238,45 +239,9 @@ void ShowReportSponsoredBox(
box->setTitle(rpl::single(result.title));
for (const auto &option : result.options) {
const auto button = box->verticalLayout()->add(
object_ptr<Ui::SettingsButton>(
box,
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);
const auto button = Ui::AddReportOptionButton(
box->verticalLayout(),
option.text);
button->setClickedCallback([=] {
repeatRequest(repeatRequest, option.id);
});

View file

@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_icon.h"
#include "settings/settings_common.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/vertical_list.h"
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.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_layers.h"
#include "styles/style_info.h"
#include "styles/style_channel_earn.h"
#include "styles/style_settings.h"
namespace Ui {
@ -124,19 +127,10 @@ void ReportDetailsBox(
const style::ReportBox &st,
Fn<void(QString)> done) {
box->setTitle(tr::lng_profile_report());
{
auto icon = Settings::CreateLottieIcon(
box->verticalLayout(),
{
.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));
}
AddReportDetailsIconButton(box);
Ui::AddSkip(
box->verticalLayout(),
st::settingsBlockedListIconPadding.bottom());
box->addRow(
object_ptr<FlatLabel>(
@ -170,4 +164,55 @@ void ReportDetailsBox(
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

View file

@ -13,7 +13,9 @@ struct ReportBox;
namespace Ui {
class AbstractButton;
class GenericBox;
class VerticalLayout;
enum class ReportSource {
Message,
@ -52,4 +54,10 @@ void ReportDetailsBox(
const style::ReportBox &st,
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

View file

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

View file

@ -2412,9 +2412,9 @@ void SessionController::clearPassportForm() {
void SessionController::showChooseReportMessages(
not_null<PeerData*> peer,
Ui::ReportReason reason,
Fn<void(MessageIdsList)> done) const {
content()->showChooseReportMessages(peer, reason, std::move(done));
Data::ReportInput reportInput,
Fn<void(std::vector<MsgId>)> done) const {
content()->showChooseReportMessages(peer, reportInput, std::move(done));
}
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 "chat_helpers/compose/compose_show.h"
#include "data/data_chat_participant_status.h"
#include "data/data_report.h"
#include "dialogs/dialogs_key.h"
#include "settings/settings_type.h"
#include "window/window_adaptive.h"
@ -55,7 +56,6 @@ class FormController;
namespace Ui {
class LayerWidget;
enum class ReportReason;
class ChatStyle;
class ChatTheme;
struct ChatThemeKey;
@ -509,8 +509,8 @@ public:
void showChooseReportMessages(
not_null<PeerData*> peer,
Ui::ReportReason reason,
Fn<void(MessageIdsList)> done) const;
Data::ReportInput reportInput,
Fn<void(std::vector<MsgId>)> done) const;
void clearChooseReportMessages() const;
void showInNewWindow(