moderate_messages_box.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  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/moderate_messages_box.h"
  8. #include "api/api_blocked_peers.h"
  9. #include "api/api_chat_participants.h"
  10. #include "api/api_messages_search.h"
  11. #include "apiwrap.h"
  12. #include "base/event_filter.h"
  13. #include "base/timer.h"
  14. #include "boxes/delete_messages_box.h"
  15. #include "boxes/peers/edit_peer_permissions_box.h"
  16. #include "core/application.h"
  17. #include "core/ui_integration.h"
  18. #include "data/data_channel.h"
  19. #include "data/data_chat.h"
  20. #include "data/data_chat_filters.h"
  21. #include "data/data_chat_participant_status.h"
  22. #include "data/data_histories.h"
  23. #include "data/data_peer.h"
  24. #include "data/data_session.h"
  25. #include "data/data_user.h"
  26. #include "data/stickers/data_custom_emoji.h"
  27. #include "history/history.h"
  28. #include "history/history_item.h"
  29. #include "lang/lang_keys.h"
  30. #include "main/main_session.h"
  31. #include "ui/boxes/confirm_box.h"
  32. #include "ui/controls/userpic_button.h"
  33. #include "ui/effects/ripple_animation.h"
  34. #include "ui/layers/generic_box.h"
  35. #include "ui/painter.h"
  36. #include "ui/rect.h"
  37. #include "ui/rect_part.h"
  38. #include "ui/text/text_utilities.h"
  39. #include "ui/vertical_list.h"
  40. #include "ui/widgets/checkbox.h"
  41. #include "ui/widgets/expandable_peer_list.h"
  42. #include "ui/widgets/participants_check_view.h"
  43. #include "ui/wrap/slide_wrap.h"
  44. #include "ui/ui_utility.h"
  45. #include "styles/style_boxes.h"
  46. #include "styles/style_layers.h"
  47. #include "styles/style_window.h"
  48. namespace {
  49. struct ModerateOptions final {
  50. bool allCanBan = false;
  51. bool allCanDelete = false;
  52. Participants participants;
  53. };
  54. ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) {
  55. Expects(!items.empty());
  56. auto result = ModerateOptions{
  57. .allCanBan = true,
  58. .allCanDelete = true,
  59. };
  60. const auto peer = items.front()->history()->peer;
  61. for (const auto &item : items) {
  62. if (!result.allCanBan && !result.allCanDelete) {
  63. return {};
  64. }
  65. if (peer != item->history()->peer) {
  66. return {};
  67. }
  68. {
  69. const auto author = item->author();
  70. if (author == peer) {
  71. return {};
  72. } else if (const auto channel = author->asChannel()) {
  73. if (channel->linkedChat() == peer) {
  74. return {};
  75. }
  76. }
  77. }
  78. if (!item->suggestBanReport()) {
  79. result.allCanBan = false;
  80. }
  81. if (!item->suggestDeleteAllReport()) {
  82. result.allCanDelete = false;
  83. }
  84. if (const auto p = item->from()) {
  85. if (!ranges::contains(result.participants, not_null{ p })) {
  86. result.participants.push_back(p);
  87. }
  88. }
  89. }
  90. return result;
  91. }
  92. [[nodiscard]] rpl::producer<base::flat_map<PeerId, int>> MessagesCountValue(
  93. not_null<History*> history,
  94. std::vector<not_null<PeerData*>> from) {
  95. return [=](auto consumer) {
  96. auto lifetime = rpl::lifetime();
  97. struct State final {
  98. base::flat_map<PeerId, int> messagesCounts;
  99. int index = 0;
  100. rpl::lifetime apiLifetime;
  101. };
  102. const auto search = lifetime.make_state<Api::MessagesSearch>(history);
  103. const auto state = lifetime.make_state<State>();
  104. const auto send = [=](auto repeat) -> void {
  105. if (state->index >= from.size()) {
  106. consumer.put_next_copy(state->messagesCounts);
  107. return;
  108. }
  109. const auto peer = from[state->index];
  110. const auto peerId = peer->id;
  111. state->apiLifetime = search->messagesFounds(
  112. ) | rpl::start_with_next([=](const Api::FoundMessages &found) {
  113. state->messagesCounts[peerId] = found.total;
  114. state->index++;
  115. repeat(repeat);
  116. });
  117. search->searchMessages({ .from = peer });
  118. };
  119. consumer.put_next({});
  120. send(send);
  121. return lifetime;
  122. };
  123. }
  124. } // namespace
  125. void CreateModerateMessagesBox(
  126. not_null<Ui::GenericBox*> box,
  127. const HistoryItemsList &items,
  128. Fn<void()> confirmed) {
  129. using Controller = Ui::ExpandablePeerListController;
  130. const auto [allCanBan, allCanDelete, participants]
  131. = CalculateModerateOptions(items);
  132. const auto inner = box->verticalLayout();
  133. Assert(!participants.empty());
  134. const auto confirms = inner->lifetime().make_state<rpl::event_stream<>>();
  135. const auto isSingle = participants.size() == 1;
  136. const auto buttonPadding = isSingle
  137. ? QMargins()
  138. : QMargins(
  139. 0,
  140. 0,
  141. Ui::ParticipantsCheckView::ComputeSize(
  142. participants.size()).width(),
  143. 0);
  144. const auto session = &items.front()->history()->session();
  145. const auto historyPeerId = items.front()->history()->peer->id;
  146. using Request = Fn<void(not_null<PeerData*>, not_null<ChannelData*>)>;
  147. const auto sequentiallyRequest = [=](
  148. Request request,
  149. Participants participants) {
  150. constexpr auto kSmallDelayMs = 5;
  151. const auto participantIds = ranges::views::all(
  152. participants
  153. ) | ranges::views::transform([](not_null<PeerData*> peer) {
  154. return peer->id;
  155. }) | ranges::to_vector;
  156. const auto lifetime = std::make_shared<rpl::lifetime>();
  157. const auto counter = lifetime->make_state<int>(0);
  158. const auto timer = lifetime->make_state<base::Timer>();
  159. timer->setCallback(crl::guard(session, [=] {
  160. if ((*counter) < participantIds.size()) {
  161. const auto peer = session->data().peer(historyPeerId);
  162. const auto channel = peer ? peer->asChannel() : nullptr;
  163. const auto from = session->data().peer(
  164. participantIds[*counter]);
  165. if (channel && from) {
  166. request(from, channel);
  167. }
  168. (*counter)++;
  169. } else {
  170. lifetime->destroy();
  171. }
  172. }));
  173. timer->callEach(kSmallDelayMs);
  174. };
  175. const auto handleConfirmation = [=](
  176. not_null<Ui::Checkbox*> checkbox,
  177. not_null<Controller*> controller,
  178. Request request) {
  179. confirms->events() | rpl::start_with_next([=] {
  180. if (checkbox->checked() && controller->collectRequests) {
  181. sequentiallyRequest(request, controller->collectRequests());
  182. }
  183. }, checkbox->lifetime());
  184. };
  185. const auto isEnter = [=](not_null<QEvent*> event) {
  186. if (event->type() == QEvent::KeyPress) {
  187. if (const auto k = static_cast<QKeyEvent*>(event.get())) {
  188. return (k->key() == Qt::Key_Enter)
  189. || (k->key() == Qt::Key_Return);
  190. }
  191. }
  192. return false;
  193. };
  194. base::install_event_filter(box, [=](not_null<QEvent*> event) {
  195. if (isEnter(event)) {
  196. box->triggerButton(0);
  197. return base::EventFilterResult::Cancel;
  198. }
  199. return base::EventFilterResult::Continue;
  200. });
  201. const auto handleSubmition = [=](not_null<Ui::Checkbox*> checkbox) {
  202. base::install_event_filter(box, [=](not_null<QEvent*> event) {
  203. if (!isEnter(event) || !checkbox->checked()) {
  204. return base::EventFilterResult::Continue;
  205. }
  206. box->uiShow()->show(Ui::MakeConfirmBox({
  207. .text = tr::lng_gigagroup_warning_title(),
  208. .confirmed = [=](Fn<void()> close) {
  209. box->triggerButton(0);
  210. close();
  211. },
  212. .confirmText = tr::lng_box_yes(),
  213. .cancelText = tr::lng_box_no(),
  214. }));
  215. return base::EventFilterResult::Cancel;
  216. });
  217. };
  218. Ui::AddSkip(inner);
  219. const auto title = box->addRow(
  220. object_ptr<Ui::FlatLabel>(
  221. box,
  222. (items.size() == 1)
  223. ? tr::lng_selected_delete_sure_this()
  224. : tr::lng_selected_delete_sure(
  225. lt_count,
  226. rpl::single(items.size()) | tr::to_count()),
  227. st::boxLabel));
  228. Ui::AddSkip(inner);
  229. Ui::AddSkip(inner);
  230. Ui::AddSkip(inner);
  231. {
  232. const auto report = box->addRow(
  233. object_ptr<Ui::Checkbox>(
  234. box,
  235. tr::lng_report_spam(tr::now),
  236. false,
  237. st::defaultBoxCheckbox),
  238. st::boxRowPadding + buttonPadding);
  239. const auto controller = box->lifetime().make_state<Controller>(
  240. Controller::Data{ .participants = participants });
  241. Ui::AddExpandablePeerList(report, controller, inner);
  242. handleSubmition(report);
  243. const auto ids = items.front()->from()->owner().itemsToIds(items);
  244. handleConfirmation(report, controller, [=](
  245. not_null<PeerData*> p,
  246. not_null<ChannelData*> c) {
  247. auto filtered = ranges::views::all(
  248. ids
  249. ) | ranges::views::transform([](const FullMsgId &id) {
  250. return MTP_int(id.msg);
  251. }) | ranges::to<QVector<MTPint>>();
  252. c->session().api().request(
  253. MTPchannels_ReportSpam(
  254. c->inputChannel,
  255. p->input,
  256. MTP_vector<MTPint>(std::move(filtered)))
  257. ).send();
  258. });
  259. }
  260. if (allCanDelete) {
  261. Ui::AddSkip(inner);
  262. Ui::AddSkip(inner);
  263. const auto deleteAll = inner->add(
  264. object_ptr<Ui::Checkbox>(
  265. inner,
  266. !(isSingle)
  267. ? tr::lng_delete_all_from_users(
  268. tr::now,
  269. Ui::Text::WithEntities)
  270. : tr::lng_delete_all_from_user(
  271. tr::now,
  272. lt_user,
  273. Ui::Text::Bold(items.front()->from()->name()),
  274. Ui::Text::WithEntities),
  275. false,
  276. st::defaultBoxCheckbox),
  277. st::boxRowPadding + buttonPadding);
  278. const auto history = items.front()->history();
  279. auto messagesCounts = MessagesCountValue(history, participants);
  280. const auto controller = box->lifetime().make_state<Controller>(
  281. Controller::Data{
  282. .messagesCounts = rpl::duplicate(messagesCounts),
  283. .participants = participants,
  284. });
  285. Ui::AddExpandablePeerList(deleteAll, controller, inner);
  286. {
  287. tr::lng_selected_delete_sure(
  288. lt_count,
  289. rpl::combine(
  290. std::move(messagesCounts),
  291. isSingle
  292. ? deleteAll->checkedValue()
  293. : rpl::merge(
  294. controller->toggleRequestsFromInner.events(),
  295. controller->checkAllRequests.events())
  296. ) | rpl::map([=, s = items.size()](const auto &map, bool c) {
  297. const auto checked = (isSingle && !c)
  298. ? Participants()
  299. : controller->collectRequests
  300. ? controller->collectRequests()
  301. : Participants();
  302. auto result = 0;
  303. for (const auto &[peerId, count] : map) {
  304. for (const auto &peer : checked) {
  305. if (peer->id == peerId) {
  306. result += count;
  307. break;
  308. }
  309. }
  310. }
  311. for (const auto &item : items) {
  312. for (const auto &peer : checked) {
  313. if (peer->id == item->from()->id) {
  314. result--;
  315. break;
  316. }
  317. }
  318. result++;
  319. }
  320. return float64(result);
  321. })
  322. ) | rpl::start_with_next([=](const QString &text) {
  323. title->setText(text);
  324. title->resizeToWidth(inner->width()
  325. - rect::m::sum::h(st::boxRowPadding));
  326. }, title->lifetime());
  327. }
  328. handleSubmition(deleteAll);
  329. handleConfirmation(deleteAll, controller, [=](
  330. not_null<PeerData*> p,
  331. not_null<ChannelData*> c) {
  332. p->session().api().deleteAllFromParticipant(c, p);
  333. });
  334. }
  335. if (allCanBan) {
  336. auto ownedWrap = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
  337. inner,
  338. object_ptr<Ui::VerticalLayout>(inner));
  339. Ui::AddSkip(inner);
  340. Ui::AddSkip(inner);
  341. const auto ban = inner->add(
  342. object_ptr<Ui::Checkbox>(
  343. box,
  344. rpl::conditional(
  345. ownedWrap->toggledValue(),
  346. tr::lng_context_restrict_user(),
  347. rpl::conditional(
  348. rpl::single(isSingle),
  349. tr::lng_ban_user(),
  350. tr::lng_ban_users())),
  351. false,
  352. st::defaultBoxCheckbox),
  353. st::boxRowPadding + buttonPadding);
  354. const auto controller = box->lifetime().make_state<Controller>(
  355. Controller::Data{ .participants = participants });
  356. Ui::AddExpandablePeerList(ban, controller, inner);
  357. handleSubmition(ban);
  358. Ui::AddSkip(inner);
  359. Ui::AddSkip(inner);
  360. const auto wrap = inner->add(std::move(ownedWrap));
  361. const auto container = wrap->entity();
  362. wrap->toggle(false, anim::type::instant);
  363. const auto session = &participants.front()->session();
  364. const auto emojiMargin = QMargins(
  365. -st::moderateBoxExpandInnerSkip,
  366. -st::moderateBoxExpandInnerSkip / 2,
  367. 0,
  368. 0);
  369. const auto emojiUp = Ui::Text::SingleCustomEmoji(
  370. session->data().customEmojiManager().registerInternalEmoji(
  371. st::moderateBoxExpandIcon,
  372. emojiMargin,
  373. false));
  374. const auto emojiDown = Ui::Text::SingleCustomEmoji(
  375. session->data().customEmojiManager().registerInternalEmoji(
  376. st::moderateBoxExpandIconDown,
  377. emojiMargin,
  378. false));
  379. auto label = object_ptr<Ui::FlatLabel>(
  380. inner,
  381. QString(),
  382. st::moderateBoxDividerLabel);
  383. const auto raw = label.data();
  384. auto &lifetime = wrap->lifetime();
  385. const auto scrollLifetime = lifetime.make_state<rpl::lifetime>();
  386. label->setClickHandlerFilter([=](
  387. const ClickHandlerPtr &handler,
  388. Qt::MouseButton button) {
  389. if (button != Qt::LeftButton) {
  390. return false;
  391. }
  392. wrap->toggle(!wrap->toggled(), anim::type::normal);
  393. {
  394. inner->heightValue() | rpl::start_with_next([=] {
  395. if (!wrap->animating()) {
  396. scrollLifetime->destroy();
  397. Ui::PostponeCall(crl::guard(box, [=] {
  398. box->scrollToY(std::numeric_limits<int>::max());
  399. }));
  400. } else {
  401. box->scrollToY(std::numeric_limits<int>::max());
  402. }
  403. }, *scrollLifetime);
  404. }
  405. return true;
  406. });
  407. wrap->toggledValue(
  408. ) | rpl::map([isSingle, emojiUp, emojiDown](bool toggled) {
  409. return ((toggled && isSingle)
  410. ? tr::lng_restrict_user_part
  411. : (toggled && !isSingle)
  412. ? tr::lng_restrict_users_part
  413. : isSingle
  414. ? tr::lng_restrict_user_full
  415. : tr::lng_restrict_users_full)(
  416. lt_emoji,
  417. rpl::single(toggled ? emojiUp : emojiDown),
  418. Ui::Text::WithEntities);
  419. }) | rpl::flatten_latest(
  420. ) | rpl::start_with_next([=](const TextWithEntities &text) {
  421. raw->setMarkedText(
  422. Ui::Text::Link(text, u"internal:"_q),
  423. Core::TextContext({ .session = session }));
  424. }, label->lifetime());
  425. Ui::AddSkip(inner);
  426. inner->add(object_ptr<Ui::DividerLabel>(
  427. inner,
  428. std::move(label),
  429. st::defaultBoxDividerLabelPadding,
  430. RectPart::Top | RectPart::Bottom));
  431. using Flag = ChatRestriction;
  432. using Flags = ChatRestrictions;
  433. const auto peer = items.front()->history()->peer;
  434. const auto chat = peer->asChat();
  435. const auto channel = peer->asChannel();
  436. const auto defaultRestrictions = chat
  437. ? chat->defaultRestrictions()
  438. : channel->defaultRestrictions();
  439. const auto prepareFlags = FixDependentRestrictions(
  440. defaultRestrictions
  441. | ((channel && channel->isPublic())
  442. ? (Flag::ChangeInfo | Flag::PinMessages)
  443. : Flags(0)));
  444. const auto disabledMessages = [&] {
  445. auto result = base::flat_map<Flags, QString>();
  446. {
  447. const auto disabled = FixDependentRestrictions(
  448. defaultRestrictions
  449. | ((channel && channel->isPublic())
  450. ? (Flag::ChangeInfo | Flag::PinMessages)
  451. : Flags(0)));
  452. result.emplace(
  453. disabled,
  454. tr::lng_rights_restriction_for_all(tr::now));
  455. }
  456. return result;
  457. }();
  458. Ui::AddSubsectionTitle(
  459. inner,
  460. rpl::conditional(
  461. rpl::single(isSingle),
  462. tr::lng_restrict_users_part_single_header(),
  463. tr::lng_restrict_users_part_header(
  464. lt_count,
  465. rpl::single(participants.size()) | tr::to_count())));
  466. auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
  467. box,
  468. prepareFlags,
  469. disabledMessages,
  470. { .isForum = peer->isForum() });
  471. std::move(changes) | rpl::start_with_next([=] {
  472. ban->setChecked(true);
  473. }, ban->lifetime());
  474. Ui::AddSkip(container);
  475. Ui::AddDivider(container);
  476. Ui::AddSkip(container);
  477. container->add(std::move(checkboxes));
  478. // Handle confirmation manually.
  479. confirms->events() | rpl::start_with_next([=] {
  480. if (ban->checked() && controller->collectRequests) {
  481. const auto kick = !wrap->toggled();
  482. const auto restrictions = getRestrictions();
  483. const auto request = [=](
  484. not_null<PeerData*> peer,
  485. not_null<ChannelData*> channel) {
  486. if (!kick) {
  487. Api::ChatParticipants::Restrict(
  488. channel,
  489. peer,
  490. ChatRestrictionsInfo(), // Unused.
  491. ChatRestrictionsInfo(restrictions, 0),
  492. nullptr,
  493. nullptr);
  494. } else {
  495. channel->session().api().chatParticipants().kick(
  496. channel,
  497. peer,
  498. { channel->restrictions(), 0 });
  499. }
  500. };
  501. sequentiallyRequest(request, controller->collectRequests());
  502. }
  503. }, ban->lifetime());
  504. }
  505. const auto close = crl::guard(box, [=] { box->closeBox(); });
  506. {
  507. const auto data = &participants.front()->session().data();
  508. const auto ids = data->itemsToIds(items);
  509. box->addButton(tr::lng_box_delete(), [=] {
  510. confirms->fire({});
  511. if (confirmed) {
  512. confirmed();
  513. }
  514. data->histories().deleteMessages(ids, true);
  515. data->sendHistoryChangeNotifications();
  516. close();
  517. });
  518. }
  519. box->addButton(tr::lng_cancel(), close);
  520. }
  521. bool CanCreateModerateMessagesBox(const HistoryItemsList &items) {
  522. const auto options = CalculateModerateOptions(items);
  523. return (options.allCanBan || options.allCanDelete)
  524. && !options.participants.empty();
  525. }
  526. void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
  527. const auto container = box->verticalLayout();
  528. const auto maybeUser = peer->asUser();
  529. const auto isBot = maybeUser && maybeUser->isBot();
  530. Ui::AddSkip(container);
  531. Ui::AddSkip(container);
  532. base::install_event_filter(box, [=](not_null<QEvent*> event) {
  533. if (event->type() == QEvent::KeyPress) {
  534. if (const auto k = static_cast<QKeyEvent*>(event.get())) {
  535. if ((k->key() == Qt::Key_Enter)
  536. || (k->key() == Qt::Key_Return)) {
  537. box->uiShow()->show(Ui::MakeConfirmBox({
  538. .text = tr::lng_gigagroup_warning_title(),
  539. .confirmed = [=](Fn<void()> close) {
  540. box->triggerButton(0);
  541. close();
  542. },
  543. .confirmText = tr::lng_box_yes(),
  544. .cancelText = tr::lng_box_no(),
  545. }));
  546. }
  547. }
  548. }
  549. return base::EventFilterResult::Continue;
  550. });
  551. const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
  552. container,
  553. peer,
  554. st::mainMenuUserpic);
  555. userpic->showSavedMessagesOnSelf(true);
  556. Ui::IconWithTitle(
  557. container,
  558. userpic,
  559. Ui::CreateChild<Ui::FlatLabel>(
  560. container,
  561. peer->isSelf()
  562. ? tr::lng_saved_messages() | Ui::Text::ToBold()
  563. : maybeUser
  564. ? tr::lng_profile_delete_conversation() | Ui::Text::ToBold()
  565. : rpl::single(
  566. peer->name()
  567. ) | Ui::Text::ToBold() | rpl::type_erased(),
  568. box->getDelegate()->style().title));
  569. Ui::AddSkip(container);
  570. Ui::AddSkip(container);
  571. box->addRow(
  572. object_ptr<Ui::FlatLabel>(
  573. container,
  574. peer->isSelf()
  575. ? tr::lng_sure_delete_saved_messages()
  576. : maybeUser
  577. ? tr::lng_sure_delete_history(
  578. lt_contact,
  579. rpl::single(peer->name()))
  580. : (peer->isChannel() && !peer->isMegagroup())
  581. ? tr::lng_sure_leave_channel()
  582. : tr::lng_sure_leave_group(),
  583. st::boxLabel));
  584. const auto maybeCheckbox = [&]() -> Ui::Checkbox* {
  585. if (!peer->canRevokeFullHistory()) {
  586. return nullptr;
  587. }
  588. Ui::AddSkip(container);
  589. Ui::AddSkip(container);
  590. return box->addRow(
  591. object_ptr<Ui::Checkbox>(
  592. container,
  593. maybeUser
  594. ? tr::lng_delete_for_other_check(
  595. tr::now,
  596. lt_user,
  597. TextWithEntities{ maybeUser->firstName },
  598. Ui::Text::RichLangValue)
  599. : tr::lng_delete_for_everyone_check(
  600. tr::now,
  601. Ui::Text::WithEntities),
  602. false,
  603. st::defaultBoxCheckbox));
  604. }();
  605. const auto maybeBotCheckbox = [&]() -> Ui::Checkbox* {
  606. if (!isBot) {
  607. return nullptr;
  608. }
  609. Ui::AddSkip(container);
  610. Ui::AddSkip(container);
  611. return box->addRow(
  612. object_ptr<Ui::Checkbox>(
  613. container,
  614. tr::lng_profile_block_bot(tr::now, Ui::Text::WithEntities),
  615. false,
  616. st::defaultBoxCheckbox));
  617. }();
  618. const auto removeFromChatsFilters = [=](
  619. not_null<History*> history) -> std::vector<FilterId> {
  620. auto result = std::vector<FilterId>();
  621. for (const auto &filter : peer->owner().chatsFilters().list()) {
  622. if (filter.withoutAlways(history) != filter) {
  623. result.push_back(filter.id());
  624. }
  625. }
  626. return result;
  627. };
  628. const auto maybeChatsFiltersCheckbox = [&]() -> Ui::Checkbox* {
  629. const auto history = (isBot || !maybeUser)
  630. ? peer->owner().history(peer).get()
  631. : nullptr;
  632. if (!history || removeFromChatsFilters(history).empty()) {
  633. return nullptr;
  634. }
  635. Ui::AddSkip(container);
  636. Ui::AddSkip(container);
  637. return box->addRow(
  638. object_ptr<Ui::Checkbox>(
  639. container,
  640. (maybeBotCheckbox
  641. ? tr::lng_filters_checkbox_remove_bot
  642. : (peer->isChannel() && !peer->isMegagroup())
  643. ? tr::lng_filters_checkbox_remove_channel
  644. : tr::lng_filters_checkbox_remove_group)(
  645. tr::now,
  646. Ui::Text::WithEntities),
  647. false,
  648. st::defaultBoxCheckbox));
  649. }();
  650. Ui::AddSkip(container);
  651. auto buttonText = maybeUser
  652. ? tr::lng_box_delete()
  653. : !maybeCheckbox
  654. ? tr::lng_box_leave()
  655. : maybeCheckbox->checkedValue() | rpl::map([](bool checked) {
  656. return checked ? tr::lng_box_delete() : tr::lng_box_leave();
  657. }) | rpl::flatten_latest();
  658. const auto close = crl::guard(box, [=] { box->closeBox(); });
  659. box->addButton(std::move(buttonText), [=] {
  660. const auto revoke = maybeCheckbox && maybeCheckbox->checked();
  661. const auto stopBot = maybeBotCheckbox && maybeBotCheckbox->checked();
  662. const auto removeFromChats = maybeChatsFiltersCheckbox
  663. && maybeChatsFiltersCheckbox->checked();
  664. Core::App().closeChatFromWindows(peer);
  665. if (stopBot) {
  666. peer->session().api().blockedPeers().block(peer);
  667. }
  668. if (removeFromChats) {
  669. const auto history = peer->owner().history(peer).get();
  670. const auto removeFrom = removeFromChatsFilters(history);
  671. for (const auto &filter : peer->owner().chatsFilters().list()) {
  672. if (!ranges::contains(removeFrom, filter.id())) {
  673. continue;
  674. }
  675. const auto result = filter.withoutAlways(history);
  676. if (result == filter) {
  677. continue;
  678. }
  679. const auto tl = result.tl();
  680. peer->owner().chatsFilters().apply(MTP_updateDialogFilter(
  681. MTP_flags(MTPDupdateDialogFilter::Flag::f_filter),
  682. MTP_int(filter.id()),
  683. tl));
  684. peer->session().api().request(MTPmessages_UpdateDialogFilter(
  685. MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
  686. MTP_int(filter.id()),
  687. tl
  688. )).send();
  689. }
  690. }
  691. // Don't delete old history by default,
  692. // because Android app doesn't.
  693. //
  694. //if (const auto from = peer->migrateFrom()) {
  695. // peer->session().api().deleteConversation(from, false);
  696. //}
  697. peer->session().api().deleteConversation(peer, revoke);
  698. close();
  699. }, st::attentionBoxButton);
  700. box->addButton(tr::lng_cancel(), close);
  701. }