calls_box_controller.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  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 "calls/calls_box_controller.h"
  8. #include "lang/lang_keys.h"
  9. #include "ui/effects/ripple_animation.h"
  10. #include "ui/widgets/labels.h"
  11. #include "ui/widgets/checkbox.h"
  12. #include "ui/widgets/popup_menu.h"
  13. #include "ui/painter.h"
  14. #include "core/application.h"
  15. #include "calls/calls_instance.h"
  16. #include "history/history.h"
  17. #include "history/history_item.h"
  18. #include "history/history_item_helpers.h"
  19. #include "mainwidget.h"
  20. #include "window/window_session_controller.h"
  21. #include "main/main_session.h"
  22. #include "data/data_session.h"
  23. #include "data/data_changes.h"
  24. #include "data/data_media_types.h"
  25. #include "data/data_user.h"
  26. #include "data/data_peer_values.h" // Data::ChannelHasActiveCall.
  27. #include "data/data_group_call.h"
  28. #include "data/data_channel.h"
  29. #include "boxes/delete_messages_box.h"
  30. #include "base/unixtime.h"
  31. #include "api/api_updates.h"
  32. #include "apiwrap.h"
  33. #include "styles/style_layers.h" // st::boxLabel.
  34. #include "styles/style_calls.h"
  35. #include "styles/style_boxes.h"
  36. #include "styles/style_menu_icons.h"
  37. namespace Calls {
  38. namespace {
  39. constexpr auto kFirstPageCount = 20;
  40. constexpr auto kPerPageCount = 100;
  41. class GroupCallRow final : public PeerListRow {
  42. public:
  43. GroupCallRow(not_null<PeerData*> peer);
  44. void rightActionAddRipple(
  45. QPoint point,
  46. Fn<void()> updateCallback) override;
  47. void rightActionStopLastRipple() override;
  48. int paintNameIconGetWidth(
  49. Painter &p,
  50. Fn<void()> repaint,
  51. crl::time now,
  52. int nameLeft,
  53. int nameTop,
  54. int nameWidth,
  55. int availableWidth,
  56. int outerWidth,
  57. bool selected) override {
  58. return 0;
  59. }
  60. QSize rightActionSize() const override {
  61. return peer()->isChannel() ? QSize(_st.width, _st.height) : QSize();
  62. }
  63. QMargins rightActionMargins() const override {
  64. return QMargins(
  65. 0,
  66. 0,
  67. st::defaultPeerListItem.photoPosition.x(),
  68. 0);
  69. }
  70. void rightActionPaint(
  71. Painter &p,
  72. int x,
  73. int y,
  74. int outerWidth,
  75. bool selected,
  76. bool actionSelected) override;
  77. private:
  78. const style::IconButton &_st;
  79. std::unique_ptr<Ui::RippleAnimation> _actionRipple;
  80. };
  81. GroupCallRow::GroupCallRow(not_null<PeerData*> peer)
  82. : PeerListRow(peer)
  83. , _st(st::callGroupCall) {
  84. if (const auto channel = peer->asChannel()) {
  85. const auto status = (channel->isMegagroup()
  86. ? (channel->isPublic()
  87. ? tr::lng_create_public_channel_title
  88. : tr::lng_create_private_channel_title)
  89. : (channel->isPublic()
  90. ? tr::lng_create_public_group_title
  91. : tr::lng_create_private_group_title))(tr::now);
  92. setCustomStatus(status.toLower());
  93. }
  94. }
  95. void GroupCallRow::rightActionPaint(
  96. Painter &p,
  97. int x,
  98. int y,
  99. int outerWidth,
  100. bool selected,
  101. bool actionSelected) {
  102. auto size = rightActionSize();
  103. if (_actionRipple) {
  104. _actionRipple->paint(
  105. p,
  106. x + _st.rippleAreaPosition.x(),
  107. y + _st.rippleAreaPosition.y(),
  108. outerWidth);
  109. if (_actionRipple->empty()) {
  110. _actionRipple.reset();
  111. }
  112. }
  113. _st.icon.paintInCenter(
  114. p,
  115. style::rtlrect(x, y, size.width(), size.height(), outerWidth));
  116. }
  117. void GroupCallRow::rightActionAddRipple(
  118. QPoint point,
  119. Fn<void()> updateCallback) {
  120. if (!_actionRipple) {
  121. auto mask = Ui::RippleAnimation::EllipseMask(
  122. QSize(_st.rippleAreaSize, _st.rippleAreaSize));
  123. _actionRipple = std::make_unique<Ui::RippleAnimation>(
  124. _st.ripple,
  125. std::move(mask),
  126. std::move(updateCallback));
  127. }
  128. _actionRipple->add(point - _st.rippleAreaPosition);
  129. }
  130. void GroupCallRow::rightActionStopLastRipple() {
  131. if (_actionRipple) {
  132. _actionRipple->lastStop();
  133. }
  134. }
  135. } // namespace
  136. namespace GroupCalls {
  137. ListController::ListController(not_null<Window::SessionController*> window)
  138. : _window(window) {
  139. setStyleOverrides(&st::peerListSingleRow);
  140. }
  141. Main::Session &ListController::session() const {
  142. return _window->session();
  143. }
  144. void ListController::prepare() {
  145. const auto removeRow = [=](not_null<PeerData*> peer) {
  146. const auto it = _groupCalls.find(peer->id);
  147. if (it != end(_groupCalls)) {
  148. const auto &row = it->second;
  149. delegate()->peerListRemoveRow(row);
  150. _groupCalls.erase(it);
  151. }
  152. };
  153. const auto createRow = [=](not_null<PeerData*> peer) {
  154. const auto it = _groupCalls.find(peer->id);
  155. if (it == end(_groupCalls)) {
  156. auto row = std::make_unique<GroupCallRow>(peer);
  157. _groupCalls.emplace(peer->id, row.get());
  158. delegate()->peerListAppendRow(std::move(row));
  159. }
  160. };
  161. const auto processPeer = [=](PeerData *peer) {
  162. if (!peer) {
  163. return;
  164. }
  165. const auto channel = peer->asChannel();
  166. if (channel && Data::ChannelHasActiveCall(channel)) {
  167. createRow(peer);
  168. } else {
  169. removeRow(peer);
  170. }
  171. };
  172. const auto finishProcess = [=] {
  173. delegate()->peerListRefreshRows();
  174. _fullCount = delegate()->peerListFullRowsCount();
  175. };
  176. session().changes().peerUpdates(
  177. Data::PeerUpdate::Flag::GroupCall
  178. ) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
  179. processPeer(update.peer);
  180. finishProcess();
  181. }, lifetime());
  182. {
  183. auto count = 0;
  184. const auto list = session().data().chatsList(nullptr);
  185. for (const auto &key : list->pinned()->order()) {
  186. processPeer(key.peer());
  187. }
  188. for (const auto &key : list->indexed()->all()) {
  189. if (count > kFirstPageCount) {
  190. break;
  191. }
  192. processPeer(key->key().peer());
  193. count++;
  194. }
  195. finishProcess();
  196. }
  197. }
  198. rpl::producer<bool> ListController::shownValue() const {
  199. return _fullCount.value(
  200. ) | rpl::map(rpl::mappers::_1 > 0) | rpl::distinct_until_changed();
  201. }
  202. void ListController::rowClicked(not_null<PeerListRow*> row) {
  203. const auto window = _window;
  204. crl::on_main(window, [=, peer = row->peer()] {
  205. window->showPeerHistory(
  206. peer,
  207. Window::SectionShow::Way::ClearStack);
  208. });
  209. }
  210. void ListController::rowRightActionClicked(not_null<PeerListRow*> row) {
  211. _window->startOrJoinGroupCall(row->peer());
  212. }
  213. } // namespace GroupCalls
  214. class BoxController::Row : public PeerListRow {
  215. public:
  216. Row(not_null<HistoryItem*> item);
  217. enum class Type {
  218. Out,
  219. In,
  220. Missed,
  221. };
  222. enum class CallType {
  223. Voice,
  224. Video,
  225. };
  226. bool canAddItem(not_null<const HistoryItem*> item) const {
  227. return (ComputeType(item) == _type)
  228. && (!hasItems() || _items.front()->history() == item->history())
  229. && (ItemDateTime(item).date() == _date);
  230. }
  231. void addItem(not_null<HistoryItem*> item) {
  232. Expects(canAddItem(item));
  233. _items.push_back(item);
  234. ranges::sort(_items, [](not_null<HistoryItem*> a, auto b) {
  235. return (a->id > b->id);
  236. });
  237. refreshStatus();
  238. }
  239. void itemRemoved(not_null<const HistoryItem*> item) {
  240. if (hasItems() && item->id >= minItemId() && item->id <= maxItemId()) {
  241. _items.erase(std::remove(_items.begin(), _items.end(), item), _items.end());
  242. refreshStatus();
  243. }
  244. }
  245. [[nodiscard]] bool hasItems() const {
  246. return !_items.empty();
  247. }
  248. [[nodiscard]] MsgId minItemId() const {
  249. Expects(hasItems());
  250. return _items.back()->id;
  251. }
  252. [[nodiscard]] MsgId maxItemId() const {
  253. Expects(hasItems());
  254. return _items.front()->id;
  255. }
  256. [[nodiscard]] const std::vector<not_null<HistoryItem*>> &items() const {
  257. return _items;
  258. }
  259. void paintStatusText(
  260. Painter &p,
  261. const style::PeerListItem &st,
  262. int x,
  263. int y,
  264. int availableWidth,
  265. int outerWidth,
  266. bool selected) override;
  267. void rightActionAddRipple(
  268. QPoint point,
  269. Fn<void()> updateCallback) override;
  270. void rightActionStopLastRipple() override;
  271. int paintNameIconGetWidth(
  272. Painter &p,
  273. Fn<void()> repaint,
  274. crl::time now,
  275. int nameLeft,
  276. int nameTop,
  277. int nameWidth,
  278. int availableWidth,
  279. int outerWidth,
  280. bool selected) override {
  281. return 0;
  282. }
  283. QSize rightActionSize() const override {
  284. return peer()->isUser() ? QSize(_st->width, _st->height) : QSize();
  285. }
  286. QMargins rightActionMargins() const override {
  287. return QMargins(
  288. 0,
  289. 0,
  290. st::defaultPeerListItem.photoPosition.x(),
  291. 0);
  292. }
  293. void rightActionPaint(
  294. Painter &p,
  295. int x,
  296. int y,
  297. int outerWidth,
  298. bool selected,
  299. bool actionSelected) override;
  300. private:
  301. void refreshStatus() override;
  302. static Type ComputeType(not_null<const HistoryItem*> item);
  303. static CallType ComputeCallType(not_null<const HistoryItem*> item);
  304. std::vector<not_null<HistoryItem*>> _items;
  305. QDate _date;
  306. Type _type;
  307. not_null<const style::IconButton*> _st;
  308. std::unique_ptr<Ui::RippleAnimation> _actionRipple;
  309. };
  310. BoxController::Row::Row(not_null<HistoryItem*> item)
  311. : PeerListRow(item->history()->peer, item->id.bare)
  312. , _items(1, item)
  313. , _date(ItemDateTime(item).date())
  314. , _type(ComputeType(item))
  315. , _st(ComputeCallType(item) == CallType::Voice
  316. ? &st::callReDial
  317. : &st::callCameraReDial) {
  318. refreshStatus();
  319. }
  320. void BoxController::Row::paintStatusText(Painter &p, const style::PeerListItem &st, int x, int y, int availableWidth, int outerWidth, bool selected) {
  321. auto icon = ([this] {
  322. switch (_type) {
  323. case Type::In: return &st::callArrowIn;
  324. case Type::Out: return &st::callArrowOut;
  325. case Type::Missed: return &st::callArrowMissed;
  326. }
  327. Unexpected("_type in Calls::BoxController::Row::paintStatusText().");
  328. })();
  329. icon->paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth);
  330. auto shift = st::callArrowPosition.x() + icon->width() + st::callArrowSkip;
  331. x += shift;
  332. availableWidth -= shift;
  333. PeerListRow::paintStatusText(p, st, x, y, availableWidth, outerWidth, selected);
  334. }
  335. void BoxController::Row::rightActionPaint(
  336. Painter &p,
  337. int x,
  338. int y,
  339. int outerWidth,
  340. bool selected,
  341. bool actionSelected) {
  342. auto size = rightActionSize();
  343. if (_actionRipple) {
  344. _actionRipple->paint(
  345. p,
  346. x + _st->rippleAreaPosition.x(),
  347. y + _st->rippleAreaPosition.y(),
  348. outerWidth);
  349. if (_actionRipple->empty()) {
  350. _actionRipple.reset();
  351. }
  352. }
  353. _st->icon.paintInCenter(
  354. p,
  355. style::rtlrect(x, y, size.width(), size.height(), outerWidth));
  356. }
  357. void BoxController::Row::refreshStatus() {
  358. if (!hasItems()) {
  359. return;
  360. }
  361. auto text = [this] {
  362. auto time = QLocale().toString(ItemDateTime(_items.front()).time(), QLocale::ShortFormat);
  363. auto today = QDateTime::currentDateTime().date();
  364. if (_date == today) {
  365. return tr::lng_call_box_status_today(tr::now, lt_time, time);
  366. } else if (_date.addDays(1) == today) {
  367. return tr::lng_call_box_status_yesterday(tr::now, lt_time, time);
  368. }
  369. return tr::lng_call_box_status_date(tr::now, lt_date, langDayOfMonthFull(_date), lt_time, time);
  370. };
  371. setCustomStatus((_items.size() > 1)
  372. ? tr::lng_call_box_status_group(
  373. tr::now,
  374. lt_amount,
  375. QString::number(_items.size()),
  376. lt_status,
  377. text())
  378. : text());
  379. }
  380. BoxController::Row::Type BoxController::Row::ComputeType(
  381. not_null<const HistoryItem*> item) {
  382. if (item->out()) {
  383. return Type::Out;
  384. } else if (auto media = item->media()) {
  385. if (const auto call = media->call()) {
  386. const auto reason = call->finishReason;
  387. if (reason == Data::Call::FinishReason::Busy
  388. || reason == Data::Call::FinishReason::Missed) {
  389. return Type::Missed;
  390. }
  391. }
  392. }
  393. return Type::In;
  394. }
  395. BoxController::Row::CallType BoxController::Row::ComputeCallType(
  396. not_null<const HistoryItem*> item) {
  397. if (auto media = item->media()) {
  398. if (const auto call = media->call()) {
  399. if (call->video) {
  400. return CallType::Video;
  401. }
  402. }
  403. }
  404. return CallType::Voice;
  405. }
  406. void BoxController::Row::rightActionAddRipple(QPoint point, Fn<void()> updateCallback) {
  407. if (!_actionRipple) {
  408. auto mask = Ui::RippleAnimation::EllipseMask(
  409. QSize(_st->rippleAreaSize, _st->rippleAreaSize));
  410. _actionRipple = std::make_unique<Ui::RippleAnimation>(
  411. _st->ripple,
  412. std::move(mask),
  413. std::move(updateCallback));
  414. }
  415. _actionRipple->add(point - _st->rippleAreaPosition);
  416. }
  417. void BoxController::Row::rightActionStopLastRipple() {
  418. if (_actionRipple) {
  419. _actionRipple->lastStop();
  420. }
  421. }
  422. BoxController::BoxController(not_null<Window::SessionController*> window)
  423. : _window(window)
  424. , _api(&_window->session().mtp()) {
  425. }
  426. Main::Session &BoxController::session() const {
  427. return _window->session();
  428. }
  429. void BoxController::prepare() {
  430. session().data().itemRemoved(
  431. ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
  432. if (const auto row = rowForItem(item)) {
  433. row->itemRemoved(item);
  434. if (!row->hasItems()) {
  435. delegate()->peerListRemoveRow(row);
  436. if (!delegate()->peerListFullRowsCount()) {
  437. refreshAbout();
  438. }
  439. }
  440. delegate()->peerListRefreshRows();
  441. }
  442. }, lifetime());
  443. session().changes().messageUpdates(
  444. Data::MessageUpdate::Flag::NewAdded
  445. ) | rpl::filter([=](const Data::MessageUpdate &update) {
  446. const auto media = update.item->media();
  447. return (media != nullptr) && (media->call() != nullptr);
  448. }) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
  449. insertRow(update.item, InsertWay::Prepend);
  450. }, lifetime());
  451. delegate()->peerListSetTitle(tr::lng_call_box_title());
  452. setDescriptionText(tr::lng_contacts_loading(tr::now));
  453. delegate()->peerListRefreshRows();
  454. loadMoreRows();
  455. }
  456. void BoxController::loadMoreRows() {
  457. if (_loadRequestId || _allLoaded) {
  458. return;
  459. }
  460. _loadRequestId = _api.request(MTPmessages_Search(
  461. MTP_flags(0),
  462. MTP_inputPeerEmpty(),
  463. MTP_string(), // q
  464. MTP_inputPeerEmpty(),
  465. MTPInputPeer(), // saved_peer_id
  466. MTPVector<MTPReaction>(), // saved_reaction
  467. MTPint(), // top_msg_id
  468. MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)),
  469. MTP_int(0), // min_date
  470. MTP_int(0), // max_date
  471. MTP_int(_offsetId),
  472. MTP_int(0), // add_offset
  473. MTP_int(_offsetId ? kFirstPageCount : kPerPageCount),
  474. MTP_int(0), // max_id
  475. MTP_int(0), // min_id
  476. MTP_long(0) // hash
  477. )).done([this](const MTPmessages_Messages &result) {
  478. _loadRequestId = 0;
  479. auto handleResult = [&](auto &data) {
  480. session().data().processUsers(data.vusers());
  481. session().data().processChats(data.vchats());
  482. receivedCalls(data.vmessages().v);
  483. };
  484. switch (result.type()) {
  485. case mtpc_messages_messages: handleResult(result.c_messages_messages()); _allLoaded = true; break;
  486. case mtpc_messages_messagesSlice: handleResult(result.c_messages_messagesSlice()); break;
  487. case mtpc_messages_channelMessages: {
  488. LOG(("API Error: received messages.channelMessages! (Calls::BoxController::preloadRows)"));
  489. handleResult(result.c_messages_channelMessages());
  490. } break;
  491. case mtpc_messages_messagesNotModified: {
  492. LOG(("API Error: received messages.messagesNotModified! (Calls::BoxController::preloadRows)"));
  493. } break;
  494. default: Unexpected("Type of messages.Messages (Calls::BoxController::preloadRows)");
  495. }
  496. }).fail([this] {
  497. _loadRequestId = 0;
  498. }).send();
  499. }
  500. base::unique_qptr<Ui::PopupMenu> BoxController::rowContextMenu(
  501. QWidget *parent,
  502. not_null<PeerListRow*> row) {
  503. const auto &items = static_cast<Row*>(row.get())->items();
  504. const auto session = &this->session();
  505. const auto ids = session->data().itemsToIds(items);
  506. auto result = base::make_unique_q<Ui::PopupMenu>(
  507. parent,
  508. st::popupMenuWithIcons);
  509. result->addAction(tr::lng_context_delete_selected(tr::now), [=] {
  510. _window->show(
  511. Box<DeleteMessagesBox>(session, base::duplicate(ids)));
  512. }, &st::menuIconDelete);
  513. result->addAction(tr::lng_context_to_msg(tr::now), [=, window = _window] {
  514. if (const auto item = session->data().message(ids.front())) {
  515. window->showMessage(item);
  516. }
  517. }, &st::menuIconShowInChat);
  518. return result;
  519. }
  520. void BoxController::refreshAbout() {
  521. setDescriptionText(delegate()->peerListFullRowsCount() ? QString() : tr::lng_call_box_about(tr::now));
  522. }
  523. void BoxController::rowClicked(not_null<PeerListRow*> row) {
  524. const auto itemsRow = static_cast<Row*>(row.get());
  525. const auto itemId = itemsRow->maxItemId();
  526. const auto window = _window;
  527. crl::on_main(window, [=, peer = row->peer()] {
  528. window->showPeerHistory(
  529. peer,
  530. Window::SectionShow::Way::ClearStack,
  531. itemId);
  532. });
  533. }
  534. void BoxController::rowRightActionClicked(not_null<PeerListRow*> row) {
  535. auto user = row->peer()->asUser();
  536. Assert(user != nullptr);
  537. Core::App().calls().startOutgoingCall(user, false);
  538. }
  539. void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
  540. if (result.empty()) {
  541. _allLoaded = true;
  542. }
  543. for (const auto &message : result) {
  544. const auto msgId = IdFromMessage(message);
  545. const auto peerId = PeerFromMessage(message);
  546. if (const auto peer = session().data().peerLoaded(peerId)) {
  547. const auto item = session().data().addNewMessage(
  548. message,
  549. MessageFlags(),
  550. NewMessageType::Existing);
  551. insertRow(item, InsertWay::Append);
  552. } else {
  553. LOG(("API Error: a search results with not loaded peer %1"
  554. ).arg(peerId.value));
  555. }
  556. _offsetId = msgId;
  557. }
  558. refreshAbout();
  559. delegate()->peerListRefreshRows();
  560. }
  561. bool BoxController::insertRow(
  562. not_null<HistoryItem*> item,
  563. InsertWay way) {
  564. if (auto row = rowForItem(item)) {
  565. if (row->canAddItem(item)) {
  566. row->addItem(item);
  567. return false;
  568. }
  569. }
  570. (way == InsertWay::Append)
  571. ? delegate()->peerListAppendRow(createRow(item))
  572. : delegate()->peerListPrependRow(createRow(item));
  573. delegate()->peerListSortRows([](
  574. const PeerListRow &a,
  575. const PeerListRow &b) {
  576. return static_cast<const Row&>(a).maxItemId()
  577. > static_cast<const Row&>(b).maxItemId();
  578. });
  579. return true;
  580. }
  581. BoxController::Row *BoxController::rowForItem(not_null<const HistoryItem*> item) {
  582. auto v = delegate();
  583. if (auto fullRowsCount = v->peerListFullRowsCount()) {
  584. auto itemId = item->id;
  585. auto lastRow = static_cast<Row*>(v->peerListRowAt(fullRowsCount - 1).get());
  586. if (itemId < lastRow->minItemId()) {
  587. return lastRow;
  588. }
  589. auto firstRow = static_cast<Row*>(v->peerListRowAt(0).get());
  590. if (itemId > firstRow->maxItemId()) {
  591. return firstRow;
  592. }
  593. // Binary search. Invariant:
  594. // 1. rowAt(left)->maxItemId() >= itemId.
  595. // 2. (left + 1 == fullRowsCount) OR rowAt(left + 1)->maxItemId() < itemId.
  596. auto left = 0;
  597. auto right = fullRowsCount;
  598. while (left + 1 < right) {
  599. auto middle = (right + left) / 2;
  600. auto middleRow = static_cast<Row*>(v->peerListRowAt(middle).get());
  601. if (middleRow->maxItemId() >= itemId) {
  602. left = middle;
  603. } else {
  604. right = middle;
  605. }
  606. }
  607. auto result = static_cast<Row*>(v->peerListRowAt(left).get());
  608. // Check for rowAt(left)->minItemId > itemId > rowAt(left + 1)->maxItemId.
  609. // In that case we sometimes need to return rowAt(left + 1), not rowAt(left).
  610. if (result->minItemId() > itemId && left + 1 < fullRowsCount) {
  611. auto possibleResult = static_cast<Row*>(v->peerListRowAt(left + 1).get());
  612. Assert(possibleResult->maxItemId() < itemId);
  613. if (possibleResult->canAddItem(item)) {
  614. return possibleResult;
  615. }
  616. }
  617. return result;
  618. }
  619. return nullptr;
  620. }
  621. std::unique_ptr<PeerListRow> BoxController::createRow(
  622. not_null<HistoryItem*> item) const {
  623. return std::make_unique<Row>(item);
  624. }
  625. void ClearCallsBox(
  626. not_null<Ui::GenericBox*> box,
  627. not_null<Window::SessionController*> window) {
  628. const auto weak = Ui::MakeWeak(box);
  629. box->addRow(
  630. object_ptr<Ui::FlatLabel>(
  631. box,
  632. tr::lng_call_box_clear_sure(),
  633. st::boxLabel),
  634. st::boxPadding);
  635. const auto revokeCheckbox = box->addRow(
  636. object_ptr<Ui::Checkbox>(
  637. box,
  638. tr::lng_delete_for_everyone_check(tr::now),
  639. false,
  640. st::defaultBoxCheckbox),
  641. style::margins(
  642. st::boxPadding.left(),
  643. st::boxPadding.bottom(),
  644. st::boxPadding.right(),
  645. st::boxPadding.bottom()));
  646. const auto api = &window->session().api();
  647. const auto sendRequest = [=](bool revoke, auto self) -> void {
  648. using Flag = MTPmessages_DeletePhoneCallHistory::Flag;
  649. api->request(MTPmessages_DeletePhoneCallHistory(
  650. MTP_flags(revoke ? Flag::f_revoke : Flag(0))
  651. )).done([=](const MTPmessages_AffectedFoundMessages &result) {
  652. result.match([&](
  653. const MTPDmessages_affectedFoundMessages &data) {
  654. api->applyUpdates(MTP_updates(
  655. MTP_vector<MTPUpdate>(
  656. 1,
  657. MTP_updateDeleteMessages(
  658. data.vmessages(),
  659. data.vpts(),
  660. data.vpts_count())),
  661. MTP_vector<MTPUser>(),
  662. MTP_vector<MTPChat>(),
  663. MTP_int(base::unixtime::now()),
  664. MTP_int(0)));
  665. const auto offset = data.voffset().v;
  666. if (offset > 0) {
  667. self(revoke, self);
  668. } else {
  669. api->session().data().destroyAllCallItems();
  670. if (const auto strong = weak.data()) {
  671. strong->closeBox();
  672. }
  673. }
  674. });
  675. }).send();
  676. };
  677. box->addButton(tr::lng_call_box_clear_button(), [=] {
  678. sendRequest(revokeCheckbox->checked(), sendRequest);
  679. });
  680. box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
  681. }
  682. } // namespace Calls