When reporting peer allow to select messages first.

This commit is contained in:
John Preston 2021-02-15 21:37:22 +04:00
parent 45dcadfff4
commit 467449ac13
25 changed files with 639 additions and 372 deletions

View file

@ -240,8 +240,6 @@ PRIVATE
boxes/photo_crop_box.h
boxes/rate_call_box.cpp
boxes/rate_call_box.h
boxes/report_box.cpp
boxes/report_box.h
boxes/self_destruction_box.cpp
boxes/self_destruction_box.h
boxes/send_files_box.cpp

View file

@ -996,6 +996,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_report_group_title" = "Report group";
"lng_report_bot_title" = "Report bot";
"lng_report_message_title" = "Report message";
"lng_report_select_messages" = "Select messages";
"lng_report_messages_none" = "Select Messages";
"lng_report_messages_count#one" = "Report {count} Message";
"lng_report_messages_count#other" = "Report {count} Messages";
"lng_report_details_about" = "Please enter any additional details relevant to your report.";
"lng_report_details" = "Additional Details";
"lng_report_reason_spam" = "Spam";
"lng_report_reason_fake" = "Fake Account";
"lng_report_reason_violence" = "Violence";

View file

@ -1,229 +0,0 @@
/*
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 "boxes/report_box.h"
#include "lang/lang_keys.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "boxes/confirm_box.h"
#include "history/history_item.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/toast/toast.h"
#include "mainwindow.h"
#include "core/core_settings.h"
#include "core/application.h"
#include "window/window_session_controller.h"
#include "window/window_peer_menu.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
namespace {
constexpr auto kReportReasonLengthMax = 200;
} // namespace
ReportBox::ReportBox(QWidget*, not_null<PeerData*> peer)
: _peer(peer)
, _api(&_peer->session().mtp()) {
}
ReportBox::ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids)
: _peer(peer)
, _api(&_peer->session().mtp())
, _ids(std::move(ids)) {
}
void ReportBox::prepare() {
setTitle([&] {
if (_ids) {
return tr::lng_report_message_title();
} else if (_peer->isUser()) {
return tr::lng_report_bot_title();
} else if (_peer->isMegagroup()) {
return tr::lng_report_group_title();
} else {
return tr::lng_report_title();
}
}());
addButton(tr::lng_report_button(), [=] { report(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
_reasonGroup = std::make_shared<Ui::RadioenumGroup<Reason>>(
Reason::Spam);
const auto createButton = [&](
object_ptr<Ui::Radioenum<Reason>> &button,
Reason reason,
const QString &text) {
button.create(
this,
_reasonGroup,
reason,
text,
st::defaultBoxCheckbox);
};
createButton(_reasonSpam, Reason::Spam, tr::lng_report_reason_spam(tr::now));
createButton(_reasonFake, Reason::Fake, tr::lng_report_reason_fake(tr::now));
createButton(_reasonViolence, Reason::Violence, tr::lng_report_reason_violence(tr::now));
createButton(_reasonChildAbuse, Reason::ChildAbuse, tr::lng_report_reason_child_abuse(tr::now));
createButton(_reasonPornography, Reason::Pornography, tr::lng_report_reason_pornography(tr::now));
createButton(_reasonOther, Reason::Other, tr::lng_report_reason_other(tr::now));
_reasonGroup->setChangedCallback([=](Reason value) {
reasonChanged(value);
});
updateMaxHeight();
}
void ReportBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _reasonSpam->getMargins().top());
_reasonFake->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->bottomNoMargins() + st::boxOptionListSkip);
_reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonFake->bottomNoMargins() + st::boxOptionListSkip);
_reasonChildAbuse->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->bottomNoMargins() + st::boxOptionListSkip);
_reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonChildAbuse->bottomNoMargins() + st::boxOptionListSkip);
_reasonOther->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonPornography->bottomNoMargins() + st::boxOptionListSkip);
if (_reasonOtherText) {
_reasonOtherText->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() - st::defaultInputField.textMargins.left(), _reasonOther->bottomNoMargins() + st::newGroupDescriptionPadding.top());
}
}
void ReportBox::reasonChanged(Reason reason) {
if (reason == Reason::Other) {
if (!_reasonOtherText) {
_reasonOtherText.create(
this,
st::profileReportReasonOther,
Ui::InputField::Mode::MultiLine,
tr::lng_report_reason_description());
_reasonOtherText->show();
_reasonOtherText->setSubmitSettings(Core::App().settings().sendSubmitWay());
_reasonOtherText->setMaxLength(kReportReasonLengthMax);
_reasonOtherText->resize(width() - (st::boxPadding.left() + st::boxOptionListPadding.left() + st::boxPadding.right()), _reasonOtherText->height());
updateMaxHeight();
connect(_reasonOtherText, &Ui::InputField::resized, [=] { reasonResized(); });
connect(_reasonOtherText, &Ui::InputField::submitted, [=] { report(); });
connect(_reasonOtherText, &Ui::InputField::cancelled, [=] { closeBox(); });
}
_reasonOtherText->setFocusFast();
} else if (_reasonOtherText) {
_reasonOtherText.destroy();
updateMaxHeight();
}
}
void ReportBox::setInnerFocus() {
if (_reasonOtherText) {
_reasonOtherText->setFocusFast();
} else {
setFocus();
}
}
void ReportBox::reasonResized() {
updateMaxHeight();
update();
}
void ReportBox::report() {
if (_requestId) {
return;
}
const auto text = _reasonOtherText
? _reasonOtherText->getLastText().trimmed()
: QString();
if (_reasonOtherText && text.isEmpty()) {
_reasonOtherText->showError();
return;
}
const auto reason = [&] {
switch (_reasonGroup->value()) {
case Reason::Spam: return MTP_inputReportReasonSpam();
case Reason::Fake: return MTP_inputReportReasonFake();
case Reason::Violence: return MTP_inputReportReasonViolence();
case Reason::ChildAbuse: return MTP_inputReportReasonChildAbuse();
case Reason::Pornography: return MTP_inputReportReasonPornography();
case Reason::Other: return MTP_inputReportReasonOther();
}
Unexpected("Bad reason group value.");
}();
if (_ids) {
auto ids = QVector<MTPint>();
for (const auto &fullId : *_ids) {
ids.push_back(MTP_int(fullId.msg));
}
_requestId = _api.request(MTPmessages_Report(
_peer->input,
MTP_vector<MTPint>(ids),
reason,
MTP_string(text)
)).done([=](const MTPBool &result) {
reportDone(result);
}).fail([=](const RPCError &error) {
reportFail(error);
}).send();
} else {
_requestId = _api.request(MTPaccount_ReportPeer(
_peer->input,
reason,
MTP_string(text)
)).done([=](const MTPBool &result) {
reportDone(result);
}).fail([=](const RPCError &error) {
reportFail(error);
}).send();
}
}
void ReportBox::reportDone(const MTPBool &result) {
_requestId = 0;
Ui::Toast::Show(tr::lng_report_thanks(tr::now));
closeBox();
}
void ReportBox::reportFail(const RPCError &error) {
_requestId = 0;
if (_reasonOtherText) {
_reasonOtherText->showError();
}
}
void ReportBox::updateMaxHeight() {
const auto buttonsCount = 6;
auto newHeight = st::boxOptionListPadding.top() + _reasonSpam->getMargins().top() + buttonsCount * _reasonSpam->heightNoMargins() + (buttonsCount - 1) * st::boxOptionListSkip + _reasonSpam->getMargins().bottom() + st::boxOptionListPadding.bottom();
if (_reasonOtherText) {
newHeight += st::newGroupDescriptionPadding.top() + _reasonOtherText->height() + st::newGroupDescriptionPadding.bottom();
}
setDimensions(st::boxWidth, newHeight);
}
void BlockSenderFromRepliesBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
FullMsgId id) {
const auto item = controller->session().data().message(id);
Assert(item != nullptr);
PeerMenuBlockUserBox(
box,
&controller->window(),
item->senderOriginal(),
true,
Window::ClearReply{ id });
}

View file

@ -1,74 +0,0 @@
/*
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
#include "boxes/abstract_box.h"
#include "ui/layers/generic_box.h"
#include "mtproto/sender.h"
namespace Window {
class SessionController;
} // namespace Window
namespace Ui {
template <typename Enum>
class RadioenumGroup;
template <typename Enum>
class Radioenum;
class InputField;
} // namespace Ui
class ReportBox final : public Ui::BoxContent {
public:
ReportBox(QWidget*, not_null<PeerData*> peer);
ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids);
protected:
void prepare() override;
void setInnerFocus() override;
void resizeEvent(QResizeEvent *e) override;
private:
enum class Reason {
Spam,
Fake,
Violence,
ChildAbuse,
Pornography,
Other,
};
void reasonChanged(Reason reason);
void reasonResized();
void updateMaxHeight();
void report();
void reportDone(const MTPBool &result);
void reportFail(const RPCError &error);
const not_null<PeerData*> _peer;
MTP::Sender _api;
std::optional<MessageIdsList> _ids;
std::shared_ptr<Ui::RadioenumGroup<Reason>> _reasonGroup;
object_ptr<Ui::Radioenum<Reason>> _reasonSpam = { nullptr };
object_ptr<Ui::Radioenum<Reason>> _reasonFake = { nullptr };
object_ptr<Ui::Radioenum<Reason>> _reasonViolence = { nullptr };
object_ptr<Ui::Radioenum<Reason>> _reasonChildAbuse = { nullptr };
object_ptr<Ui::Radioenum<Reason>> _reasonPornography = { nullptr };
object_ptr<Ui::Radioenum<Reason>> _reasonOther = { nullptr };
object_ptr<Ui::InputField> _reasonOtherText = { nullptr };
mtpRequestId _requestId = 0;
};
void BlockSenderFromRepliesBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
FullMsgId id);

View file

@ -185,8 +185,9 @@ constexpr auto EndClientMsgId = MsgId(-0x40000000);
constexpr auto ShowAtTheEndMsgId = MsgId(-0x40000000);
constexpr auto SwitchAtTopMsgId = MsgId(-0x3FFFFFFF);
constexpr auto ShowAtProfileMsgId = MsgId(-0x3FFFFFFE);
constexpr auto ShowAndStartBotMsgId = MsgId(-0x3FFFFFD);
constexpr auto ShowAtGameShareMsgId = MsgId(-0x3FFFFFC);
constexpr auto ShowAndStartBotMsgId = MsgId(-0x3FFFFFFD);
constexpr auto ShowAtGameShareMsgId = MsgId(-0x3FFFFFFC);
constexpr auto ShowForChooseMessagesMsgId = MsgId(-0x3FFFFFFB);
constexpr auto ServerMaxMsgId = MsgId(0x3FFFFFFF);
constexpr auto ShowAtUnreadMsgId = MsgId(0);
constexpr inline bool IsClientMsgId(MsgId id) {

View file

@ -26,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/toast/toast.h"
#include "ui/text/text_options.h"
#include "ui/boxes/report_box.h"
#include "ui/layers/generic_box.h"
#include "ui/controls/delete_message_context_action.h"
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
@ -35,7 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "window/notifications_manager.h"
#include "boxes/confirm_box.h"
#include "boxes/report_box.h"
#include "boxes/sticker_set_box.h"
#include "chat_helpers/message_field.h"
#include "history/history_widget.h"
@ -1058,15 +1059,13 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
if (ClickHandler::getPressed()) {
_mouseAction = MouseAction::PrepareDrag;
} else if (!_selected.empty()) {
if (_selected.cbegin()->second == FullSelection) {
if (_dragStateItem
&& _selected.find(_dragStateItem) != _selected.cend()
&& App::hoveredItem()) {
_mouseAction = MouseAction::PrepareDrag; // start items drag
} else if (!_pressWasInactive) {
_mouseAction = MouseAction::PrepareSelect; // start items select
}
} else if (inSelectionMode()) {
if (_dragStateItem
&& _selected.find(_dragStateItem) != _selected.cend()
&& App::hoveredItem()) {
_mouseAction = MouseAction::PrepareDrag; // start items drag
} else if (!_pressWasInactive) {
_mouseAction = MouseAction::PrepareSelect; // start items select
}
}
if (_mouseAction == MouseAction::None && mouseActionView) {
@ -1319,7 +1318,10 @@ void HistoryInner::mouseActionFinish(
} else if (_mouseActionItem) {
// if we are in selecting items mode perhaps we want to
// toggle selection instead of activating the pressed link
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
if (_mouseAction == MouseAction::PrepareDrag
&& !_pressWasInactive
&& inSelectionMode()
&& button != Qt::RightButton) {
if (const auto view = _mouseActionItem->mainView()) {
if (const auto media = view->media()) {
if (media->toggleSelectionByHandlerClick(activated)) {
@ -1357,8 +1359,7 @@ void HistoryInner::mouseActionFinish(
}
if ((_mouseAction == MouseAction::PrepareSelect)
&& !_pressWasInactive
&& !_selected.empty()
&& (_selected.cbegin()->second == FullSelection)) {
&& inSelectionMode()) {
changeSelectionAsGroup(
&_selected,
_mouseActionItem,
@ -1375,8 +1376,7 @@ void HistoryInner::mouseActionFinish(
} else if ((i == _selected.cend())
&& !_dragStateItem->serviceMsg()
&& (_dragStateItem->id > 0)
&& !_selected.empty()
&& _selected.cbegin()->second == FullSelection) {
&& inSelectionMode()) {
if (_selected.size() < MaxSelectedItems) {
_selected.emplace(_dragStateItem, FullSelection);
repaintItem(_mouseActionItem);
@ -2519,6 +2519,8 @@ bool HistoryInner::inSelectionMode() const {
&& _dragSelFrom
&& _dragSelTo) {
return true;
} else if (_chooseForReportReason.has_value()) {
return true;
}
return false;
}
@ -2993,6 +2995,14 @@ int HistoryInner::historyDrawTop() const {
return (top >= 0) ? (top + _historySkipHeight) : -1;
}
void HistoryInner::setChooseReportReason(Ui::ReportReason reason) {
_chooseForReportReason = reason;
}
void HistoryInner::clearChooseReportReason() {
_chooseForReportReason = std::nullopt;
}
// -1 if should not be visible, -2 if bad history()
int HistoryInner::itemTop(const HistoryItem *item) const {
if (!item) {
@ -3226,25 +3236,24 @@ void HistoryInner::deleteAsGroup(FullMsgId itemId) {
}
void HistoryInner::reportItem(FullMsgId itemId) {
Ui::show(Box<ReportBox>(_peer, MessageIdsList(1, itemId)));
HistoryView::ShowReportItemsBox(_peer, { 1, itemId });
}
void HistoryInner::reportAsGroup(FullMsgId itemId) {
if (const auto item = session().data().message(itemId)) {
const auto group = session().data().groups().find(item);
if (!group) {
return reportItem(itemId);
}
Ui::show(Box<ReportBox>(
HistoryView::ShowReportItemsBox(
_peer,
session().data().itemsToIds(group->items)));
(group
? session().data().itemsToIds(group->items)
: MessageIdsList{ 1, itemId }));
}
}
void HistoryInner::blockSenderItem(FullMsgId itemId) {
if (const auto item = session().data().message(itemId)) {
Ui::show(Box(
BlockSenderFromRepliesBox,
Window::BlockSenderFromRepliesBox,
_controller,
itemId));
}

View file

@ -35,6 +35,7 @@ class SessionController;
namespace Ui {
class PopupMenu;
enum class ReportReason;
} // namespace Ui
class HistoryWidget;
@ -111,6 +112,9 @@ public:
int historyTop() const;
int historyDrawTop() const;
void setChooseReportReason(Ui::ReportReason reason);
void clearChooseReportReason();
// -1 if should not be visible, -2 if bad history()
int itemTop(const HistoryItem *item) const;
int itemTop(const Element *view) const;
@ -312,6 +316,7 @@ private:
void deleteAsGroup(FullMsgId itemId);
void reportItem(FullMsgId itemId);
void reportAsGroup(FullMsgId itemId);
void reportItems(MessageIdsList ids);
void blockSenderItem(FullMsgId itemId);
void blockSenderAsGroup(FullMsgId itemId);
void copySelectedText();
@ -350,6 +355,7 @@ private:
style::cursor _cursor = style::cur_default;
SelectedItems _selected;
std::optional<Ui::ReportReason> _chooseForReportReason;
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
base::flat_map<

View file

@ -73,6 +73,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_webpage_preview.h"
#include "history/view/history_view_top_bar_widget.h"
#include "history/view/history_view_contact_status.h"
#include "history/view/history_view_context_menu.h"
#include "history/view/history_view_pinned_tracker.h"
#include "history/view/history_view_pinned_section.h"
#include "history/view/history_view_pinned_bar.h"
@ -102,6 +103,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "apiwrap.h"
#include "base/qthelp_regex.h"
#include "ui/boxes/report_box.h"
#include "ui/chat/pinned_bar.h"
#include "ui/chat/group_call_bar.h"
#include "ui/widgets/popup_menu.h"
@ -111,6 +113,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session_settings.h"
#include "window/themes/window_theme.h"
#include "window/notifications_manager.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "window/window_slide_animation.h"
#include "window/window_peer_menu.h"
@ -196,6 +199,7 @@ HistoryWidget::HistoryWidget(
this,
tr::lng_channel_mute(tr::now).toUpper(),
st::historyComposeButton)
, _reportMessages(this, QString(), st::historyComposeButton)
, _attachToggle(this, st::historyAttach)
, _tabbedSelectorToggle(this, st::historyAttachEmoji)
, _botKeyboardShow(this, st::historyBotKeyboardShow)
@ -242,6 +246,7 @@ HistoryWidget::HistoryWidget(
_botStart->addClickHandler([=] { sendBotStartCommand(); });
_joinChannel->addClickHandler([=] { joinChannel(); });
_muteUnmute->addClickHandler([=] { toggleMuteUnmute(); });
_reportMessages->addClickHandler([=] { reportSelectedMessages(); });
connect(
_field,
&Ui::InputField::submitted,
@ -373,6 +378,7 @@ HistoryWidget::HistoryWidget(
_botStart->hide();
_joinChannel->hide();
_muteUnmute->hide();
_reportMessages->hide();
initVoiceRecordBar();
@ -687,6 +693,10 @@ HistoryWidget::HistoryWidget(
) | rpl::start_with_next([=] {
clearSelected();
}, _topBar->lifetime());
_topBar->cancelChooseForReportRequest(
) | rpl::start_with_next([=] {
setChooseReportMessagesDetails({}, nullptr);
}, _topBar->lifetime());
session().api().sendActions(
) | rpl::filter([=](const Api::SendAction &action) {
@ -1743,6 +1753,9 @@ void HistoryWidget::showHistory(
if (startBot) {
showAtMsgId = ShowAtTheEndMsgId;
}
if (showAtMsgId != ShowForChooseMessagesMsgId) {
_chooseForReport = nullptr;
}
clearHighlightMessages();
hideInfoTooltip(anim::type::instant);
@ -1753,7 +1766,17 @@ void HistoryWidget::showHistory(
if (showAtMsgId == ShowAtUnreadMsgId
&& insideJumpToEndInsteadOfToUnread()) {
showAtMsgId = ShowAtTheEndMsgId;
} else if (showAtMsgId == ShowForChooseMessagesMsgId) {
if (_chooseForReport) {
_chooseForReport->active = true;
_list->setChooseReportReason(_chooseForReport->reason);
updateControlsVisibility();
updateControlsGeometry();
updateTopBarChooseForReport();
}
return;
}
_list->clearChooseReportReason();
if (!IsServerMsgId(showAtMsgId)
&& !IsServerMsgId(-showAtMsgId)) {
// To end or to unread.
@ -1918,6 +1941,12 @@ void HistoryWidget::showHistory(
refreshTopBarActiveChat();
updateTopBarSelection();
if (showAtMsgId == ShowForChooseMessagesMsgId) {
showAtMsgId = ShowAtUnreadMsgId;
if (_chooseForReport) {
_chooseForReport->active = true;
}
}
if (_channel) {
updateNotifyControls();
@ -1944,6 +1973,11 @@ void HistoryWidget::showHistory(
object_ptr<HistoryInner>(this, _scroll, controller(), _history));
_list->show();
if (_chooseForReport && _chooseForReport->active) {
_list->setChooseReportReason(_chooseForReport->reason);
}
updateTopBarChooseForReport();
_updateHistoryItems.cancel();
setupPinnedTracker();
@ -2168,8 +2202,18 @@ void HistoryWidget::updateControlsVisibility() {
if (_contactStatus) {
_contactStatus->show();
}
if (!editingMessage() && (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart())) {
if (isBlocked()) {
if (!editingMessage() && (isBlocked() || isJoinChannel() || isMuteUnmute() || isBotStart() || isReportMessages())) {
if (isReportMessages()) {
_unblock->hide();
_joinChannel->hide();
_muteUnmute->hide();
_botStart->hide();
if (_reportMessages->isHidden()) {
_reportMessages->clearState();
_reportMessages->show();
}
} else if (isBlocked()) {
_reportMessages->hide();
_joinChannel->hide();
_muteUnmute->hide();
_botStart->hide();
@ -2178,6 +2222,7 @@ void HistoryWidget::updateControlsVisibility() {
_unblock->show();
}
} else if (isJoinChannel()) {
_reportMessages->hide();
_unblock->hide();
_muteUnmute->hide();
_botStart->hide();
@ -2186,6 +2231,7 @@ void HistoryWidget::updateControlsVisibility() {
_joinChannel->show();
}
} else if (isMuteUnmute()) {
_reportMessages->hide();
_unblock->hide();
_joinChannel->hide();
_botStart->hide();
@ -2194,6 +2240,7 @@ void HistoryWidget::updateControlsVisibility() {
_muteUnmute->show();
}
} else if (isBotStart()) {
_reportMessages->hide();
_unblock->hide();
_joinChannel->hide();
_muteUnmute->hide();
@ -2244,6 +2291,7 @@ void HistoryWidget::updateControlsVisibility() {
_botStart->hide();
_joinChannel->hide();
_muteUnmute->hide();
_reportMessages->hide();
_send->show();
updateSendButtonType();
@ -2303,6 +2351,7 @@ void HistoryWidget::updateControlsVisibility() {
_botStart->hide();
_joinChannel->hide();
_muteUnmute->hide();
_reportMessages->hide();
_attachToggle->hide();
if (_silent) {
_silent->hide();
@ -3268,6 +3317,24 @@ void HistoryWidget::toggleMuteUnmute() {
session().data().updateNotifySettings(_peer, muteForSeconds);
}
void HistoryWidget::reportSelectedMessages() {
if (!_list || !_chooseForReport || !_list->getSelectionState().count) {
return;
}
const auto ids = _list->getSelectedItems();
const auto peer = _peer;
const auto reason = _chooseForReport->reason;
const auto box = std::make_shared<QPointer<Ui::GenericBox>>();
const auto send = [=](const QString &text) {
HistoryView::SendReport(peer, reason, text, ids);
controller()->clearChooseReportMessages();
if (*box) {
(*box)->closeBox();
}
};
*box = controller()->window().show(Box(Ui::ReportDetailsBox, send));
}
History *HistoryWidget::history() const {
return _history;
}
@ -3706,6 +3773,10 @@ bool HistoryWidget::isBotStart() const {
return false;
}
bool HistoryWidget::isReportMessages() const {
return _peer && _chooseForReport && _chooseForReport->active;
}
bool HistoryWidget::isBlocked() const {
return _peer && _peer->isUser() && _peer->asUser()->isBlocked();
}
@ -3993,7 +4064,7 @@ void HistoryWidget::moveFieldControls() {
// _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel
// (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send
// (_botStart|_unblock|_joinChannel|_muteUnmute)
// (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages)
auto buttonsBottom = bottom - _attachToggle->height();
auto left = st::historySendRight;
@ -4037,8 +4108,8 @@ void HistoryWidget::moveFieldControls() {
_botStart->setGeometry(fullWidthButtonRect);
_unblock->setGeometry(fullWidthButtonRect);
_joinChannel->setGeometry(fullWidthButtonRect);
_muteUnmute->setGeometry(fullWidthButtonRect);
_reportMessages->setGeometry(fullWidthButtonRect);
}
void HistoryWidget::updateFieldSize() {
@ -4406,13 +4477,19 @@ void HistoryWidget::handleHistoryChange(not_null<const History*> history) {
const auto botStart = isBotStart();
const auto joinChannel = isJoinChannel();
const auto muteUnmute = isMuteUnmute();
const auto reportMessages = isReportMessages();
const auto update = false
|| (_unblock->isHidden() == unblock)
|| (!unblock && _botStart->isHidden() == botStart)
|| (!unblock
|| (_reportMessages->isHidden() == reportMessages)
|| (!reportMessages && _unblock->isHidden() == unblock)
|| (!reportMessages
&& !unblock
&& _botStart->isHidden() == botStart)
|| (!reportMessages
&& !unblock
&& !botStart
&& _joinChannel->isHidden() == joinChannel)
|| (!unblock
|| (!reportMessages
&& !unblock
&& !botStart
&& !joinChannel
&& _muteUnmute->isHidden() == muteUnmute);
@ -4667,7 +4744,7 @@ void HistoryWidget::updateHistoryGeometry(
if (_contactStatus) {
newScrollHeight -= _contactStatus->height();
}
if (!editingMessage() && (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute())) {
if (!editingMessage() && (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute() || isReportMessages())) {
newScrollHeight -= _unblock->height();
} else {
if (editingMessage() || _canSendMessages) {
@ -5427,6 +5504,28 @@ void HistoryWidget::checkMessagesTTL() {
}
}
void HistoryWidget::setChooseReportMessagesDetails(
Ui::ReportReason reason,
Fn<void(MessageIdsList)> callback) {
if (!callback) {
const auto refresh = _chooseForReport && _chooseForReport->active;
_chooseForReport = nullptr;
if (_list) {
_list->clearChooseReportReason();
}
if (refresh) {
updateControlsVisibility();
updateControlsGeometry();
updateTopBarChooseForReport();
}
} else {
_chooseForReport = std::make_unique<ChooseMessagesForReport>(
ChooseMessagesForReport{
.reason = reason,
.callback = std::move(callback) });
}
}
void HistoryWidget::refreshPinnedBarButton(bool many) {
const auto close = !many;
auto button = object_ptr<Ui::IconButton>(
@ -6209,6 +6308,18 @@ MessageIdsList HistoryWidget::getSelectedItems() const {
return _list ? _list->getSelectedItems() : MessageIdsList();
}
void HistoryWidget::updateTopBarChooseForReport() {
if (_chooseForReport && _chooseForReport->active) {
_topBar->showChooseMessagesForReport(
_chooseForReport->reason);
} else {
_topBar->clearChooseMessagesForReport();
}
updateTopBarSelection();
updateControlsVisibility();
updateControlsGeometry();
}
void HistoryWidget::updateTopBarSelection() {
if (!_list) {
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
@ -6216,8 +6327,25 @@ void HistoryWidget::updateTopBarSelection() {
}
auto selectedState = _list->getSelectionState();
_nonEmptySelection = (selectedState.count > 0) || selectedState.textSelected;
_nonEmptySelection = (selectedState.count > 0)
|| selectedState.textSelected;
_topBar->showSelected(selectedState);
const auto transparent = Qt::WA_TransparentForMouseEvents;
if (selectedState.count == 0) {
_reportMessages->clearState();
_reportMessages->setAttribute(transparent);
_reportMessages->setColorOverride(st::windowSubTextFg->c);
} else if (_reportMessages->testAttribute(transparent)) {
_reportMessages->setAttribute(transparent, false);
_reportMessages->setColorOverride(std::nullopt);
}
_reportMessages->setText(Ui::Text::Upper(selectedState.count
? tr::lng_report_messages_count(
tr::now,
lt_count,
selectedState.count)
: tr::lng_report_messages_none(tr::now)));
updateControlsVisibility();
updateHistoryGeometry();
if (!Ui::isLayerShown() && !Core::App().passcodeLocked()) {

View file

@ -68,6 +68,7 @@ class PinnedBar;
class GroupCallBar;
struct PreparedList;
class SendFilesWay;
enum class ReportReason;
namespace Toast {
class Instance;
} // namespace Toast
@ -141,6 +142,7 @@ public:
bool isItemCompletelyHidden(HistoryItem *item) const;
void updateTopBarSelection();
void updateTopBarChooseForReport();
void loadMessages();
void loadMessagesDown();
@ -230,6 +232,9 @@ public:
void applyDraft(
FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false);
void setChooseReportMessagesDetails(
Ui::ReportReason reason,
Fn<void(MessageIdsList)> callback);
void clearAllLoadRequests();
void clearDelayedShowAtRequest();
void clearDelayedShowAt();
@ -313,6 +318,11 @@ private:
ScrollChangeType type;
int value;
};
struct ChooseMessagesForReport {
Ui::ReportReason reason = {};
Fn<void(MessageIdsList)> callback;
bool active = false;
};
enum class TextUpdateEvent {
SaveDraft = (1 << 0),
SendTyping = (1 << 1),
@ -377,6 +387,7 @@ private:
[[nodiscard]] int computeMaxFieldHeight() const;
void toggleMuteUnmute();
void reportSelectedMessages();
void toggleKeyboard(bool manual = true);
void startBotCommand();
void hidePinnedMessage();
@ -572,6 +583,7 @@ private:
bool isBlocked() const;
bool isJoinChannel() const;
bool isMuteUnmute() const;
bool isReportMessages() const;
bool updateCmdStartShown();
void updateSendButtonType();
bool showRecordButton() const;
@ -687,6 +699,7 @@ private:
object_ptr<Ui::FlatButton> _botStart;
object_ptr<Ui::FlatButton> _joinChannel;
object_ptr<Ui::FlatButton> _muteUnmute;
object_ptr<Ui::FlatButton> _reportMessages;
object_ptr<Ui::IconButton> _attachToggle;
object_ptr<Ui::EmojiButton> _tabbedSelectorToggle;
object_ptr<Ui::IconButton> _botKeyboardShow;
@ -740,6 +753,7 @@ private:
base::Timer _saveCloudDraftTimer;
base::weak_ptr<Ui::Toast::Instance> _topToast;
std::unique_ptr<ChooseMessagesForReport> _chooseForReport;
object_ptr<Ui::PlainShadow> _topShadow;
bool _inGrab = false;

View file

@ -24,11 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/toast/toast.h"
#include "ui/controls/delete_message_context_action.h"
#include "ui/boxes/report_box.h"
#include "ui/ui_utility.h"
#include "chat_helpers/send_context_menu.h"
#include "boxes/confirm_box.h"
#include "boxes/sticker_set_box.h"
#include "boxes/report_box.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_document.h"
@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "base/platform/base_platform_info.h"
#include "window/window_peer_menu.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "lang/lang_keys.h"
#include "core/application.h"
@ -782,13 +783,12 @@ void AddReportAction(
const auto itemId = item->fullId();
const auto callback = crl::guard(controller, [=] {
if (const auto item = owner->message(itemId)) {
const auto peer = item->history()->peer;
const auto group = owner->groups().find(item);
Ui::show(Box<ReportBox>(
peer,
ShowReportItemsBox(
item->history()->peer,
(group
? owner->itemsToIds(group->items)
: MessageIdsList(1, itemId))));
: MessageIdsList{ 1, itemId }));
}
});
menu->addAction(tr::lng_context_report_msg(tr::now), callback);
@ -1051,4 +1051,105 @@ void AddPollActions(
}
}
void ShowReportItemsBox(not_null<PeerData*> peer, MessageIdsList ids) {
const auto chosen = [=](Ui::ReportReason reason) {
Ui::show(Box(Ui::ReportDetailsBox, [=](const QString &text) {
SendReport(peer, reason, text, ids);
Ui::hideLayer();
}));
};
Ui::show(Box(
Ui::ReportReasonBox,
Ui::ReportSource::Message,
chosen));
}
void ShowReportPeerBox(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer) {
struct State {
QPointer<Ui::GenericBox> reasonBox;
QPointer<Ui::GenericBox> detailsBox;
MessageIdsList ids;
};
const auto state = std::make_shared<State>();
const auto chosen = [=](Ui::ReportReason reason) {
const auto send = [=](const QString &text) {
window->clearChooseReportMessages();
SendReport(peer, reason, text, std::move(state->ids));
if (const auto strong = state->reasonBox.data()) {
strong->closeBox();
}
if (const auto strong = state->detailsBox.data()) {
strong->closeBox();
}
};
if (reason == Ui::ReportReason::Fake
|| reason == Ui::ReportReason::Other) {
state->ids = {};
state->detailsBox = window->window().show(
Box(Ui::ReportDetailsBox, send));
return;
}
window->showChooseReportMessages(peer, reason, [=](
MessageIdsList ids) {
state->ids = std::move(ids);
state->detailsBox = window->window().show(
Box(Ui::ReportDetailsBox, send));
});
};
state->reasonBox = window->window().show(Box(
Ui::ReportReasonBox,
(peer->isBroadcast()
? Ui::ReportSource::Channel
: peer->isUser()
? Ui::ReportSource::Bot
: Ui::ReportSource::Group),
chosen));
}
void SendReport(
not_null<PeerData*> peer,
Ui::ReportReason reason,
const QString &comment,
MessageIdsList ids) {
const auto apiReason = [&] {
using Reason = Ui::ReportReason;
switch (reason) {
case Reason::Spam: return MTP_inputReportReasonSpam();
case Reason::Fake: return MTP_inputReportReasonFake();
case Reason::Violence: return MTP_inputReportReasonViolence();
case Reason::ChildAbuse: return MTP_inputReportReasonChildAbuse();
case Reason::Pornography: return MTP_inputReportReasonPornography();
case Reason::Other: return MTP_inputReportReasonOther();
}
Unexpected("Bad reason group value.");
}();
if (ids.empty()) {
peer->session().api().request(MTPaccount_ReportPeer(
peer->input,
apiReason,
MTP_string(comment)
)).done([=](const MTPBool &result) {
Ui::Toast::Show(tr::lng_report_thanks(tr::now));
}).fail([=](const RPCError &error) {
}).send();
} else {
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),
apiReason,
MTP_string(comment)
)).done([=](const MTPBool &result) {
Ui::Toast::Show(tr::lng_report_thanks(tr::now));
}).fail([=](const RPCError &error) {
}).send();
}
}
} // namespace HistoryView

View file

@ -11,10 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
class PopupMenu;
enum class ReportReason;
} // namespace Ui
namespace Window {
class SessionNavigation;
class SessionController;
} // namespace Main
namespace HistoryView {
@ -55,4 +57,14 @@ void AddPollActions(
not_null<HistoryItem*> item,
Context context);
} // namespace
void ShowReportItemsBox(not_null<PeerData*> peer, MessageIdsList ids);
void ShowReportPeerBox(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer);
void SendReport(
not_null<PeerData*> peer,
Ui::ReportReason reason,
const QString &comment,
MessageIdsList ids = {});
} // namespace HistoryView

View file

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/dropdown_menu.h"
#include "ui/effects/radial_animation.h"
#include "ui/toasts/common_toasts.h"
#include "ui/boxes/report_box.h" // Ui::ReportReason
#include "ui/special_buttons.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
@ -63,6 +64,7 @@ TopBarWidget::TopBarWidget(
, _sendNow(this, tr::lng_selected_send_now(), st::defaultActiveButton)
, _delete(this, tr::lng_selected_delete(), st::defaultActiveButton)
, _back(this, st::historyTopBarBack)
, _cancelChoose(this, st::topBarCloseChoose)
, _call(this, st::topBarCall)
, _groupCall(this, st::topBarGroupCall)
, _search(this, st::topBarSearch)
@ -90,6 +92,8 @@ TopBarWidget::TopBarWidget(
_menuToggle->setClickedCallback([=] { showMenu(); });
_infoToggle->setClickedCallback([=] { toggleInfoSection(); });
_back->addClickHandler([=] { backClicked(); });
_cancelChoose->setClickedCallback(
[=] { _cancelChooseForReport.fire({}); });
rpl::combine(
_controller->activeChatValue(),
@ -224,6 +228,34 @@ void TopBarWidget::groupCall() {
}
}
void TopBarWidget::showChooseMessagesForReport(Ui::ReportReason reason) {
setChooseForReportReason(reason);
}
void TopBarWidget::clearChooseMessagesForReport() {
setChooseForReportReason(std::nullopt);
}
void TopBarWidget::setChooseForReportReason(
std::optional<Ui::ReportReason> reason) {
if (_chooseForReportReason == reason) {
return;
}
const auto wasNoReason = !_chooseForReportReason;
_chooseForReportReason = reason;
const auto nowNoReason = !_chooseForReportReason;
updateControlsVisibility();
updateControlsGeometry();
update();
if (wasNoReason != nowNoReason && _selectedCount > 0) {
toggleSelectedControls(false);
finishAnimating();
}
setCursor((nowNoReason && !_selectedCount)
? style::cur_pointer
: style::cur_default);
}
void TopBarWidget::showMenu() {
if (!_activeChat.key || _menu) {
return;
@ -318,8 +350,8 @@ void TopBarWidget::paintEvent(QPaintEvent *e) {
}
Painter p(this);
auto hasSelected = (_selectedCount > 0);
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.value(hasSelected ? 1. : 0.));
auto selectedButtonsTop = countSelectedButtonsTop(
_selectedShown.value(showSelectedActions() ? 1. : 0.));
p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBg);
if (selectedButtonsTop < 0) {
@ -337,6 +369,34 @@ void TopBarWidget::paintTopBar(Painter &p) {
auto statustop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
auto availableWidth = width() - _rightTaken - nameleft;
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);
}
Unexpected("reason in TopBarWidget::paintTopBar.");
}();
p.setPen(st::dialogsNameFg);
p.setFont(st::semiboldFont);
p.drawTextLeft(nameleft, nametop, width(), text);
p.setFont(st::dialogsTextFont);
p.setPen(st::historyStatusFg);
p.drawTextLeft(
nameleft,
statustop,
width(),
tr::lng_report_select_messages(tr::now));
return;
}
const auto history = _activeChat.key.history();
const auto folder = _activeChat.key.folder();
if (folder
@ -476,7 +536,8 @@ QRect TopBarWidget::getMembersShowAreaGeometry() const {
void TopBarWidget::mousePressEvent(QMouseEvent *e) {
auto handleClick = (e->button() == Qt::LeftButton)
&& (e->pos().y() < st::topBarHeight)
&& (!_selectedCount);
&& !_selectedCount
&& !_chooseForReportReason;
if (handleClick) {
if (_animatingMode && _back->rect().contains(e->pos())) {
backClicked();
@ -610,14 +671,16 @@ void TopBarWidget::updateSearchVisibility() {
const auto historyMode = (_activeChat.section == Section::History);
const auto smallDialogsColumn = _activeChat.key.folder()
&& (width() < _back->width() + _search->width());
_search->setVisible(historyMode && !smallDialogsColumn);
_search->setVisible(historyMode
&& !smallDialogsColumn
&& !_chooseForReportReason);
}
void TopBarWidget::updateControlsGeometry() {
if (!_activeChat.key) {
return;
}
auto hasSelected = (_selectedCount > 0);
auto hasSelected = showSelectedActions();
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.value(hasSelected ? 1. : 0.));
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
auto buttonsLeft = st::topBarActionSkip + (Adaptive::OneColumn() ? 0 : st::lineWidth);
@ -648,7 +711,11 @@ void TopBarWidget::updateControlsGeometry() {
_delete->moveToLeft(buttonsLeft, selectedButtonsTop);
_clear->moveToRight(st::topBarActionSkip, selectedButtonsTop);
if (_back->isHidden()) {
if (!_cancelChoose->isHidden()) {
_leftTaken = 0;
_cancelChoose->moveToLeft(_leftTaken, otherButtonsTop);
_leftTaken += _cancelChoose->width();
} else if (_back->isHidden()) {
_leftTaken = st::topBarArrowPadding.right();
} else {
const auto smallDialogsColumn = _activeChat.key.folder()
@ -714,12 +781,13 @@ void TopBarWidget::updateControlsVisibility() {
auto backVisible = Adaptive::OneColumn()
|| !_controller->content()->stackIsEmpty()
|| _activeChat.key.folder();
_back->setVisible(backVisible);
_back->setVisible(backVisible && !_chooseForReportReason);
_cancelChoose->setVisible(_chooseForReportReason.has_value());
if (_info) {
_info->setVisible(Adaptive::OneColumn());
_info->setVisible(Adaptive::OneColumn() && !_chooseForReportReason);
}
if (_unreadBadge) {
_unreadBadge->show();
_unreadBadge->setVisible(!_chooseForReportReason);
}
const auto section = _activeChat.section;
const auto historyMode = (section == Section::History);
@ -730,11 +798,12 @@ void TopBarWidget::updateControlsVisibility() {
? hasPollsMenu
: historyMode);
updateSearchVisibility();
_menuToggle->setVisible(hasMenu);
_menuToggle->setVisible(hasMenu && !_chooseForReportReason);
_infoToggle->setVisible(historyMode
&& !_activeChat.key.folder()
&& !Adaptive::OneColumn()
&& _controller->canShowThirdSection());
&& _controller->canShowThirdSection()
&& !_chooseForReportReason);
const auto callsEnabled = [&] {
if (const auto peer = _activeChat.key.peer()) {
if (const auto user = peer->asUser()) {
@ -743,7 +812,9 @@ void TopBarWidget::updateControlsVisibility() {
}
return false;
}();
_call->setVisible(historyMode && callsEnabled);
_call->setVisible(historyMode
&& callsEnabled
&& !_chooseForReportReason);
const auto groupCallsEnabled = [&] {
if (const auto peer = _activeChat.key.peer()) {
if (peer->canManageGroupCall()) {
@ -755,10 +826,12 @@ void TopBarWidget::updateControlsVisibility() {
}
return false;
}();
_groupCall->setVisible(historyMode && groupCallsEnabled);
_groupCall->setVisible(historyMode
&& groupCallsEnabled
&& !_chooseForReportReason);
if (_membersShowArea) {
_membersShowArea->show();
_membersShowArea->setVisible(!_chooseForReportReason);
}
updateControlsGeometry();
}
@ -824,21 +897,29 @@ void TopBarWidget::showSelected(SelectedState state) {
_canSendNow = canSendNow;
updateControlsVisibility();
}
if (wasSelected != hasSelected) {
if (wasSelected != hasSelected && !_chooseForReportReason) {
setCursor(hasSelected ? style::cur_default : style::cur_pointer);
updateMembersShowArea();
_selectedShown.start(
[this] { selectedShowCallback(); },
hasSelected ? 0. : 1.,
hasSelected ? 1. : 0.,
st::slideWrapDuration,
anim::easeOutCirc);
toggleSelectedControls(hasSelected);
} else {
updateControlsGeometry();
}
}
void TopBarWidget::toggleSelectedControls(bool shown) {
_selectedShown.start(
[this] { selectedShowCallback(); },
shown ? 0. : 1.,
shown ? 1. : 0.,
st::slideWrapDuration,
anim::easeOutCirc);
}
bool TopBarWidget::showSelectedActions() const {
return (_selectedCount > 0) && !_chooseForReportReason;
}
void TopBarWidget::selectedShowCallback() {
updateControlsGeometry();
update();

View file

@ -24,6 +24,7 @@ class IconButton;
class DropdownMenu;
class UnreadBadge;
class InfiniteRadialAnimation;
enum class ReportReason;
} // namespace Ui
namespace Window {
@ -66,6 +67,9 @@ public:
SendActionPainter *sendAction);
void setCustomTitle(const QString &title);
void showChooseMessagesForReport(Ui::ReportReason reason);
void clearChooseMessagesForReport();
rpl::producer<> forwardSelectionRequest() const {
return _forwardSelection.events();
}
@ -78,6 +82,9 @@ public:
rpl::producer<> clearSelectionRequest() const {
return _clearSelection.events();
}
rpl::producer<> cancelChooseForReportRequest() const {
return _cancelChooseForReport.events();
}
protected:
void paintEvent(QPaintEvent *e) override;
@ -126,6 +133,9 @@ private:
void refreshUnreadBadge();
void updateUnreadBadge();
void setChooseForReportReason(std::optional<Ui::ReportReason> reason);
void toggleSelectedControls(bool shown);
[[nodiscard]] bool showSelectedActions() const;
const not_null<Window::SessionController*> _controller;
ActiveChat _activeChat;
@ -143,6 +153,7 @@ private:
object_ptr<Ui::RoundButton> _forward, _sendNow, _delete;
object_ptr<Ui::IconButton> _back;
object_ptr<Ui::IconButton> _cancelChoose;
object_ptr<Ui::UnreadBadge> _unreadBadge = { nullptr };
object_ptr<Ui::AbstractButton> _info = { nullptr };
@ -164,6 +175,7 @@ private:
std::unique_ptr<Ui::InfiniteRadialAnimation> _connecting;
SendActionPainter *_sendAction = nullptr;
std::optional<Ui::ReportReason> _chooseForReportReason;
base::Timer _onlineUpdater;
@ -171,6 +183,7 @@ private:
rpl::event_stream<> _sendNowSelection;
rpl::event_stream<> _deleteSelection;
rpl::event_stream<> _clearSelection;
rpl::event_stream<> _cancelChooseForReport;
};

View file

@ -749,6 +749,13 @@ topBarSearch: IconButton {
color: windowBgOver;
}
}
topBarCloseChoose: IconButton(topBarSearch) {
width: 56px;
icon: icon {{ "info_close", boxTitleCloseFg }};
iconOver: icon {{ "info_close", boxTitleCloseFgOver }};
iconPosition: point(10px, -1px);
rippleAreaPosition: point(7px, 7px);
}
topBarSkip: -5px;
topBarCallSkip: -1px;
topBarMenuToggle: IconButton(topBarSearch) {

View file

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/boxes/report_box.h"
#include "ui/layers/generic_box.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
@ -29,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/add_contact_box.h"
#include "boxes/report_box.h"
#include "boxes/peers/edit_contact_box.h"
#include "lang/lang_keys.h"
#include "info/info_controller.h"
@ -586,11 +586,14 @@ void ActionsFiller::addBotCommandActions(not_null<UserData*> user) {
void ActionsFiller::addReportAction() {
const auto peer = _peer;
const auto report = [=] {
};
AddActionButton(
_wrap,
tr::lng_profile_report(),
rpl::single(true),
[=] { Ui::show(Box<ReportBox>(peer)); },
report,
st::infoBlockButton);
}

View file

@ -23,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "boxes/add_contact_box.h"
#include "boxes/confirm_box.h"
#include "boxes/report_box.h"
#include "mainwidget.h"
#include "main/main_session.h"
#include "apiwrap.h"

View file

@ -1414,6 +1414,18 @@ void MainWidget::ctrlEnterSubmitUpdated() {
_history->updateFieldSubmitSettings();
}
void MainWidget::showChooseReportMessages(
not_null<PeerData*> peer,
Ui::ReportReason reason,
Fn<void(MessageIdsList)> done) {
_history->setChooseReportMessagesDetails(reason, std::move(done));
ui_showPeerHistory(peer->id, SectionShow(), ShowForChooseMessagesMsgId);
}
void MainWidget::clearChooseReportMessages() {
_history->setChooseReportMessagesDetails({}, nullptr);
}
void MainWidget::ui_showPeerHistory(
PeerId peerId,
const SectionShow &params,

View file

@ -65,6 +65,7 @@ namespace Ui {
class ResizeArea;
class PlainShadow;
class DropdownMenu;
enum class ReportReason;
template <typename Widget>
class SlideWrap;
} // namespace Ui
@ -210,6 +211,12 @@ public:
void searchInChat(Dialogs::Key chat);
void showChooseReportMessages(
not_null<PeerData*> peer,
Ui::ReportReason reason,
Fn<void(MessageIdsList)> done);
void clearChooseReportMessages();
void ui_showPeerHistory(
PeerId peer,
const SectionShow &params,

View file

@ -0,0 +1,93 @@
/*
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 "ui/boxes/report_box.h"
#include "lang/lang_keys.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/toast/toast.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
namespace Ui {
namespace {
constexpr auto kReportReasonLengthMax = 512;
using Source = ReportSource;
using Reason = ReportReason;
} // namespace
void ReportReasonBox(
not_null<GenericBox*> box,
ReportSource source,
Fn<void(Reason)> done) {
box->setTitle([&] {
switch (source) {
case Source::Message: return tr::lng_report_message_title();
case Source::Channel: return tr::lng_report_title();
case Source::Group: return tr::lng_report_group_title();
case Source::Bot: return tr::lng_report_bot_title();
}
Unexpected("'source' in ReportReasonBox.");
}());
const auto add = [&](Reason reason, tr::phrase<> text) {
const auto layout = box->verticalLayout();
const auto button = layout->add(
object_ptr<Ui::SettingsButton>(layout, text()));
button->setClickedCallback([=] {
done(reason);
});
};
add(Reason::Spam, tr::lng_report_reason_spam);
if (source != Source::Message) {
add(Reason::Fake, tr::lng_report_reason_fake);
}
add(Reason::Violence, tr::lng_report_reason_violence);
add(Reason::ChildAbuse, tr::lng_report_reason_child_abuse);
add(Reason::Pornography, tr::lng_report_reason_pornography);
add(Reason::Other, tr::lng_report_reason_other);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void ReportDetailsBox(
not_null<GenericBox*> box,
Fn<void(QString)> done) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box, // #TODO reports
tr::lng_report_details_about(),
st::boxLabel),
{
st::boxRowPadding.left(),
st::boxPadding.top(),
st::boxRowPadding.right(),
st::boxPadding.bottom() });
const auto details = box->addRow(
object_ptr<Ui::InputField>(
box,
st::newGroupDescription,
Ui::InputField::Mode::MultiLine,
tr::lng_report_details(),
QString()));
details->setMaxLength(kReportReasonLengthMax);
box->setFocusCallback([=] {
details->setFocusFast();
});
box->addButton(tr::lng_report_button(), [=] {
const auto text = details->getLastText();
done(text);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
} // namespace Ui

View file

@ -0,0 +1,39 @@
/*
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 Ui {
class GenericBox;
enum class ReportSource {
Message,
Channel,
Group,
Bot,
};
enum class ReportReason {
Spam,
Fake,
Violence,
ChildAbuse,
Pornography,
Other,
};
void ReportReasonBox(
not_null<GenericBox*> box,
ReportSource source,
Fn<void(ReportReason)> done);
void ReportDetailsBox(
not_null<GenericBox*> box,
Fn<void(QString)> done);
} // namespace Ui

View file

@ -11,10 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "boxes/mute_settings_box.h"
#include "boxes/add_contact_box.h"
#include "boxes/report_box.h"
#include "boxes/create_poll_box.h"
#include "boxes/peers/add_participants_box.h"
#include "boxes/peers/edit_contact_box.h"
#include "ui/boxes/report_box.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/labels.h"
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_message.h" // GetErrorTextForSending.
#include "history/view/history_view_context_menu.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "support/support_helper.h"
@ -604,8 +605,8 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) {
const auto needReport = !channel->amCreator()
&& (!isGroup || channel->isPublic());
if (needReport) {
_addAction(tr::lng_profile_report(tr::now), [channel] {
Ui::show(Box<ReportBox>(channel));
_addAction(tr::lng_profile_report(tr::now), [=] {
HistoryView::ShowReportPeerBox(navigation, channel);
});
}
}
@ -974,6 +975,21 @@ void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user) {
});
}
void BlockSenderFromRepliesBox(
not_null<Ui::GenericBox*> box,
not_null<SessionController*> controller,
FullMsgId id) {
const auto item = controller->session().data().message(id);
Assert(item != nullptr);
PeerMenuBlockUserBox(
box,
&controller->window(),
item->senderOriginal(),
true,
Window::ClearReply{ id });
}
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
not_null<Window::SessionNavigation*> navigation,
MessageIdsList &&items,

View file

@ -86,6 +86,11 @@ void PeerMenuBlockUserBox(
std::variant<v::null_t, ClearChat, ClearReply> suggestClear);
void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user);
void BlockSenderFromRepliesBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
FullMsgId id);
void ToggleHistoryArchived(not_null<History*> history, bool archived);
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer);
Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer);

View file

@ -1073,6 +1073,17 @@ void SessionController::clearPassportForm() {
_passportForm = nullptr;
}
void SessionController::showChooseReportMessages(
not_null<PeerData*> peer,
Ui::ReportReason reason,
Fn<void(MessageIdsList)> done) {
content()->showChooseReportMessages(peer, reason, std::move(done));
}
void SessionController::clearChooseReportMessages() {
content()->clearChooseReportMessages();
}
void SessionController::updateColumnLayout() {
content()->updateColumnLayout();
}

View file

@ -45,6 +45,7 @@ class FormController;
namespace Ui {
class LayerWidget;
enum class ReportReason;
} // namespace Ui
namespace Window {
@ -332,6 +333,12 @@ public:
void showPassportForm(const Passport::FormRequest &request);
void clearPassportForm();
void showChooseReportMessages(
not_null<PeerData*> peer,
Ui::ReportReason reason,
Fn<void(MessageIdsList)> done);
void clearChooseReportMessages();
base::Variable<bool> &dialogsListFocused() {
return _dialogsListFocused;
}

View file

@ -72,6 +72,8 @@ PRIVATE
ui/boxes/choose_date_time.h
ui/boxes/edit_invite_link.cpp
ui/boxes/edit_invite_link.h
ui/boxes/report_box.cpp
ui/boxes/report_box.h
ui/chat/attach/attach_album_thumbnail.cpp
ui/chat/attach/attach_album_thumbnail.h
ui/chat/attach/attach_album_preview.cpp