Add PollResultsBox.

This commit is contained in:
John Preston 2020-01-14 14:55:18 +03:00
parent 3d1275e19a
commit 3e0b2f5553
16 changed files with 427 additions and 10 deletions

View File

@ -188,6 +188,8 @@ PRIVATE
boxes/passcode_box.h
boxes/photo_crop_box.cpp
boxes/photo_crop_box.h
boxes/poll_results_box.cpp
boxes/poll_results_box.h
boxes/rate_call_box.cpp
boxes/rate_call_box.h
boxes/report_box.cpp

View File

@ -2200,6 +2200,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_polls_create_quiz_mode" = "Quiz Mode";
"lng_polls_create_button" = "Create";
"lng_polls_poll_results_title" = "Poll results";
"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_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM.";
"lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}.";
"lng_outdated_now" = "So that Telegram Desktop can update to newer versions.";

View File

@ -351,6 +351,7 @@ updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
updateTheme#8216fba3 theme:Theme = Update;
updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
updateLoginToken#564fe691 = Update;
updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1082,10 +1083,6 @@ theme#28f1114 flags:# creator:flags.0?true default:flags.1?true id:long access_h
account.themesNotModified#f41eb622 = account.Themes;
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
wallet.liteResponse#764386d7 response:bytes = wallet.LiteResponse;
wallet.secretSalt#dd484d64 salt:bytes = wallet.KeySecretSalt;
auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken;
auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken;
auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken;
@ -1106,7 +1103,9 @@ themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
messageUserVote#f212f56d user_id:int option:bytes = MessageUserVote;
messageUserVote#a28e5559 user_id:int option:bytes date:int = MessageUserVote;
messageUserVoteInputOption#36377430 user_id:int date:int = MessageUserVote;
messageUserVoteMultiple#e8fe0de user_id:int options:Vector<bytes> date:int = MessageUserVote;
messages.votesList#823f649 flags:# count:int votes:Vector<MessageUserVote> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
@ -1455,7 +1454,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates;
wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse;
wallet.getKeySecretSalt#b57f346 revoke:Bool = wallet.KeySecretSalt;
// LAYER 109

View File

@ -923,3 +923,17 @@ customBadgeField: InputField(defaultInputField) {
heightMin: 32px;
}
pollResultsQuestion: FlatLabel(defaultFlatLabel) {
minWidth: 320px;
textFg: windowBoldFg;
style: TextStyle(defaultTextStyle) {
font: font(16px semibold);
linkFont: font(16px semibold);
linkFontOver: font(16px semibold underline);
}
}
pollResultsVotesCount: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
}
pollResultsHeaderPadding: margins(22px, 8px, 22px, 8px);

View File

@ -0,0 +1,322 @@
/*
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 "boxes/poll_results_box.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/padding_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/text/text_utilities.h"
#include "boxes/peer_list_box.h"
#include "window/window_session_controller.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 {
constexpr auto kFirstPage = 10;
constexpr auto kPerPage = 100;
class Delegate final : public PeerListContentDelegate {
public:
void peerListSetTitle(rpl::producer<QString> title) override;
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
bool peerListIsRowSelected(not_null<PeerData*> peer) override;
int peerListSelectedRowsCount() override;
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
void peerListScrollToTop() override;
void peerListAddSelectedRowInBunch(
not_null<PeerData*> peer) override;
void peerListFinishSelectedRowsBunch() override;
void peerListSetDescription(
object_ptr<Ui::FlatLabel> description) override;
};
class Controller final : public PeerListController {
public:
Controller(
not_null<Window::SessionController*> window,
not_null<PollData*> poll,
FullMsgId context,
QByteArray option);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void loadMoreRows() override;
void allowLoadAll();
private:
bool appendRow(not_null<UserData*> user);
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
const not_null<Window::SessionController*> _window;
const not_null<PollData*> _poll;
const FullMsgId _context;
const QByteArray _option;
MTP::Sender _api;
QString _offset;
mtpRequestId _loadRequestId = 0;
int _fullCount = 0;
bool _allLoaded = false;
bool _loadingAll = false;
};
void Delegate::peerListSetTitle(rpl::producer<QString> title) {
}
void Delegate::peerListSetAdditionalTitle(rpl::producer<QString> title) {
}
bool Delegate::peerListIsRowSelected(not_null<PeerData*> peer) {
return false;
}
int Delegate::peerListSelectedRowsCount() {
return 0;
}
std::vector<not_null<PeerData*>> Delegate::peerListCollectSelectedRows() {
return {};
}
void Delegate::peerListScrollToTop() {
}
void Delegate::peerListAddSelectedRowInBunch(not_null<PeerData*> peer) {
Unexpected("Item selection in Info::Profile::Members.");
}
void Delegate::peerListFinishSelectedRowsBunch() {
}
void Delegate::peerListSetDescription(
object_ptr<Ui::FlatLabel> description) {
description.destroy();
}
Controller::Controller(
not_null<Window::SessionController*> window,
not_null<PollData*> poll,
FullMsgId context,
QByteArray option)
: _window(window)
, _poll(poll)
, _context(context)
, _option(option)
, _api(_window->session().api().instance()) {
}
Main::Session &Controller::session() const {
return _window->session();
}
void Controller::prepare() {
delegate()->peerListRefreshRows();
}
void Controller::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 Controller::allowLoadAll() {
_loadingAll = true;
loadMoreRows();
}
void Controller::rowClicked(not_null<PeerListRow*> row) {
_window->showPeerHistory(row->peer(), Window::SectionShow::Way::Forward);
}
bool Controller::appendRow(not_null<UserData*> user) {
if (delegate()->peerListFindRow(user->id)) {
return false;
}
delegate()->peerListAppendRow(createRow(user));
return true;
}
std::unique_ptr<PeerListRow> Controller::createRow(
not_null<UserData*> user) const {
auto row = std::make_unique<PeerListRow>(user);
row->setCustomStatus(QString());
return row;
}
void CreateAnswerRows(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
not_null<PollData*> poll,
FullMsgId context,
const PollAnswer &answer) {
if (!answer.votes) {
return;
}
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 = box->addRow(
object_ptr<Ui::DividerLabel>(
box,
object_ptr<Ui::FlatLabel>(
box,
(answer.text.repeated(20)
+ 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())),
style::margins());
const auto votes = Ui::CreateChild<Ui::FlatLabel>(
header,
rightText,
st::pollResultsVotesCount);
header->widthValue(
) | rpl::start_with_next([=](int width) {
votes->moveToRight(
st::pollResultsHeaderPadding.right(),
st::pollResultsHeaderPadding.top(),
width);
}, votes->lifetime());
box->addRow(object_ptr<Ui::FixedHeightWidget>(box, st::boxLittleSkip));
const auto delegate = box->lifetime().make_state<Delegate>();
const auto controller = box->lifetime().make_state<Controller>(
window,
poll,
context,
answer.option);
const auto content = box->addRow(
object_ptr<PeerListContent>(
box,
controller,
st::infoCommonGroupsList),
style::margins());
delegate->setContent(content);
controller->setDelegate(delegate);
const auto more = box->addRow(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
box,
object_ptr<Ui::SettingsButton>(
box,
tr::lng_polls_show_more(
lt_count_decimal,
rpl::single(answer.votes + 0.),
Ui::Text::Upper),
st::infoMainButton)),
style::margins());
more->toggle(answer.votes > kFirstPage, anim::type::instant);
more->entity()->setClickedCallback([=] {
controller->allowLoadAll();
more->hide(anim::type::instant);
});
box->addRow(object_ptr<Ui::FixedHeightWidget>(box, st::boxLittleSkip));
}
} // namespace
void PollResultsBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
not_null<PollData*> poll,
FullMsgId context) {
const auto quiz = poll->quiz();
box->setWidth(st::boxWideWidth);
box->setTitle(quiz
? tr::lng_polls_quiz_results_title()
: tr::lng_polls_poll_results_title());
box->setAdditionalTitle((quiz
? tr::lng_polls_answers_count
: tr::lng_polls_votes_count)(
lt_count_decimal,
rpl::single(poll->totalVoters * 1.)));
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
poll->question,
st::pollResultsQuestion),
style::margins{
st::boxRowPadding.left(),
0,
st::boxRowPadding.right(),
st::boxMediumSkip });
for (const auto &answer : poll->answers) {
CreateAnswerRows(box, window, poll, context, answer);
}
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
}

View File

@ -0,0 +1,23 @@
/*
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
*/
#pragma once
#include "ui/layers/generic_box.h"
class PollData;
class HistoryItem;
namespace Window {
class SessionController;
} // namespace Window
void PollResultsBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
not_null<PollData*> poll,
FullMsgId context);

View File

@ -112,7 +112,10 @@ bool PollData::applyResults(const MTPPollResults &results) {
recentVoters = ranges::view::all(
recent->v
) | ranges::view::transform([&](MTPint userId) {
return _owner->userLoaded(userId.v);
const auto user = _owner->user(userId.v);
return (user->loadedStatus != PeerData::NotLoaded)
? user.get()
: nullptr;
}) | ranges::view::filter([](UserData *user) {
return user != nullptr;
}) | ranges::view::transform([](UserData *user) {

View File

@ -577,6 +577,11 @@ bool InnerWidget::elementIntersectsRange(
void InnerWidget::elementStartStickerLoop(not_null<const Element*> view) {
}
void InnerWidget::elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) {
}
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
memento->setFilter(std::move(_filter));
memento->setAdmins(std::move(_admins));

View File

@ -101,6 +101,9 @@ public:
int till) override;
void elementStartStickerLoop(
not_null<const HistoryView::Element*> view) override;
void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) override;
~InnerWidget();

View File

@ -30,9 +30,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/inactive_press.h"
#include "window/window_session_controller.h"
#include "window/window_peer_menu.h"
#include "window/window_controller.h"
#include "boxes/confirm_box.h"
#include "boxes/report_box.h"
#include "boxes/sticker_set_box.h"
#include "boxes/poll_results_box.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h"
#include "history/history_widget.h"
@ -2418,6 +2420,13 @@ void HistoryInner::elementStartStickerLoop(
_animatedStickersPlayed.emplace(view->data());
}
void HistoryInner::elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) {
_controller->window().show(
Box(PollResultsBox, _controller, poll, context));
}
auto HistoryInner::getSelectionState() const
-> HistoryView::TopBarWidget::SelectedState {
auto result = HistoryView::TopBarWidget::SelectedState {};
@ -3278,6 +3287,13 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
Instance->elementStartStickerLoop(view);
}
}
void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) override {
if (Instance) {
Instance->elementShowPollResults(poll, context);
}
}
};

View File

@ -81,6 +81,9 @@ public:
int from,
int till) const;
void elementStartStickerLoop(not_null<const Element*> view);
void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context);
void updateBotInfo(bool recount = true);

View File

@ -94,6 +94,11 @@ void SimpleElementDelegate::elementStartStickerLoop(
not_null<const Element*> view) {
}
void SimpleElementDelegate::elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) {
}
TextSelection UnshiftItemSelection(
TextSelection selection,
uint16 byLength) {

View File

@ -51,6 +51,9 @@ public:
int from,
int till) = 0;
virtual void elementStartStickerLoop(not_null<const Element*> view) = 0;
virtual void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) = 0;
};
@ -71,6 +74,9 @@ public:
int from,
int till) override;
void elementStartStickerLoop(not_null<const Element*> view) override;
void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) override;
};

View File

@ -1153,6 +1153,11 @@ bool ListWidget::elementIntersectsRange(
void ListWidget::elementStartStickerLoop(not_null<const Element*> view) {
}
void ListWidget::elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) {
}
void ListWidget::saveState(not_null<ListMemento*> memento) {
memento->setAroundPosition(_aroundPosition);
auto state = countScrollState();

View File

@ -197,6 +197,9 @@ public:
int from,
int till) override;
void elementStartStickerLoop(not_null<const Element*> view) override;
void elementShowPollResults(
not_null<PollData*> poll,
FullMsgId context) override;
~ListWidget();

View File

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/effects/ripple_animation.h"
#include "boxes/poll_results_box.h"
#include "data/data_media_types.h"
#include "data/data_poll.h"
#include "data/data_user.h"
@ -255,6 +256,9 @@ bool Poll::canSendVotes() const {
}
bool Poll::showVotersCount() const {
if (!_totalVotes) {
return true;
}
return showVotes()
? !(_flags & PollData::Flag::PublicVotes)
: !(_flags & PollData::Flag::MultiChoice);
@ -475,7 +479,9 @@ void Poll::sendMultiOptions() {
}
void Poll::showResults() {
// #TODO polls
_parent->delegate()->elementShowPollResults(
_poll,
_parent->data()->fullId());
}
void Poll::updateVotes() {