/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/polls/info_polls_results_inner_widget.h" #include "info/info_controller.h" #include "lang/lang_keys.h" #include "data/data_poll.h" #include "data/data_peer.h" #include "data/data_user.h" #include "data/data_session.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/slide_wrap.h" #include "ui/text/text_utilities.h" #include "boxes/peer_list_box.h" #include "main/main_session.h" #include "history/history.h" #include "history/history_item.h" #include "apiwrap.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_info.h" namespace Info { namespace Polls { namespace { constexpr auto kFirstPage = 10; constexpr auto kPerPage = 100; class ListDelegate final : public PeerListContentDelegate { public: void peerListSetTitle(rpl::producer title) override; void peerListSetAdditionalTitle(rpl::producer title) override; bool peerListIsRowSelected(not_null peer) override; int peerListSelectedRowsCount() override; std::vector> peerListCollectSelectedRows() override; void peerListScrollToTop() override; void peerListAddSelectedRowInBunch( not_null peer) override; void peerListFinishSelectedRowsBunch() override; void peerListSetDescription( object_ptr description) override; }; class ListController final : public PeerListController { public: ListController( not_null session, not_null poll, FullMsgId context, QByteArray option); Main::Session &session() const override; void prepare() override; void rowClicked(not_null row) override; void loadMoreRows() override; void allowLoadAll(); rpl::producer> showPeerInfoRequests() const; private: bool appendRow(not_null user); std::unique_ptr createRow(not_null user) const; const not_null _session; const not_null _poll; const FullMsgId _context; const QByteArray _option; MTP::Sender _api; QString _offset; mtpRequestId _loadRequestId = 0; int _fullCount = 0; bool _allLoaded = false; bool _loadingAll = false; rpl::event_stream> _showPeerInfoRequests; }; void ListDelegate::peerListSetTitle(rpl::producer title) { } void ListDelegate::peerListSetAdditionalTitle(rpl::producer title) { } bool ListDelegate::peerListIsRowSelected(not_null peer) { return false; } int ListDelegate::peerListSelectedRowsCount() { return 0; } auto ListDelegate::peerListCollectSelectedRows() -> std::vector> { return {}; } void ListDelegate::peerListScrollToTop() { } void ListDelegate::peerListAddSelectedRowInBunch(not_null peer) { Unexpected("Item selection in Info::Profile::Members."); } void ListDelegate::peerListFinishSelectedRowsBunch() { } void ListDelegate::peerListSetDescription( object_ptr description) { description.destroy(); } ListController::ListController( not_null session, not_null poll, FullMsgId context, QByteArray option) : _session(session) , _poll(poll) , _context(context) , _option(option) , _api(_session->api().instance()) { } Main::Session &ListController::session() const { return *_session; } void ListController::prepare() { delegate()->peerListRefreshRows(); } void ListController::loadMoreRows() { if (_loadRequestId || _allLoaded || (!_loadingAll && !_offset.isEmpty())) { return; } const auto item = session().data().message(_context); if (!item || !IsServerMsgId(item->id)) { _allLoaded = true; return; } using Flag = MTPmessages_GetPollVotes::Flag; const auto flags = Flag::f_option | (_offset.isEmpty() ? Flag(0) : Flag::f_offset); _loadRequestId = _api.request(MTPmessages_GetPollVotes( MTP_flags(flags), item->history()->peer->input, MTP_int(item->id), MTP_bytes(_option), MTP_string(_offset), MTP_int(_offset.isEmpty() ? kFirstPage : kPerPage) )).done([=](const MTPmessages_VotesList &result) { _loadRequestId = 0; result.match([&](const MTPDmessages_votesList &data) { _fullCount = data.vcount().v; _offset = data.vnext_offset().value_or_empty(); auto &owner = session().data(); owner.processUsers(data.vusers()); for (const auto &vote : data.vvotes().v) { vote.match([&](const auto &data) { const auto user = owner.user(data.vuser_id().v); if (user->loadedStatus != PeerData::NotLoaded) { appendRow(user); } }); } }); _allLoaded = _offset.isEmpty(); delegate()->peerListRefreshRows(); }).fail([=](const RPCError &error) { _loadRequestId = 0; }).send(); } void ListController::allowLoadAll() { _loadingAll = true; loadMoreRows(); } auto ListController::showPeerInfoRequests() const -> rpl::producer> { return _showPeerInfoRequests.events(); } void ListController::rowClicked(not_null row) { _showPeerInfoRequests.fire(row->peer()); } bool ListController::appendRow(not_null user) { if (delegate()->peerListFindRow(user->id)) { return false; } delegate()->peerListAppendRow(createRow(user)); return true; } std::unique_ptr ListController::createRow( not_null user) const { auto row = std::make_unique(user); row->setCustomStatus(QString()); return row; } ListController *CreateAnswerRows( not_null container, not_null session, not_null poll, FullMsgId context, const PollAnswer &answer) { if (!answer.votes) { return nullptr; } const auto percent = answer.votes * 100 / poll->totalVoters; const auto rightText = (poll->quiz() ? tr::lng_polls_answers_count : tr::lng_polls_votes_count)( tr::now, lt_count_decimal, answer.votes); const auto &font = st::boxDividerLabel.style.font; const auto rightWidth = font->width(rightText); const auto rightSkip = rightWidth + font->spacew * 4; const auto header = container->add( object_ptr( container, object_ptr( container, (answer.text + QString::fromUtf8(" \xe2\x80\x94 ") + QString::number(percent) + "%"), st::boxDividerLabel), style::margins( st::pollResultsHeaderPadding.left(), st::pollResultsHeaderPadding.top(), st::pollResultsHeaderPadding.right() + rightSkip, st::pollResultsHeaderPadding.bottom()))); const auto votes = Ui::CreateChild( header, rightText, st::pollResultsVotesCount); header->widthValue( ) | rpl::start_with_next([=](int width) { votes->moveToRight( st::pollResultsHeaderPadding.right(), st::pollResultsHeaderPadding.top(), width); }, votes->lifetime()); container->add(object_ptr( container, st::boxLittleSkip)); const auto delegate = container->lifetime().make_state(); const auto controller = container->lifetime().make_state( session, poll, context, answer.option); const auto content = container->add(object_ptr( container, controller, st::infoCommonGroupsList)); delegate->setContent(content); controller->setDelegate(delegate); const auto more = container->add( object_ptr>( container, object_ptr( container, tr::lng_polls_show_more( lt_count_decimal, rpl::single(answer.votes + 0.), Ui::Text::Upper), st::pollResultsShowMore))); more->toggle(answer.votes > kFirstPage, anim::type::instant); more->entity()->setClickedCallback([=] { controller->allowLoadAll(); more->hide(anim::type::instant); }); container->add(object_ptr( container, st::boxLittleSkip)); return controller; } } // namespace InnerWidget::InnerWidget( QWidget *parent, not_null controller, not_null poll, FullMsgId contextId) : RpWidget(parent) , _controller(controller) , _poll(poll) , _contextId(contextId) , _content(setupContent(this)) { } void InnerWidget::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { setChildVisibleTopBottom(_content, visibleTop, visibleBottom); } void InnerWidget::saveState(not_null memento) { //memento->setListState(_listController->saveState()); } void InnerWidget::restoreState(not_null memento) { //_listController->restoreState(memento->listState()); } int InnerWidget::desiredHeight() const { auto desired = 0; //auto count = qMax(_user->commonChatsCount(), 1); //desired += qMax(count, _list->fullRowsCount()) // * st::infoCommonGroupsList.item.height; return qMax(height(), desired); } object_ptr InnerWidget::setupContent( RpWidget *parent) { auto result = object_ptr(parent); const auto quiz = _poll->quiz(); result->add( object_ptr( result, _poll->question, st::pollResultsQuestion), style::margins{ st::boxRowPadding.left(), 0, st::boxRowPadding.right(), st::boxMediumSkip }); for (const auto &answer : _poll->answers) { const auto session = &_controller->parentController()->session(); const auto controller = CreateAnswerRows( result, session, _poll, _contextId, answer); if (controller) { controller->showPeerInfoRequests( ) | rpl::start_to_stream( _showPeerInfoRequests, lifetime()); } } parent->widthValue( ) | rpl::start_with_next([content = result.data()](int newWidth) { content->resizeToWidth(newWidth); }, result->lifetime()); result->heightValue( ) | rpl::start_with_next([=](int height) { parent->resize(parent->width(), height); }, result->lifetime()); return result; } auto InnerWidget::showPeerInfoRequests() const -> rpl::producer> { return _showPeerInfoRequests.events(); } } // namespace Polls } // namespace Info