diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a585d93b7..9710f6803 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2204,6 +2204,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_polls_quiz_results_title" = "Quiz results"; "lng_polls_show_more#one" = "Show {count} more voter"; "lng_polls_show_more#other" = "Show {count} more voters"; +"lng_polls_votes_collapse" = "Collapse"; "lng_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM."; "lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}."; diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp index b895681d1..cef0c22f1 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp @@ -165,9 +165,11 @@ public: void loadMoreRows() override; void allowLoadMore(); + void collapse(); [[nodiscard]] auto showPeerInfoRequests() const -> rpl::producer>; + [[nodiscard]] rpl::producer scrollToRequests() const; [[nodiscard]] rpl::producer count() const; [[nodiscard]] rpl::producer fullCount() const; [[nodiscard]] rpl::producer loadMoreCount() const; @@ -178,6 +180,8 @@ public: std::unique_ptr createRestoredRow( not_null peer) override; + void scrollTo(int y); + private: struct SavedState : SavedStateBase { QString offset; @@ -191,6 +195,8 @@ private: bool appendRow(not_null user); std::unique_ptr createRow(not_null user) const; void addPreloaded(); + bool addPreloadedPage(); + void preloadedAdded(); const not_null _session; const not_null _poll; @@ -208,6 +214,7 @@ private: rpl::variable _leftToLoad; rpl::event_stream> _showPeerInfoRequests; + rpl::event_stream _scrollToRequests; }; @@ -280,15 +287,15 @@ void ListController::loadMoreRows() { } return data.vcount().v; }); - _count = delegate()->peerListFullRowsCount(); if (_offset.isEmpty()) { addPreloaded(); _fullCount = delegate()->peerListFullRowsCount(); _leftToLoad = 0; } else { - delegate()->peerListRefreshRows(); + _count = delegate()->peerListFullRowsCount(); _fullCount = count; _leftToLoad = count - delegate()->peerListFullRowsCount(); + delegate()->peerListRefreshRows(); } _loadRequestId = 0; }).fail([=](const RPCError &error) { @@ -297,16 +304,58 @@ void ListController::loadMoreRows() { } void ListController::allowLoadMore() { - _loadForOffset = _offset; - addPreloaded(); - loadMoreRows(); + if (!addPreloadedPage()) { + _loadForOffset = _offset; + addPreloaded(); + loadMoreRows(); + } +} + +void ListController::collapse() { + const auto count = delegate()->peerListFullRowsCount(); + if (count <= kFirstPage) { + return; + } + const auto remove = count - (kFirstPage - kLeavePreloaded); + ranges::action::reverse(_preloaded); + _preloaded.reserve(_preloaded.size() + remove); + for (auto i = 0; i != remove; ++i) { + const auto row = delegate()->peerListRowAt(count - i - 1); + _preloaded.push_back(row->peer()->asUser()); + delegate()->peerListRemoveRow(row); + } + ranges::action::reverse(_preloaded); + + delegate()->peerListRefreshRows(); + const auto now = count - remove; + _count = now; + _leftToLoad = _fullCount.current() - now; } void ListController::addPreloaded() { for (const auto user : base::take(_preloaded)) { appendRow(user); } - _leftToLoad = _fullCount.current() - delegate()->peerListFullRowsCount(); + preloadedAdded(); +} + +bool ListController::addPreloadedPage() { + if (_preloaded.size() < kPerPage + kLeavePreloaded) { + return false; + } + const auto from = begin(_preloaded); + const auto till = from + kPerPage; + for (auto i = from; i != till; ++i) { + appendRow(*i); + } + _preloaded.erase(from, till); + preloadedAdded(); + return true; +} + +void ListController::preloadedAdded() { + _count = delegate()->peerListFullRowsCount(); + _leftToLoad = _fullCount.current() - _count.current(); delegate()->peerListRefreshRows(); } @@ -315,6 +364,10 @@ auto ListController::showPeerInfoRequests() const return _showPeerInfoRequests.events(); } +rpl::producer ListController::scrollToRequests() const { + return _scrollToRequests.events(); +} + rpl::producer ListController::count() const { return _count.value(); } @@ -399,6 +452,10 @@ std::unique_ptr ListController::createRow( return row; } +void ListController::scrollTo(int y) { + _scrollToRequests.fire_copy(y); +} + ListController *CreateAnswerRows( not_null container, rpl::producer visibleTop, @@ -479,6 +536,23 @@ ListController *CreateAnswerRows( lt_count_decimal, controller->fullCount() | rpl::map(_1 + 0.)), st::pollResultsVotesCount); + const auto collapse = Ui::CreateChild( + header, + tr::lng_polls_votes_collapse(tr::now), + st::defaultLinkButton); + collapse->setClickedCallback([=] { + controller->scrollTo(headerWrap->y()); + controller->collapse(); + }); + rpl::combine( + controller->fullCount(), + controller->count() + ) | rpl::start_with_next([=](int fullCount, int count) { + const auto many = (fullCount > kFirstPage) + && (count > kFirstPage - kLeavePreloaded); + collapse->setVisible(many); + votes->setVisible(!many); + }, collapse->lifetime()); headerWrap->widthValue( ) | rpl::start_with_next([=](int width) { @@ -487,6 +561,10 @@ ListController *CreateAnswerRows( st::pollResultsHeaderPadding.right(), st::pollResultsHeaderPadding.top(), width); + collapse->moveToRight( + st::pollResultsHeaderPadding.right(), + st::pollResultsHeaderPadding.top(), + width); }, header->lifetime()); header->heightValue( @@ -520,7 +598,9 @@ ListController *CreateAnswerRows( std::move(visibleTop), headerWrap->geometryValue(), more->topValue() - ) | rpl::start_with_next([=]( + ) | rpl::filter([=](int, QRect headerRect, int moreTop) { + return moreTop >= headerRect.y() + headerRect.height(); + }) | rpl::start_with_next([=]( int visibleTop, QRect headerRect, int moreTop) { @@ -612,6 +692,10 @@ void InnerWidget::setupContent() { ) | rpl::start_to_stream( _showPeerInfoRequests, lifetime()); + controller->scrollToRequests( + ) | rpl::start_with_next([=](int y) { + _scrollToRequests.fire({ y, -1 }); + }, lifetime()); _sections.emplace(answer.option, controller); } @@ -626,6 +710,10 @@ void InnerWidget::setupContent() { }, _content->lifetime()); } +rpl::producer InnerWidget::scrollToRequests() const { + return _scrollToRequests.events(); +} + auto InnerWidget::showPeerInfoRequests() const -> rpl::producer> { return _showPeerInfoRequests.events(); diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.h b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.h index da8bd1f0e..f2fbf05af 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.h +++ b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/rp_widget.h" +#include "ui/widgets/scroll_area.h" #include "base/object_ptr.h" namespace Ui { @@ -38,6 +39,9 @@ public: return _contextId; } + [[nodiscard]] auto scrollToRequests() const + -> rpl::producer; + [[nodiscard]] auto showPeerInfoRequests() const -> rpl::producer>; @@ -58,9 +62,11 @@ private: not_null _poll; FullMsgId _contextId; object_ptr _content; + base::flat_map> _sections; + + rpl::event_stream _scrollToRequests; rpl::event_stream> _showPeerInfoRequests; rpl::variable _visibleTop = 0; - base::flat_map> _sections; }; diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_results_widget.cpp index cd071a397..e979fd3f4 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_results_widget.cpp @@ -55,6 +55,10 @@ Widget::Widget(QWidget *parent, not_null controller) ) | rpl::start_with_next([=](not_null peer) { controller->showPeerInfo(peer); }, _inner->lifetime()); + _inner->scrollToRequests( + ) | rpl::start_with_next([=](const Ui::ScrollToRequest &request) { + scrollTo(request); + }, _inner->lifetime()); controller->setCanSaveChanges(rpl::single(false)); }