report_messages_box.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "boxes/report_messages_box.h"
  8. #include "api/api_report.h"
  9. #include "core/application.h"
  10. #include "data/data_peer.h"
  11. #include "data/data_photo.h"
  12. #include "lang/lang_keys.h"
  13. #include "ui/boxes/report_box_graphics.h"
  14. #include "ui/layers/generic_box.h"
  15. #include "ui/rect.h"
  16. #include "ui/vertical_list.h"
  17. #include "ui/widgets/buttons.h"
  18. #include "ui/widgets/fields/input_field.h"
  19. #include "window/window_controller.h"
  20. #include "window/window_session_controller.h"
  21. #include "styles/style_boxes.h"
  22. #include "styles/style_chat_helpers.h"
  23. #include "styles/style_layers.h"
  24. #include "styles/style_settings.h"
  25. namespace {
  26. [[nodiscard]] object_ptr<Ui::BoxContent> ReportPhoto(
  27. not_null<PeerData*> peer,
  28. not_null<PhotoData*> photo,
  29. const style::ReportBox *stOverride) {
  30. const auto source = [&] {
  31. return peer->isUser()
  32. ? (photo->hasVideo()
  33. ? Ui::ReportSource::ProfileVideo
  34. : Ui::ReportSource::ProfilePhoto)
  35. : (peer->isChat() || (peer->isChannel() && peer->isMegagroup()))
  36. ? (photo->hasVideo()
  37. ? Ui::ReportSource::GroupVideo
  38. : Ui::ReportSource::GroupPhoto)
  39. : (photo->hasVideo()
  40. ? Ui::ReportSource::ChannelVideo
  41. : Ui::ReportSource::ChannelPhoto);
  42. }();
  43. const auto st = stOverride ? stOverride : &st::defaultReportBox;
  44. return Box([=](not_null<Ui::GenericBox*> box) {
  45. const auto show = box->uiShow();
  46. Ui::ReportReasonBox(box, *st, source, [=](Ui::ReportReason reason) {
  47. show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
  48. Ui::ReportDetailsBox(box, *st, [=](const QString &text) {
  49. Api::SendPhotoReport(show, peer, reason, text, photo);
  50. show->hideLayer();
  51. });
  52. }));
  53. });
  54. });
  55. }
  56. } // namespace
  57. object_ptr<Ui::BoxContent> ReportProfilePhotoBox(
  58. not_null<PeerData*> peer,
  59. not_null<PhotoData*> photo) {
  60. return ReportPhoto(peer, photo, nullptr);
  61. }
  62. void ShowReportMessageBox(
  63. std::shared_ptr<Ui::Show> show,
  64. not_null<PeerData*> peer,
  65. const std::vector<MsgId> &ids,
  66. const std::vector<StoryId> &stories,
  67. const style::ReportBox *stOverride) {
  68. const auto report = Api::CreateReportMessagesOrStoriesCallback(
  69. show,
  70. peer);
  71. auto performRequest = [=](
  72. const auto &repeatRequest,
  73. Data::ReportInput reportInput) -> void {
  74. report(reportInput, [=](const Api::ReportResult &result) {
  75. if (!result.error.isEmpty()) {
  76. if (result.error == u"MESSAGE_ID_REQUIRED"_q) {
  77. const auto widget = show->toastParent();
  78. const auto window = Core::App().findWindow(widget);
  79. const auto controller = window
  80. ? window->sessionController()
  81. : nullptr;
  82. if (controller) {
  83. const auto callback = [=](std::vector<MsgId> ids) {
  84. auto copy = reportInput;
  85. copy.ids = std::move(ids);
  86. repeatRequest(repeatRequest, std::move(copy));
  87. };
  88. controller->showChooseReportMessages(
  89. peer,
  90. reportInput,
  91. std::move(callback));
  92. }
  93. } else {
  94. show->showToast(result.error);
  95. }
  96. return;
  97. }
  98. if (!result.options.empty() || result.commentOption) {
  99. show->show(Box([=](not_null<Ui::GenericBox*> box) {
  100. box->setTitle(
  101. rpl::single(
  102. result.title.isEmpty()
  103. ? reportInput.optionText
  104. : result.title));
  105. for (const auto &option : result.options) {
  106. const auto button = Ui::AddReportOptionButton(
  107. box->verticalLayout(),
  108. option.text,
  109. stOverride);
  110. button->setClickedCallback([=] {
  111. auto copy = reportInput;
  112. copy.optionId = option.id;
  113. copy.optionText = option.text;
  114. repeatRequest(repeatRequest, std::move(copy));
  115. });
  116. }
  117. if (const auto commentOption = result.commentOption) {
  118. constexpr auto kReportReasonLengthMax = 512;
  119. const auto &st = stOverride
  120. ? stOverride
  121. : &st::defaultReportBox;
  122. Ui::AddReportDetailsIconButton(box);
  123. Ui::AddSkip(box->verticalLayout());
  124. Ui::AddSkip(box->verticalLayout());
  125. const auto details = box->addRow(
  126. object_ptr<Ui::InputField>(
  127. box,
  128. st->field,
  129. Ui::InputField::Mode::MultiLine,
  130. commentOption->optional
  131. ? tr::lng_report_details_optional()
  132. : tr::lng_report_details_non_optional(),
  133. QString()));
  134. Ui::AddSkip(box->verticalLayout());
  135. Ui::AddSkip(box->verticalLayout());
  136. {
  137. const auto container = box->verticalLayout();
  138. auto label = object_ptr<Ui::FlatLabel>(
  139. container,
  140. tr::lng_report_details_message_about(),
  141. st::boxDividerLabel);
  142. label->setTextColorOverride(st->dividerFg->c);
  143. using namespace Ui;
  144. const auto widget = container->add(
  145. object_ptr<PaddingWrap<>>(
  146. container,
  147. std::move(label),
  148. st::defaultBoxDividerLabelPadding));
  149. const auto background
  150. = CreateChild<BoxContentDivider>(
  151. widget,
  152. st::boxDividerHeight,
  153. st->dividerBg,
  154. RectPart::Top | RectPart::Bottom);
  155. background->lower();
  156. widget->sizeValue(
  157. ) | rpl::start_with_next([=](const QSize &s) {
  158. background->resize(s);
  159. }, background->lifetime());
  160. }
  161. details->setMaxLength(kReportReasonLengthMax);
  162. box->setFocusCallback([=] {
  163. details->setFocusFast();
  164. });
  165. const auto submit = [=] {
  166. if (!commentOption->optional
  167. && details->empty()) {
  168. details->showError();
  169. details->setFocus();
  170. return;
  171. }
  172. auto copy = reportInput;
  173. copy.optionId = commentOption->id;
  174. copy.comment = details->getLastText();
  175. repeatRequest(repeatRequest, std::move(copy));
  176. };
  177. details->submits(
  178. ) | rpl::start_with_next(submit, details->lifetime());
  179. box->addButton(tr::lng_report_button(), submit);
  180. } else {
  181. box->addButton(
  182. tr::lng_close(),
  183. [=] { show->hideLayer(); });
  184. }
  185. if (!reportInput.optionId.isNull()) {
  186. box->addLeftButton(
  187. tr::lng_create_group_back(),
  188. [=] { box->closeBox(); });
  189. }
  190. }));
  191. } else if (result.successful) {
  192. constexpr auto kToastDuration = crl::time(4000);
  193. show->showToast(
  194. tr::lng_report_thanks(tr::now),
  195. kToastDuration);
  196. show->hideLayer();
  197. }
  198. });
  199. };
  200. performRequest(performRequest, { .ids = ids, .stories = stories });
  201. }