edit_peer_requests_box.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  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/peers/edit_peer_requests_box.h"
  8. #include "api/api_invite_links.h"
  9. #include "apiwrap.h"
  10. #include "base/unixtime.h"
  11. #include "boxes/peer_list_controllers.h"
  12. #include "boxes/peers/edit_participants_box.h" // SubscribeToMigration
  13. #include "boxes/peers/edit_peer_invite_link.h" // PrepareRequestedRowStatus
  14. #include "boxes/peers/edit_peer_requests_box.h"
  15. #include "data/data_channel.h"
  16. #include "data/data_chat.h"
  17. #include "data/data_peer.h"
  18. #include "data/data_session.h"
  19. #include "data/data_user.h"
  20. #include "history/view/history_view_requests_bar.h" // kRecentRequestsLimit
  21. #include "info/info_controller.h"
  22. #include "info/info_memento.h"
  23. #include "info/requests_list/info_requests_list_widget.h"
  24. #include "lang/lang_keys.h"
  25. #include "main/main_session.h"
  26. #include "mtproto/sender.h"
  27. #include "ui/effects/ripple_animation.h"
  28. #include "ui/painter.h"
  29. #include "ui/round_rect.h"
  30. #include "ui/text/text_utilities.h"
  31. #include "window/window_session_controller.h"
  32. #include "styles/style_boxes.h"
  33. namespace {
  34. constexpr auto kFirstPageCount = 16;
  35. constexpr auto kPerPage = 200;
  36. constexpr auto kServerSearchDelay = crl::time(1000);
  37. constexpr auto kAcceptButton = 1;
  38. constexpr auto kRejectButton = 2;
  39. class RowDelegate {
  40. public:
  41. [[nodiscard]] virtual QSize rowAcceptButtonSize() = 0;
  42. [[nodiscard]] virtual QSize rowRejectButtonSize() = 0;
  43. virtual void rowPaintAccept(
  44. Painter &p,
  45. QRect geometry,
  46. std::unique_ptr<Ui::RippleAnimation> &ripple,
  47. int outerWidth,
  48. bool over) = 0;
  49. virtual void rowPaintReject(
  50. Painter &p,
  51. QRect geometry,
  52. std::unique_ptr<Ui::RippleAnimation> &ripple,
  53. int outerWidth,
  54. bool over) = 0;
  55. };
  56. class Row final : public PeerListRow {
  57. public:
  58. Row(
  59. not_null<RowDelegate*> delegate,
  60. not_null<UserData*> user,
  61. TimeId date);
  62. int elementsCount() const override;
  63. QRect elementGeometry(int element, int outerWidth) const override;
  64. bool elementDisabled(int element) const override;
  65. bool elementOnlySelect(int element) const override;
  66. void elementAddRipple(
  67. int element,
  68. QPoint point,
  69. Fn<void()> updateCallback) override;
  70. void elementsStopLastRipple() override;
  71. void elementsPaint(
  72. Painter &p,
  73. int outerWidth,
  74. bool selected,
  75. int selectedElement) override;
  76. private:
  77. const not_null<RowDelegate*> _delegate;
  78. std::unique_ptr<Ui::RippleAnimation> _acceptRipple;
  79. std::unique_ptr<Ui::RippleAnimation> _rejectRipple;
  80. };
  81. Row::Row(
  82. not_null<RowDelegate*> delegate,
  83. not_null<UserData*> user,
  84. TimeId date)
  85. : PeerListRow(user)
  86. , _delegate(delegate) {
  87. setCustomStatus(PrepareRequestedRowStatus(date));
  88. }
  89. int Row::elementsCount() const {
  90. return 2;
  91. }
  92. QRect Row::elementGeometry(int element, int outerWidth) const {
  93. switch (element) {
  94. case kAcceptButton: {
  95. const auto size = _delegate->rowAcceptButtonSize();
  96. return QRect(st::requestAcceptPosition, size);
  97. } break;
  98. case kRejectButton: {
  99. const auto accept = _delegate->rowAcceptButtonSize();
  100. const auto size = _delegate->rowRejectButtonSize();
  101. return QRect(
  102. (st::requestAcceptPosition
  103. + QPoint(accept.width() + st::requestButtonsSkip, 0)),
  104. size);
  105. } break;
  106. }
  107. return QRect();
  108. }
  109. bool Row::elementDisabled(int element) const {
  110. return false;
  111. }
  112. bool Row::elementOnlySelect(int element) const {
  113. return true;
  114. }
  115. void Row::elementAddRipple(
  116. int element,
  117. QPoint point,
  118. Fn<void()> updateCallback) {
  119. const auto pointer = (element == kAcceptButton)
  120. ? &_acceptRipple
  121. : (element == kRejectButton)
  122. ? &_rejectRipple
  123. : nullptr;
  124. if (!pointer) {
  125. return;
  126. }
  127. auto &ripple = *pointer;
  128. if (!ripple) {
  129. auto mask = Ui::RippleAnimation::RoundRectMask(
  130. (element == kAcceptButton
  131. ? _delegate->rowAcceptButtonSize()
  132. : _delegate->rowRejectButtonSize()),
  133. st::buttonRadius);
  134. ripple = std::make_unique<Ui::RippleAnimation>(
  135. (element == kAcceptButton
  136. ? st::requestsAcceptButton.ripple
  137. : st::requestsRejectButton.ripple),
  138. std::move(mask),
  139. std::move(updateCallback));
  140. }
  141. ripple->add(point);
  142. }
  143. void Row::elementsStopLastRipple() {
  144. if (_acceptRipple) {
  145. _acceptRipple->lastStop();
  146. }
  147. if (_rejectRipple) {
  148. _rejectRipple->lastStop();
  149. }
  150. }
  151. void Row::elementsPaint(
  152. Painter &p,
  153. int outerWidth,
  154. bool selected,
  155. int selectedElement) {
  156. const auto accept = elementGeometry(kAcceptButton, outerWidth);
  157. const auto reject = elementGeometry(kRejectButton, outerWidth);
  158. const auto over = [&](int element) {
  159. return (selectedElement == element);
  160. };
  161. _delegate->rowPaintAccept(
  162. p,
  163. accept,
  164. _acceptRipple,
  165. outerWidth,
  166. over(kAcceptButton));
  167. _delegate->rowPaintReject(
  168. p,
  169. reject,
  170. _rejectRipple,
  171. outerWidth,
  172. over(kRejectButton));
  173. }
  174. } // namespace
  175. class RequestsBoxController::RowHelper final : public RowDelegate {
  176. public:
  177. explicit RowHelper(bool isGroup);
  178. [[nodiscard]] QSize rowAcceptButtonSize() override;
  179. [[nodiscard]] QSize rowRejectButtonSize() override;
  180. void rowPaintAccept(
  181. Painter &p,
  182. QRect geometry,
  183. std::unique_ptr<Ui::RippleAnimation> &ripple,
  184. int outerWidth,
  185. bool over) override;
  186. void rowPaintReject(
  187. Painter &p,
  188. QRect geometry,
  189. std::unique_ptr<Ui::RippleAnimation> &ripple,
  190. int outerWidth,
  191. bool over) override;
  192. private:
  193. void paintButton(
  194. Painter &p,
  195. QRect geometry,
  196. const style::RoundButton &st,
  197. const Ui::RoundRect &rect,
  198. const Ui::RoundRect &rectOver,
  199. std::unique_ptr<Ui::RippleAnimation> &ripple,
  200. const QString &text,
  201. int textWidth,
  202. int outerWidth,
  203. bool over);
  204. Ui::RoundRect _acceptRect;
  205. Ui::RoundRect _acceptRectOver;
  206. Ui::RoundRect _rejectRect;
  207. Ui::RoundRect _rejectRectOver;
  208. QString _acceptText;
  209. QString _rejectText;
  210. int _acceptTextWidth = 0;
  211. int _rejectTextWidth = 0;
  212. };
  213. RequestsBoxController::RowHelper::RowHelper(bool isGroup)
  214. : _acceptRect(st::buttonRadius, st::requestsAcceptButton.textBg)
  215. , _acceptRectOver(st::buttonRadius, st::requestsAcceptButton.textBgOver)
  216. , _rejectRect(st::buttonRadius, st::requestsRejectButton.textBg)
  217. , _rejectRectOver(st::buttonRadius, st::requestsRejectButton.textBgOver)
  218. , _acceptText(isGroup
  219. ? tr::lng_group_requests_add(tr::now)
  220. : tr::lng_group_requests_add_channel(tr::now))
  221. , _rejectText(tr::lng_group_requests_dismiss(tr::now))
  222. , _acceptTextWidth(st::requestsAcceptButton.style.font->width(_acceptText))
  223. , _rejectTextWidth(st::requestsRejectButton.style.font->width(_rejectText)) {
  224. }
  225. RequestsBoxController::RequestsBoxController(
  226. not_null<Window::SessionNavigation*> navigation,
  227. not_null<PeerData*> peer)
  228. : PeerListController(CreateSearchController(peer))
  229. , _navigation(navigation)
  230. , _helper(std::make_unique<RowHelper>(!peer->isBroadcast()))
  231. , _peer(peer)
  232. , _api(&_peer->session().mtp()) {
  233. setStyleOverrides(&st::requestsBoxList);
  234. subscribeToMigration();
  235. }
  236. RequestsBoxController::~RequestsBoxController() = default;
  237. void RequestsBoxController::Start(
  238. not_null<Window::SessionNavigation*> navigation,
  239. not_null<PeerData*> peer) {
  240. navigation->showSection(
  241. std::make_shared<Info::Memento>(
  242. peer->migrateToOrMe(),
  243. Info::Section::Type::RequestsList));
  244. }
  245. Main::Session &RequestsBoxController::session() const {
  246. return _peer->session();
  247. }
  248. auto RequestsBoxController::CreateSearchController(not_null<PeerData*> peer)
  249. -> std::unique_ptr<PeerListSearchController> {
  250. return std::make_unique<RequestsBoxSearchController>(peer);
  251. }
  252. std::unique_ptr<PeerListRow> RequestsBoxController::createSearchRow(
  253. not_null<PeerData*> peer) {
  254. if (const auto user = peer->asUser()) {
  255. return createRow(user);
  256. }
  257. return nullptr;
  258. }
  259. std::unique_ptr<PeerListRow> RequestsBoxController::createRestoredRow(
  260. not_null<PeerData*> peer) {
  261. if (const auto user = peer->asUser()) {
  262. return createRow(user, _dates[user]);
  263. }
  264. return nullptr;
  265. }
  266. auto RequestsBoxController::saveState() const
  267. -> std::unique_ptr<PeerListState> {
  268. auto result = PeerListController::saveState();
  269. auto my = std::make_unique<SavedState>();
  270. my->dates = _dates;
  271. my->offsetDate = _offsetDate;
  272. my->offsetUser = _offsetUser;
  273. my->allLoaded = _allLoaded;
  274. my->wasLoading = (_loadRequestId != 0);
  275. if (const auto search = searchController()) {
  276. my->searchState = search->saveState();
  277. }
  278. result->controllerState = std::move(my);
  279. return result;
  280. }
  281. void RequestsBoxController::restoreState(
  282. std::unique_ptr<PeerListState> state) {
  283. auto typeErasedState = state
  284. ? state->controllerState.get()
  285. : nullptr;
  286. if (const auto my = dynamic_cast<SavedState*>(typeErasedState)) {
  287. if (const auto requestId = base::take(_loadRequestId)) {
  288. _api.request(requestId).cancel();
  289. }
  290. _dates = std::move(my->dates);
  291. _offsetDate = my->offsetDate;
  292. _offsetUser = my->offsetUser;
  293. _allLoaded = my->allLoaded;
  294. if (const auto search = searchController()) {
  295. search->restoreState(std::move(my->searchState));
  296. }
  297. if (my->wasLoading) {
  298. loadMoreRows();
  299. }
  300. PeerListController::restoreState(std::move(state));
  301. if (delegate()->peerListFullRowsCount() || _allLoaded) {
  302. refreshDescription();
  303. delegate()->peerListRefreshRows();
  304. }
  305. }
  306. }
  307. void RequestsBoxController::prepare() {
  308. delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
  309. delegate()->peerListSetTitle(_peer->isBroadcast()
  310. ? tr::lng_manage_peer_requests_channel()
  311. : tr::lng_manage_peer_requests());
  312. setDescriptionText(tr::lng_contacts_loading(tr::now));
  313. setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
  314. loadMoreRows();
  315. }
  316. void RequestsBoxController::loadMoreRows() {
  317. if (searchController() && searchController()->loadMoreRows()) {
  318. return;
  319. } else if (_loadRequestId || _allLoaded) {
  320. return;
  321. }
  322. // First query is small and fast, next loads a lot of rows.
  323. const auto limit = _offsetDate ? kPerPage : kFirstPageCount;
  324. using Flag = MTPmessages_GetChatInviteImporters::Flag;
  325. _loadRequestId = _api.request(MTPmessages_GetChatInviteImporters(
  326. MTP_flags(Flag::f_requested),
  327. _peer->input,
  328. MTPstring(), // link
  329. MTPstring(), // q
  330. MTP_int(_offsetDate),
  331. _offsetUser ? _offsetUser->inputUser : MTP_inputUserEmpty(),
  332. MTP_int(limit)
  333. )).done([=](const MTPmessages_ChatInviteImporters &result) {
  334. const auto firstLoad = !_offsetDate;
  335. _loadRequestId = 0;
  336. result.match([&](const MTPDmessages_chatInviteImporters &data) {
  337. session().data().processUsers(data.vusers());
  338. const auto &importers = data.vimporters().v;
  339. auto &owner = _peer->owner();
  340. for (const auto &importer : importers) {
  341. importer.match([&](const MTPDchatInviteImporter &data) {
  342. _offsetDate = data.vdate().v;
  343. _offsetUser = owner.user(data.vuser_id());
  344. appendRow(_offsetUser, _offsetDate);
  345. });
  346. }
  347. // To be sure - wait for a whole empty result list.
  348. _allLoaded = importers.isEmpty();
  349. });
  350. if (_allLoaded
  351. || (firstLoad && delegate()->peerListFullRowsCount() > 0)) {
  352. refreshDescription();
  353. }
  354. delegate()->peerListRefreshRows();
  355. }).fail([=] {
  356. _loadRequestId = 0;
  357. _allLoaded = true;
  358. }).send();
  359. }
  360. void RequestsBoxController::refreshDescription() {
  361. setDescriptionText((delegate()->peerListFullRowsCount() > 0)
  362. ? QString()
  363. : _peer->isBroadcast()
  364. ? tr::lng_group_requests_none_channel(tr::now)
  365. : tr::lng_group_requests_none(tr::now));
  366. }
  367. void RequestsBoxController::rowClicked(not_null<PeerListRow*> row) {
  368. _navigation->showPeerInfo(row->peer());
  369. }
  370. void RequestsBoxController::rowElementClicked(
  371. not_null<PeerListRow*> row,
  372. int element) {
  373. processRequest(row->peer()->asUser(), (element == kAcceptButton));
  374. }
  375. void RequestsBoxController::processRequest(
  376. not_null<UserData*> user,
  377. bool approved) {
  378. const auto remove = [=] {
  379. if (const auto row = delegate()->peerListFindRow(user->id.value)) {
  380. delegate()->peerListRemoveRow(row);
  381. refreshDescription();
  382. delegate()->peerListRefreshRows();
  383. }
  384. static_cast<RequestsBoxSearchController*>(
  385. searchController())->removeFromCache(user);
  386. };
  387. const auto done = crl::guard(this, [=] {
  388. remove();
  389. if (approved) {
  390. delegate()->peerListUiShow()->showToast((_peer->isBroadcast()
  391. ? tr::lng_group_requests_was_added_channel
  392. : tr::lng_group_requests_was_added)(
  393. tr::now,
  394. lt_user,
  395. Ui::Text::Bold(user->name()),
  396. Ui::Text::WithEntities));
  397. }
  398. });
  399. const auto fail = crl::guard(this, remove);
  400. session().api().inviteLinks().processRequest(
  401. _peer,
  402. QString(), // link
  403. user,
  404. approved,
  405. done,
  406. fail);
  407. }
  408. void RequestsBoxController::appendRow(
  409. not_null<UserData*> user,
  410. TimeId date) {
  411. if (!delegate()->peerListFindRow(user->id.value)) {
  412. _dates.emplace(user, date);
  413. if (auto row = createRow(user, date)) {
  414. delegate()->peerListAppendRow(std::move(row));
  415. setDescriptionText(QString());
  416. }
  417. }
  418. }
  419. QSize RequestsBoxController::RowHelper::rowAcceptButtonSize() {
  420. const auto &st = st::requestsAcceptButton;
  421. return {
  422. (st.width <= 0) ? (_acceptTextWidth - st.width) : st.width,
  423. st.height,
  424. };
  425. }
  426. QSize RequestsBoxController::RowHelper::rowRejectButtonSize() {
  427. const auto &st = st::requestsRejectButton;
  428. return {
  429. (st.width <= 0) ? (_rejectTextWidth - st.width) : st.width,
  430. st.height,
  431. };
  432. }
  433. void RequestsBoxController::RowHelper::rowPaintAccept(
  434. Painter &p,
  435. QRect geometry,
  436. std::unique_ptr<Ui::RippleAnimation> &ripple,
  437. int outerWidth,
  438. bool over) {
  439. paintButton(
  440. p,
  441. geometry,
  442. st::requestsAcceptButton,
  443. _acceptRect,
  444. _acceptRectOver,
  445. ripple,
  446. _acceptText,
  447. _acceptTextWidth,
  448. outerWidth,
  449. over);
  450. }
  451. void RequestsBoxController::RowHelper::rowPaintReject(
  452. Painter &p,
  453. QRect geometry,
  454. std::unique_ptr<Ui::RippleAnimation> &ripple,
  455. int outerWidth,
  456. bool over) {
  457. paintButton(
  458. p,
  459. geometry,
  460. st::requestsRejectButton,
  461. _rejectRect,
  462. _rejectRectOver,
  463. ripple,
  464. _rejectText,
  465. _rejectTextWidth,
  466. outerWidth,
  467. over);
  468. }
  469. void RequestsBoxController::RowHelper::paintButton(
  470. Painter &p,
  471. QRect geometry,
  472. const style::RoundButton &st,
  473. const Ui::RoundRect &rect,
  474. const Ui::RoundRect &rectOver,
  475. std::unique_ptr<Ui::RippleAnimation> &ripple,
  476. const QString &text,
  477. int textWidth,
  478. int outerWidth,
  479. bool over) {
  480. rect.paint(p, geometry);
  481. if (over) {
  482. rectOver.paint(p, geometry);
  483. }
  484. if (ripple) {
  485. ripple->paint(p, geometry.x(), geometry.y(), outerWidth);
  486. if (ripple->empty()) {
  487. ripple = nullptr;
  488. }
  489. }
  490. const auto textLeft = geometry.x()
  491. + ((geometry.width() - textWidth) / 2);
  492. const auto textTop = geometry.y() + st.textTop;
  493. p.setFont(st.style.font);
  494. p.setPen(over ? st.textFgOver : st.textFg);
  495. p.drawTextLeft(textLeft, textTop, outerWidth, text);
  496. }
  497. std::unique_ptr<PeerListRow> RequestsBoxController::createRow(
  498. not_null<UserData*> user,
  499. TimeId date) {
  500. if (!date) {
  501. const auto search = static_cast<RequestsBoxSearchController*>(
  502. searchController());
  503. date = search->dateForUser(user);
  504. _dates.emplace(user, date);
  505. }
  506. return std::make_unique<Row>(_helper.get(), user, date);
  507. }
  508. void RequestsBoxController::subscribeToMigration() {
  509. const auto chat = _peer->asChat();
  510. if (!chat) {
  511. return;
  512. }
  513. SubscribeToMigration(
  514. chat,
  515. lifetime(),
  516. [=](not_null<ChannelData*> channel) { migrate(chat, channel); });
  517. }
  518. void RequestsBoxController::migrate(
  519. not_null<ChatData*> chat,
  520. not_null<ChannelData*> channel) {
  521. _peer = channel;
  522. }
  523. RequestsBoxSearchController::RequestsBoxSearchController(
  524. not_null<PeerData*> peer)
  525. : _peer(peer)
  526. , _api(&_peer->session().mtp()) {
  527. _timer.setCallback([=] { searchOnServer(); });
  528. }
  529. void RequestsBoxSearchController::searchQuery(const QString &query) {
  530. if (_query != query) {
  531. _query = query;
  532. _offsetDate = 0;
  533. _offsetUser = nullptr;
  534. _requestId = 0;
  535. _allLoaded = false;
  536. if (!_query.isEmpty() && !searchInCache()) {
  537. _timer.callOnce(kServerSearchDelay);
  538. } else {
  539. _timer.cancel();
  540. }
  541. }
  542. }
  543. void RequestsBoxSearchController::searchOnServer() {
  544. Expects(!_query.isEmpty());
  545. loadMoreRows();
  546. }
  547. bool RequestsBoxSearchController::isLoading() {
  548. return _timer.isActive() || _requestId;
  549. }
  550. void RequestsBoxSearchController::removeFromCache(not_null<UserData*> user) {
  551. for (auto &entry : _cache) {
  552. auto &items = entry.second.items;
  553. const auto j = ranges::remove(items, user, &Item::user);
  554. if (j != end(items)) {
  555. entry.second.requestedCount -= (end(items) - j);
  556. items.erase(j, end(items));
  557. }
  558. }
  559. }
  560. TimeId RequestsBoxSearchController::dateForUser(not_null<UserData*> user) {
  561. if (const auto i = _dates.find(user); i != end(_dates)) {
  562. return i->second;
  563. }
  564. return {};
  565. }
  566. auto RequestsBoxSearchController::saveState() const
  567. -> std::unique_ptr<SavedStateBase> {
  568. auto result = std::make_unique<SavedState>();
  569. result->query = _query;
  570. result->offsetDate = _offsetDate;
  571. result->offsetUser = _offsetUser;
  572. result->allLoaded = _allLoaded;
  573. result->wasLoading = (_requestId != 0);
  574. return result;
  575. }
  576. void RequestsBoxSearchController::restoreState(
  577. std::unique_ptr<SavedStateBase> state) {
  578. if (auto my = dynamic_cast<SavedState*>(state.get())) {
  579. if (auto requestId = base::take(_requestId)) {
  580. _api.request(requestId).cancel();
  581. }
  582. _cache.clear();
  583. _queries.clear();
  584. _allLoaded = my->allLoaded;
  585. _offsetDate = my->offsetDate;
  586. _offsetUser = my->offsetUser;
  587. _query = my->query;
  588. if (my->wasLoading) {
  589. searchOnServer();
  590. }
  591. }
  592. }
  593. bool RequestsBoxSearchController::searchInCache() {
  594. const auto i = _cache.find(_query);
  595. if (i != _cache.cend()) {
  596. _requestId = 0;
  597. searchDone(
  598. _requestId,
  599. i->second.items,
  600. i->second.requestedCount);
  601. return true;
  602. }
  603. return false;
  604. }
  605. bool RequestsBoxSearchController::loadMoreRows() {
  606. if (_query.isEmpty()) {
  607. return false;
  608. } else if (_allLoaded || isLoading()) {
  609. return true;
  610. }
  611. // For search we request a lot of rows from the first query.
  612. // (because we've waited for search request by timer already,
  613. // so we don't expect it to be fast, but we want to fill cache).
  614. const auto limit = kPerPage;
  615. using Flag = MTPmessages_GetChatInviteImporters::Flag;
  616. _requestId = _api.request(MTPmessages_GetChatInviteImporters(
  617. MTP_flags(Flag::f_requested | Flag::f_q),
  618. _peer->input,
  619. MTPstring(), // link
  620. MTP_string(_query),
  621. MTP_int(_offsetDate),
  622. _offsetUser ? _offsetUser->inputUser : MTP_inputUserEmpty(),
  623. MTP_int(limit)
  624. )).done([=](
  625. const MTPmessages_ChatInviteImporters &result,
  626. mtpRequestId requestId) {
  627. auto items = std::vector<Item>();
  628. result.match([&](const MTPDmessages_chatInviteImporters &data) {
  629. const auto &importers = data.vimporters().v;
  630. auto &owner = _peer->owner();
  631. owner.processUsers(data.vusers());
  632. items.reserve(importers.size());
  633. for (const auto &importer : importers) {
  634. importer.match([&](const MTPDchatInviteImporter &data) {
  635. items.push_back({
  636. owner.user(data.vuser_id()),
  637. data.vdate().v,
  638. });
  639. });
  640. }
  641. });
  642. searchDone(requestId, items, limit);
  643. auto it = _queries.find(requestId);
  644. if (it != _queries.cend()) {
  645. const auto &query = it->second.text;
  646. if (it->second.offsetDate == 0) {
  647. auto &entry = _cache[query];
  648. entry.items = std::move(items);
  649. entry.requestedCount = limit;
  650. }
  651. _queries.erase(it);
  652. }
  653. }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
  654. if (_requestId == requestId) {
  655. _requestId = 0;
  656. _allLoaded = true;
  657. delegate()->peerListSearchRefreshRows();
  658. }
  659. }).send();
  660. auto entry = Query();
  661. entry.text = _query;
  662. entry.offsetDate = _offsetDate;
  663. _queries.emplace(_requestId, entry);
  664. return true;
  665. }
  666. void RequestsBoxSearchController::searchDone(
  667. mtpRequestId requestId,
  668. const std::vector<Item> &items,
  669. int requestedCount) {
  670. if (_requestId != requestId) {
  671. return;
  672. }
  673. _requestId = 0;
  674. if (!_offsetDate) {
  675. _dates.clear();
  676. }
  677. for (const auto &[user, date] : items) {
  678. _offsetDate = date;
  679. _offsetUser = user;
  680. _dates.emplace(user, date);
  681. delegate()->peerListSearchAddRow(user);
  682. }
  683. if (items.size() < requestedCount) {
  684. // We want cache to have full information about a query with
  685. // small results count (that we don't need the second request).
  686. // So we don't wait for empty list unlike the non-search case.
  687. _allLoaded = true;
  688. }
  689. delegate()->peerListSearchRefreshRows();
  690. }