info_polls_results_inner_widget.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  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 "info/polls/info_polls_results_inner_widget.h"
  8. #include "info/polls/info_polls_results_widget.h"
  9. #include "lang/lang_keys.h"
  10. #include "core/ui_integration.h"
  11. #include "data/data_peer.h"
  12. #include "data/data_poll.h"
  13. #include "data/data_session.h"
  14. #include "ui/controls/peer_list_dummy.h"
  15. #include "ui/widgets/buttons.h"
  16. #include "ui/wrap/vertical_layout.h"
  17. #include "ui/wrap/slide_wrap.h"
  18. #include "ui/text/text_utilities.h"
  19. #include "ui/vertical_list.h"
  20. #include "boxes/peer_list_box.h"
  21. #include "main/main_session.h"
  22. #include "history/history.h"
  23. #include "history/history_item.h"
  24. #include "styles/style_layers.h"
  25. #include "styles/style_info.h"
  26. namespace Info::Polls {
  27. namespace {
  28. constexpr auto kFirstPage = 15;
  29. constexpr auto kPerPage = 50;
  30. constexpr auto kLeavePreloaded = 5;
  31. class ListDelegate final : public PeerListContentDelegate {
  32. public:
  33. void peerListSetTitle(rpl::producer<QString> title) override;
  34. void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
  35. bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
  36. int peerListSelectedRowsCount() override;
  37. void peerListScrollToTop() override;
  38. void peerListAddSelectedPeerInBunch(
  39. not_null<PeerData*> peer) override;
  40. void peerListAddSelectedRowInBunch(
  41. not_null<PeerListRow*> row) override;
  42. void peerListFinishSelectedRowsBunch() override;
  43. void peerListSetDescription(
  44. object_ptr<Ui::FlatLabel> description) override;
  45. std::shared_ptr<Main::SessionShow> peerListUiShow() override;
  46. };
  47. void ListDelegate::peerListSetTitle(rpl::producer<QString> title) {
  48. }
  49. void ListDelegate::peerListSetAdditionalTitle(rpl::producer<QString> title) {
  50. }
  51. bool ListDelegate::peerListIsRowChecked(not_null<PeerListRow*> row) {
  52. return false;
  53. }
  54. int ListDelegate::peerListSelectedRowsCount() {
  55. return 0;
  56. }
  57. void ListDelegate::peerListScrollToTop() {
  58. }
  59. void ListDelegate::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
  60. Unexpected("Item selection in Info::Profile::Members.");
  61. }
  62. void ListDelegate::peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) {
  63. Unexpected("Item selection in Info::Profile::Members.");
  64. }
  65. void ListDelegate::peerListFinishSelectedRowsBunch() {
  66. }
  67. void ListDelegate::peerListSetDescription(
  68. object_ptr<Ui::FlatLabel> description) {
  69. description.destroy();
  70. }
  71. std::shared_ptr<Main::SessionShow> ListDelegate::peerListUiShow() {
  72. Unexpected("...ListDelegate::peerListUiShow");
  73. }
  74. } // namespace
  75. class ListController final : public PeerListController {
  76. public:
  77. ListController(
  78. not_null<Main::Session*> session,
  79. not_null<PollData*> poll,
  80. FullMsgId context,
  81. QByteArray option);
  82. Main::Session &session() const override;
  83. void prepare() override;
  84. void rowClicked(not_null<PeerListRow*> row) override;
  85. void loadMoreRows() override;
  86. void allowLoadMore();
  87. void collapse();
  88. [[nodiscard]] auto showPeerInfoRequests() const
  89. -> rpl::producer<not_null<PeerData*>>;
  90. [[nodiscard]] rpl::producer<int> scrollToRequests() const;
  91. [[nodiscard]] rpl::producer<int> count() const;
  92. [[nodiscard]] rpl::producer<int> fullCount() const;
  93. [[nodiscard]] rpl::producer<int> loadMoreCount() const;
  94. std::unique_ptr<PeerListState> saveState() const override;
  95. void restoreState(std::unique_ptr<PeerListState> state) override;
  96. std::unique_ptr<PeerListRow> createRestoredRow(
  97. not_null<PeerData*> peer) override;
  98. void scrollTo(int y);
  99. private:
  100. struct SavedState : SavedStateBase {
  101. QString offset;
  102. QString loadForOffset;
  103. int leftToLoad = 0;
  104. int fullCount = 0;
  105. std::vector<not_null<PeerData*>> preloaded;
  106. bool wasLoading = false;
  107. };
  108. bool appendRow(not_null<PeerData*> peer);
  109. std::unique_ptr<PeerListRow> createRow(not_null<PeerData*> peer) const;
  110. void addPreloaded();
  111. bool addPreloadedPage();
  112. void preloadedAdded();
  113. const not_null<Main::Session*> _session;
  114. const not_null<PollData*> _poll;
  115. const FullMsgId _context;
  116. const QByteArray _option;
  117. MTP::Sender _api;
  118. QString _offset;
  119. mtpRequestId _loadRequestId = 0;
  120. QString _loadForOffset;
  121. std::vector<not_null<PeerData*>> _preloaded;
  122. rpl::variable<int> _count = 0;
  123. rpl::variable<int> _fullCount;
  124. rpl::variable<int> _leftToLoad;
  125. rpl::event_stream<not_null<PeerData*>> _showPeerInfoRequests;
  126. rpl::event_stream<int> _scrollToRequests;
  127. };
  128. ListController::ListController(
  129. not_null<Main::Session*> session,
  130. not_null<PollData*> poll,
  131. FullMsgId context,
  132. QByteArray option)
  133. : _session(session)
  134. , _poll(poll)
  135. , _context(context)
  136. , _option(option)
  137. , _api(&_session->mtp()) {
  138. const auto i = ranges::find(poll->answers, option, &PollAnswer::option);
  139. Assert(i != poll->answers.end());
  140. _fullCount = i->votes;
  141. _leftToLoad = i->votes;
  142. }
  143. Main::Session &ListController::session() const {
  144. return *_session;
  145. }
  146. void ListController::prepare() {
  147. delegate()->peerListRefreshRows();
  148. }
  149. void ListController::loadMoreRows() {
  150. if (_loadRequestId
  151. || !_leftToLoad.current()
  152. || (!_offset.isEmpty() && _loadForOffset != _offset)
  153. || !_preloaded.empty()) {
  154. return;
  155. }
  156. const auto item = session().data().message(_context);
  157. if (!item || !item->isRegular()) {
  158. _leftToLoad = 0;
  159. return;
  160. }
  161. using Flag = MTPmessages_GetPollVotes::Flag;
  162. const auto flags = Flag::f_option
  163. | (_offset.isEmpty() ? Flag(0) : Flag::f_offset);
  164. const auto limit = _offset.isEmpty() ? kFirstPage : kPerPage;
  165. _loadRequestId = _api.request(MTPmessages_GetPollVotes(
  166. MTP_flags(flags),
  167. item->history()->peer->input,
  168. MTP_int(item->id),
  169. MTP_bytes(_option),
  170. MTP_string(_offset),
  171. MTP_int(limit)
  172. )).done([=](const MTPmessages_VotesList &result) {
  173. const auto count = result.match([&](
  174. const MTPDmessages_votesList &data) {
  175. _offset = data.vnext_offset().value_or_empty();
  176. auto &owner = session().data();
  177. owner.processUsers(data.vusers());
  178. owner.processChats(data.vchats());
  179. auto add = limit - kLeavePreloaded;
  180. for (const auto &vote : data.vvotes().v) {
  181. vote.match([&](const auto &data) {
  182. const auto peer = owner.peer(peerFromMTP(data.vpeer()));
  183. if (peer->isMinimalLoaded()) {
  184. if (add) {
  185. appendRow(peer);
  186. --add;
  187. } else {
  188. _preloaded.push_back(peer);
  189. }
  190. }
  191. });
  192. }
  193. return data.vcount().v;
  194. });
  195. if (_offset.isEmpty()) {
  196. addPreloaded();
  197. _fullCount = delegate()->peerListFullRowsCount();
  198. _leftToLoad = 0;
  199. } else {
  200. _count = delegate()->peerListFullRowsCount();
  201. _fullCount = count;
  202. _leftToLoad = count - delegate()->peerListFullRowsCount();
  203. delegate()->peerListRefreshRows();
  204. }
  205. _loadRequestId = 0;
  206. }).fail([=] {
  207. _loadRequestId = 0;
  208. }).send();
  209. }
  210. void ListController::allowLoadMore() {
  211. if (!addPreloadedPage()) {
  212. _loadForOffset = _offset;
  213. addPreloaded();
  214. loadMoreRows();
  215. }
  216. }
  217. void ListController::collapse() {
  218. const auto count = delegate()->peerListFullRowsCount();
  219. if (count <= kFirstPage) {
  220. return;
  221. }
  222. const auto remove = count - (kFirstPage - kLeavePreloaded);
  223. ranges::actions::reverse(_preloaded);
  224. _preloaded.reserve(_preloaded.size() + remove);
  225. for (auto i = 0; i != remove; ++i) {
  226. const auto row = delegate()->peerListRowAt(count - i - 1);
  227. _preloaded.push_back(row->peer());
  228. delegate()->peerListRemoveRow(row);
  229. }
  230. ranges::actions::reverse(_preloaded);
  231. delegate()->peerListRefreshRows();
  232. const auto now = count - remove;
  233. _count = now;
  234. _leftToLoad = _fullCount.current() - now;
  235. }
  236. void ListController::addPreloaded() {
  237. for (const auto peer : base::take(_preloaded)) {
  238. appendRow(peer);
  239. }
  240. preloadedAdded();
  241. }
  242. bool ListController::addPreloadedPage() {
  243. if (_preloaded.size() < kPerPage + kLeavePreloaded) {
  244. return false;
  245. }
  246. const auto from = begin(_preloaded);
  247. const auto till = from + kPerPage;
  248. for (auto i = from; i != till; ++i) {
  249. appendRow(*i);
  250. }
  251. _preloaded.erase(from, till);
  252. preloadedAdded();
  253. return true;
  254. }
  255. void ListController::preloadedAdded() {
  256. _count = delegate()->peerListFullRowsCount();
  257. _leftToLoad = _fullCount.current() - _count.current();
  258. delegate()->peerListRefreshRows();
  259. }
  260. auto ListController::showPeerInfoRequests() const
  261. -> rpl::producer<not_null<PeerData*>> {
  262. return _showPeerInfoRequests.events();
  263. }
  264. rpl::producer<int> ListController::scrollToRequests() const {
  265. return _scrollToRequests.events();
  266. }
  267. rpl::producer<int> ListController::count() const {
  268. return _count.value();
  269. }
  270. rpl::producer<int> ListController::fullCount() const {
  271. return _fullCount.value();
  272. }
  273. rpl::producer<int> ListController::loadMoreCount() const {
  274. const auto initial = (_fullCount.current() <= kFirstPage)
  275. ? _fullCount.current()
  276. : (kFirstPage - kLeavePreloaded);
  277. return rpl::combine(
  278. _count.value(),
  279. _leftToLoad.value()
  280. ) | rpl::map([=](int count, int leftToLoad) {
  281. return (count > 0) ? leftToLoad : (leftToLoad - initial);
  282. });
  283. }
  284. auto ListController::saveState() const -> std::unique_ptr<PeerListState> {
  285. auto result = PeerListController::saveState();
  286. auto my = std::make_unique<SavedState>();
  287. my->offset = _offset;
  288. my->fullCount = _fullCount.current();
  289. my->leftToLoad = _leftToLoad.current();
  290. my->preloaded = _preloaded;
  291. my->wasLoading = (_loadRequestId != 0);
  292. my->loadForOffset = _loadForOffset;
  293. result->controllerState = std::move(my);
  294. return result;
  295. }
  296. void ListController::restoreState(std::unique_ptr<PeerListState> state) {
  297. auto typeErasedState = state
  298. ? state->controllerState.get()
  299. : nullptr;
  300. if (const auto my = dynamic_cast<SavedState*>(typeErasedState)) {
  301. if (const auto requestId = base::take(_loadRequestId)) {
  302. _api.request(requestId).cancel();
  303. }
  304. _offset = my->offset;
  305. _loadForOffset = my->loadForOffset;
  306. _preloaded = std::move(my->preloaded);
  307. _count = int(state->list.size());
  308. _fullCount = my->fullCount;
  309. _leftToLoad = my->leftToLoad;
  310. if (my->wasLoading) {
  311. loadMoreRows();
  312. }
  313. PeerListController::restoreState(std::move(state));
  314. }
  315. }
  316. std::unique_ptr<PeerListRow> ListController::createRestoredRow(
  317. not_null<PeerData*> peer) {
  318. return createRow(peer);
  319. }
  320. void ListController::rowClicked(not_null<PeerListRow*> row) {
  321. _showPeerInfoRequests.fire(row->peer());
  322. }
  323. bool ListController::appendRow(not_null<PeerData*> peer) {
  324. if (delegate()->peerListFindRow(peer->id.value)) {
  325. return false;
  326. }
  327. delegate()->peerListAppendRow(createRow(peer));
  328. return true;
  329. }
  330. std::unique_ptr<PeerListRow> ListController::createRow(
  331. not_null<PeerData*> peer) const {
  332. auto row = std::make_unique<PeerListRow>(peer);
  333. row->setCustomStatus(QString());
  334. return row;
  335. }
  336. void ListController::scrollTo(int y) {
  337. _scrollToRequests.fire_copy(y);
  338. }
  339. ListController *CreateAnswerRows(
  340. not_null<Ui::VerticalLayout*> container,
  341. rpl::producer<int> visibleTop,
  342. not_null<Main::Session*> session,
  343. not_null<PollData*> poll,
  344. FullMsgId context,
  345. const PollAnswer &answer) {
  346. using namespace rpl::mappers;
  347. if (!answer.votes) {
  348. return nullptr;
  349. }
  350. const auto delegate = container->lifetime().make_state<ListDelegate>();
  351. const auto controller = container->lifetime().make_state<ListController>(
  352. session,
  353. poll,
  354. context,
  355. answer.option);
  356. const auto percent = answer.votes * 100 / poll->totalVoters;
  357. const auto phrase = poll->quiz()
  358. ? tr::lng_polls_answers_count
  359. : tr::lng_polls_votes_count;
  360. const auto sampleText = phrase(
  361. tr::now,
  362. lt_count_decimal,
  363. answer.votes);
  364. const auto &font = st::boxDividerLabel.style.font;
  365. const auto sampleWidth = font->width(sampleText);
  366. const auto rightSkip = sampleWidth + font->spacew * 4;
  367. const auto headerWrap = container->add(
  368. object_ptr<Ui::RpWidget>(
  369. container));
  370. container->add(object_ptr<Ui::FixedHeightWidget>(
  371. container,
  372. st::boxLittleSkip));
  373. controller->setStyleOverrides(&st::infoCommonGroupsList);
  374. const auto content = container->add(object_ptr<PeerListContent>(
  375. container,
  376. controller));
  377. delegate->setContent(content);
  378. controller->setDelegate(delegate);
  379. const auto count = (answer.votes <= kFirstPage)
  380. ? answer.votes
  381. : (kFirstPage - kLeavePreloaded);
  382. const auto placeholder = container->add(object_ptr<PeerListDummy>(
  383. container,
  384. count,
  385. st::infoCommonGroupsList));
  386. controller->count(
  387. ) | rpl::filter(_1 > 0) | rpl::start_with_next([=] {
  388. delete placeholder;
  389. }, placeholder->lifetime());
  390. const auto header = Ui::CreateChild<Ui::DividerLabel>(
  391. container.get(),
  392. object_ptr<Ui::FlatLabel>(
  393. container,
  394. rpl::single(
  395. TextWithEntities(answer.text)
  396. .append(QString::fromUtf8(" \xe2\x80\x94 "))
  397. .append(QString::number(percent))
  398. .append('%')),
  399. st::boxDividerLabel,
  400. st::defaultPopupMenu,
  401. Core::TextContext({ .session = session })),
  402. style::margins(
  403. st::pollResultsHeaderPadding.left(),
  404. st::pollResultsHeaderPadding.top(),
  405. st::pollResultsHeaderPadding.right() + rightSkip,
  406. st::pollResultsHeaderPadding.bottom()));
  407. const auto votes = Ui::CreateChild<Ui::FlatLabel>(
  408. header,
  409. phrase(
  410. lt_count_decimal,
  411. controller->fullCount() | rpl::map(_1 + 0.)),
  412. st::pollResultsVotesCount);
  413. const auto collapse = Ui::CreateChild<Ui::LinkButton>(
  414. header,
  415. tr::lng_polls_votes_collapse(tr::now),
  416. st::defaultLinkButton);
  417. collapse->setClickedCallback([=] {
  418. controller->scrollTo(headerWrap->y());
  419. controller->collapse();
  420. });
  421. rpl::combine(
  422. controller->fullCount(),
  423. controller->count()
  424. ) | rpl::start_with_next([=](int fullCount, int count) {
  425. const auto many = (fullCount > kFirstPage)
  426. && (count > kFirstPage - kLeavePreloaded);
  427. collapse->setVisible(many);
  428. votes->setVisible(!many);
  429. }, collapse->lifetime());
  430. headerWrap->widthValue(
  431. ) | rpl::start_with_next([=](int width) {
  432. header->resizeToWidth(width);
  433. votes->moveToRight(
  434. st::pollResultsHeaderPadding.right(),
  435. st::pollResultsHeaderPadding.top(),
  436. width);
  437. collapse->moveToRight(
  438. st::pollResultsHeaderPadding.right(),
  439. st::pollResultsHeaderPadding.top(),
  440. width);
  441. }, header->lifetime());
  442. header->heightValue(
  443. ) | rpl::start_with_next([=](int height) {
  444. headerWrap->resize(headerWrap->width(), height);
  445. }, header->lifetime());
  446. auto moreTopWidget = object_ptr<Ui::RpWidget>(container);
  447. moreTopWidget->resize(0, 0);
  448. const auto moreTop = container->add(std::move(moreTopWidget));
  449. const auto more = container->add(
  450. object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
  451. container,
  452. object_ptr<Ui::SettingsButton>(
  453. container,
  454. tr::lng_polls_show_more(
  455. lt_count_decimal,
  456. controller->loadMoreCount() | rpl::map(_1 + 0.),
  457. Ui::Text::Upper),
  458. st::pollResultsShowMore)));
  459. more->entity()->setClickedCallback([=] {
  460. controller->allowLoadMore();
  461. });
  462. controller->loadMoreCount(
  463. ) | rpl::map(_1 > 0) | rpl::start_with_next([=](bool visible) {
  464. more->toggle(visible, anim::type::instant);
  465. }, more->lifetime());
  466. container->add(object_ptr<Ui::FixedHeightWidget>(
  467. container,
  468. st::boxLittleSkip));
  469. rpl::combine(
  470. std::move(visibleTop),
  471. headerWrap->geometryValue(),
  472. moreTop->topValue()
  473. ) | rpl::filter([=](int, QRect headerRect, int moreTop) {
  474. return moreTop >= headerRect.y() + headerRect.height();
  475. }) | rpl::start_with_next([=](
  476. int visibleTop,
  477. QRect headerRect,
  478. int moreTop) {
  479. const auto skip = st::pollResultsHeaderPadding.top()
  480. - st::pollResultsHeaderPadding.bottom();
  481. const auto top = std::clamp(
  482. visibleTop - skip,
  483. headerRect.y(),
  484. moreTop - headerRect.height());
  485. header->move(0, top);
  486. }, header->lifetime());
  487. return controller;
  488. }
  489. InnerWidget::InnerWidget(
  490. QWidget *parent,
  491. not_null<Controller*> controller,
  492. not_null<PollData*> poll,
  493. FullMsgId contextId)
  494. : RpWidget(parent)
  495. , _controller(controller)
  496. , _poll(poll)
  497. , _contextId(contextId)
  498. , _content(this) {
  499. setupContent();
  500. }
  501. void InnerWidget::visibleTopBottomUpdated(
  502. int visibleTop,
  503. int visibleBottom) {
  504. setChildVisibleTopBottom(_content, visibleTop, visibleBottom);
  505. _visibleTop = visibleTop;
  506. }
  507. void InnerWidget::saveState(not_null<Memento*> memento) {
  508. auto states = base::flat_map<
  509. QByteArray,
  510. std::unique_ptr<PeerListState>>();
  511. for (const auto &[option, controller] : _sections) {
  512. states[option] = controller->saveState();
  513. }
  514. memento->setListStates(std::move(states));
  515. }
  516. void InnerWidget::restoreState(not_null<Memento*> memento) {
  517. auto states = memento->listStates();
  518. for (const auto &[option, controller] : _sections) {
  519. const auto i = states.find(option);
  520. if (i != end(states)) {
  521. controller->restoreState(std::move(i->second));
  522. }
  523. }
  524. }
  525. int InnerWidget::desiredHeight() const {
  526. auto desired = 0;
  527. //auto count = qMax(_user->commonChatsCount(), 1);
  528. //desired += qMax(count, _list->fullRowsCount())
  529. // * st::infoCommonGroupsList.item.height;
  530. return qMax(height(), desired);
  531. }
  532. void InnerWidget::setupContent() {
  533. _content->add(
  534. object_ptr<Ui::FlatLabel>(
  535. _content,
  536. rpl::single(_poll->question),
  537. st::pollResultsQuestion,
  538. st::defaultPopupMenu,
  539. Core::TextContext({ .session = &_controller->session() })),
  540. st::boxRowPadding);
  541. Ui::AddSkip(_content, st::boxLittleSkip / 2);
  542. _content->add(
  543. object_ptr<Ui::FlatLabel>(
  544. _content,
  545. tr::lng_polls_votes_count(
  546. lt_count_decimal,
  547. rpl::single(float64(_poll->totalVoters))),
  548. st::boxDividerLabel),
  549. st::boxRowPadding);
  550. Ui::AddSkip(_content, st::boxLittleSkip);
  551. for (const auto &answer : _poll->answers) {
  552. const auto session = &_controller->session();
  553. const auto controller = CreateAnswerRows(
  554. _content,
  555. _visibleTop.value(),
  556. session,
  557. _poll,
  558. _contextId,
  559. answer);
  560. if (!controller) {
  561. continue;
  562. }
  563. controller->showPeerInfoRequests(
  564. ) | rpl::start_to_stream(
  565. _showPeerInfoRequests,
  566. lifetime());
  567. controller->scrollToRequests(
  568. ) | rpl::start_with_next([=](int y) {
  569. _scrollToRequests.fire({ y, -1 });
  570. }, lifetime());
  571. _sections.emplace(answer.option, controller);
  572. }
  573. widthValue(
  574. ) | rpl::start_with_next([=](int newWidth) {
  575. _content->resizeToWidth(newWidth);
  576. }, _content->lifetime());
  577. _content->heightValue(
  578. ) | rpl::start_with_next([=](int height) {
  579. resize(width(), height);
  580. }, _content->lifetime());
  581. }
  582. rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
  583. return _scrollToRequests.events();
  584. }
  585. auto InnerWidget::showPeerInfoRequests() const
  586. -> rpl::producer<not_null<PeerData*>> {
  587. return _showPeerInfoRequests.events();
  588. }
  589. } // namespace Info::Polls