mirror of https://github.com/procxx/kepka.git
Improve poll results loading.
This commit is contained in:
parent
ebae0d71b8
commit
80168a58a7
|
@ -33,8 +33,9 @@ namespace Info {
|
||||||
namespace Polls {
|
namespace Polls {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kFirstPage = 10;
|
constexpr auto kFirstPage = 15;
|
||||||
constexpr auto kPerPage = 100;
|
constexpr auto kPerPage = 50;
|
||||||
|
constexpr auto kLeavePreloaded = 5;
|
||||||
|
|
||||||
class ListDelegate final : public PeerListContentDelegate {
|
class ListDelegate final : public PeerListContentDelegate {
|
||||||
public:
|
public:
|
||||||
|
@ -101,9 +102,12 @@ public:
|
||||||
void rowClicked(not_null<PeerListRow*> row) override;
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
void loadMoreRows() override;
|
void loadMoreRows() override;
|
||||||
|
|
||||||
void allowLoadAll();
|
void allowLoadMore();
|
||||||
|
|
||||||
rpl::producer<not_null<PeerData*>> showPeerInfoRequests() const;
|
[[nodiscard]] auto showPeerInfoRequests() const
|
||||||
|
-> rpl::producer<not_null<PeerData*>>;
|
||||||
|
[[nodiscard]] rpl::producer<int> fullCount() const;
|
||||||
|
[[nodiscard]] rpl::producer<int> leftToLoad() const;
|
||||||
|
|
||||||
std::unique_ptr<PeerListState> saveState() const override;
|
std::unique_ptr<PeerListState> saveState() const override;
|
||||||
void restoreState(std::unique_ptr<PeerListState> state) override;
|
void restoreState(std::unique_ptr<PeerListState> state) override;
|
||||||
|
@ -114,13 +118,16 @@ public:
|
||||||
private:
|
private:
|
||||||
struct SavedState : SavedStateBase {
|
struct SavedState : SavedStateBase {
|
||||||
QString offset;
|
QString offset;
|
||||||
bool allLoaded = false;
|
QString loadForOffset;
|
||||||
|
int leftToLoad = 0;
|
||||||
|
int fullCount = 0;
|
||||||
|
std::vector<not_null<UserData*>> preloaded;
|
||||||
bool wasLoading = false;
|
bool wasLoading = false;
|
||||||
bool loadingAll = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool appendRow(not_null<UserData*> user);
|
bool appendRow(not_null<UserData*> user);
|
||||||
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
|
std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;
|
||||||
|
void addPreloaded();
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
const not_null<PollData*> _poll;
|
const not_null<PollData*> _poll;
|
||||||
|
@ -131,9 +138,10 @@ private:
|
||||||
|
|
||||||
QString _offset;
|
QString _offset;
|
||||||
mtpRequestId _loadRequestId = 0;
|
mtpRequestId _loadRequestId = 0;
|
||||||
int _fullCount = 0;
|
QString _loadForOffset;
|
||||||
bool _allLoaded = false;
|
std::vector<not_null<UserData*>> _preloaded;
|
||||||
bool _loadingAll = false;
|
rpl::variable<int> _leftToLoad;
|
||||||
|
rpl::variable<int> _fullCount;
|
||||||
|
|
||||||
rpl::event_stream<not_null<PeerData*>> _showPeerInfoRequests;
|
rpl::event_stream<not_null<PeerData*>> _showPeerInfoRequests;
|
||||||
|
|
||||||
|
@ -149,6 +157,10 @@ ListController::ListController(
|
||||||
, _context(context)
|
, _context(context)
|
||||||
, _option(option)
|
, _option(option)
|
||||||
, _api(_session->api().instance()) {
|
, _api(_session->api().instance()) {
|
||||||
|
const auto i = ranges::find(poll->answers, option, &PollAnswer::option);
|
||||||
|
Assert(i != poll->answers.end());
|
||||||
|
_leftToLoad = i->votes;
|
||||||
|
_fullCount = i->votes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &ListController::session() const {
|
Main::Session &ListController::session() const {
|
||||||
|
@ -161,67 +173,100 @@ void ListController::prepare() {
|
||||||
|
|
||||||
void ListController::loadMoreRows() {
|
void ListController::loadMoreRows() {
|
||||||
if (_loadRequestId
|
if (_loadRequestId
|
||||||
|| _allLoaded
|
|| !_leftToLoad.current()
|
||||||
|| (!_loadingAll && !_offset.isEmpty())) {
|
|| (!_offset.isEmpty() && _loadForOffset != _offset)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto item = session().data().message(_context);
|
const auto item = session().data().message(_context);
|
||||||
if (!item || !IsServerMsgId(item->id)) {
|
if (!item || !IsServerMsgId(item->id)) {
|
||||||
_allLoaded = true;
|
_leftToLoad = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using Flag = MTPmessages_GetPollVotes::Flag;
|
using Flag = MTPmessages_GetPollVotes::Flag;
|
||||||
const auto flags = Flag::f_option
|
const auto flags = Flag::f_option
|
||||||
| (_offset.isEmpty() ? Flag(0) : Flag::f_offset);
|
| (_offset.isEmpty() ? Flag(0) : Flag::f_offset);
|
||||||
|
const auto limit = _offset.isEmpty() ? kFirstPage : kPerPage;
|
||||||
_loadRequestId = _api.request(MTPmessages_GetPollVotes(
|
_loadRequestId = _api.request(MTPmessages_GetPollVotes(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
item->history()->peer->input,
|
item->history()->peer->input,
|
||||||
MTP_int(item->id),
|
MTP_int(item->id),
|
||||||
MTP_bytes(_option),
|
MTP_bytes(_option),
|
||||||
MTP_string(_offset),
|
MTP_string(_offset),
|
||||||
MTP_int(_offset.isEmpty() ? kFirstPage : kPerPage)
|
MTP_int(limit)
|
||||||
)).done([=](const MTPmessages_VotesList &result) {
|
)).done([=](const MTPmessages_VotesList &result) {
|
||||||
_loadRequestId = 0;
|
const auto count = result.match([&](
|
||||||
result.match([&](const MTPDmessages_votesList &data) {
|
const MTPDmessages_votesList &data) {
|
||||||
_fullCount = data.vcount().v;
|
|
||||||
_offset = data.vnext_offset().value_or_empty();
|
_offset = data.vnext_offset().value_or_empty();
|
||||||
auto &owner = session().data();
|
auto &owner = session().data();
|
||||||
owner.processUsers(data.vusers());
|
owner.processUsers(data.vusers());
|
||||||
|
auto add = limit - kLeavePreloaded;
|
||||||
for (const auto &vote : data.vvotes().v) {
|
for (const auto &vote : data.vvotes().v) {
|
||||||
vote.match([&](const auto &data) {
|
vote.match([&](const auto &data) {
|
||||||
const auto user = owner.user(data.vuser_id().v);
|
const auto user = owner.user(data.vuser_id().v);
|
||||||
if (user->loadedStatus != PeerData::NotLoaded) {
|
if (user->loadedStatus != PeerData::NotLoaded) {
|
||||||
|
if (add) {
|
||||||
appendRow(user);
|
appendRow(user);
|
||||||
|
--add;
|
||||||
|
} else {
|
||||||
|
_preloaded.push_back(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return data.vcount().v;
|
||||||
});
|
});
|
||||||
_allLoaded = _offset.isEmpty();
|
if (_offset.isEmpty()) {
|
||||||
|
addPreloaded();
|
||||||
|
_fullCount = delegate()->peerListFullRowsCount();
|
||||||
|
_leftToLoad = 0;
|
||||||
|
} else {
|
||||||
delegate()->peerListRefreshRows();
|
delegate()->peerListRefreshRows();
|
||||||
|
_fullCount = count;
|
||||||
|
_leftToLoad = count - delegate()->peerListFullRowsCount();
|
||||||
|
}
|
||||||
|
_loadRequestId = 0;
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_loadRequestId = 0;
|
_loadRequestId = 0;
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListController::allowLoadAll() {
|
void ListController::allowLoadMore() {
|
||||||
_loadingAll = true;
|
_loadForOffset = _offset;
|
||||||
|
addPreloaded();
|
||||||
loadMoreRows();
|
loadMoreRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListController::addPreloaded() {
|
||||||
|
for (const auto user : base::take(_preloaded)) {
|
||||||
|
appendRow(user);
|
||||||
|
}
|
||||||
|
delegate()->peerListRefreshRows();
|
||||||
|
}
|
||||||
|
|
||||||
auto ListController::showPeerInfoRequests() const
|
auto ListController::showPeerInfoRequests() const
|
||||||
-> rpl::producer<not_null<PeerData*>> {
|
-> rpl::producer<not_null<PeerData*>> {
|
||||||
return _showPeerInfoRequests.events();
|
return _showPeerInfoRequests.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> ListController::leftToLoad() const {
|
||||||
|
return _leftToLoad.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> ListController::fullCount() const {
|
||||||
|
return _fullCount.value();
|
||||||
|
}
|
||||||
|
|
||||||
auto ListController::saveState() const -> std::unique_ptr<PeerListState> {
|
auto ListController::saveState() const -> std::unique_ptr<PeerListState> {
|
||||||
auto result = PeerListController::saveState();
|
auto result = PeerListController::saveState();
|
||||||
|
|
||||||
auto my = std::make_unique<SavedState>();
|
auto my = std::make_unique<SavedState>();
|
||||||
my->offset = _offset;
|
my->offset = _offset;
|
||||||
my->allLoaded = _allLoaded;
|
my->leftToLoad = _leftToLoad.current();
|
||||||
|
my->fullCount = _fullCount.current();
|
||||||
|
my->preloaded = _preloaded;
|
||||||
my->wasLoading = (_loadRequestId != 0);
|
my->wasLoading = (_loadRequestId != 0);
|
||||||
my->loadingAll = _loadingAll;
|
my->loadForOffset = _loadForOffset;
|
||||||
result->controllerState = std::move(my);
|
result->controllerState = std::move(my);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -237,11 +282,13 @@ void ListController::restoreState(std::unique_ptr<PeerListState> state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_offset = my->offset;
|
_offset = my->offset;
|
||||||
_allLoaded = my->allLoaded;
|
_loadForOffset = my->loadForOffset;
|
||||||
_loadingAll = my->loadingAll;
|
_preloaded = std::move(my->preloaded);
|
||||||
if (my->wasLoading) {
|
if (my->wasLoading) {
|
||||||
loadMoreRows();
|
loadMoreRows();
|
||||||
}
|
}
|
||||||
|
_leftToLoad = my->leftToLoad;
|
||||||
|
_fullCount = my->fullCount;
|
||||||
PeerListController::restoreState(std::move(state));
|
PeerListController::restoreState(std::move(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,20 +326,30 @@ ListController *CreateAnswerRows(
|
||||||
not_null<PollData*> poll,
|
not_null<PollData*> poll,
|
||||||
FullMsgId context,
|
FullMsgId context,
|
||||||
const PollAnswer &answer) {
|
const PollAnswer &answer) {
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
if (!answer.votes) {
|
if (!answer.votes) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto delegate = container->lifetime().make_state<ListDelegate>();
|
||||||
|
const auto controller = container->lifetime().make_state<ListController>(
|
||||||
|
session,
|
||||||
|
poll,
|
||||||
|
context,
|
||||||
|
answer.option);
|
||||||
|
|
||||||
const auto percent = answer.votes * 100 / poll->totalVoters;
|
const auto percent = answer.votes * 100 / poll->totalVoters;
|
||||||
const auto rightText = (poll->quiz()
|
const auto phrase = poll->quiz()
|
||||||
? tr::lng_polls_answers_count
|
? tr::lng_polls_answers_count
|
||||||
: tr::lng_polls_votes_count)(
|
: tr::lng_polls_votes_count;
|
||||||
|
const auto sampleText = phrase(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_count_decimal,
|
lt_count_decimal,
|
||||||
answer.votes);
|
answer.votes);
|
||||||
const auto &font = st::boxDividerLabel.style.font;
|
const auto &font = st::boxDividerLabel.style.font;
|
||||||
const auto rightWidth = font->width(rightText);
|
const auto sampleWidth = font->width(sampleText);
|
||||||
const auto rightSkip = rightWidth + font->spacew * 4;
|
const auto rightSkip = sampleWidth + font->spacew * 4;
|
||||||
const auto header = container->add(
|
const auto header = container->add(
|
||||||
object_ptr<Ui::DividerLabel>(
|
object_ptr<Ui::DividerLabel>(
|
||||||
container,
|
container,
|
||||||
|
@ -310,7 +367,9 @@ ListController *CreateAnswerRows(
|
||||||
st::pollResultsHeaderPadding.bottom())));
|
st::pollResultsHeaderPadding.bottom())));
|
||||||
const auto votes = Ui::CreateChild<Ui::FlatLabel>(
|
const auto votes = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
header,
|
header,
|
||||||
rightText,
|
phrase(
|
||||||
|
lt_count_decimal,
|
||||||
|
controller->fullCount() | rpl::map(_1 + 0.)),
|
||||||
st::pollResultsVotesCount);
|
st::pollResultsVotesCount);
|
||||||
header->widthValue(
|
header->widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
@ -323,12 +382,6 @@ ListController *CreateAnswerRows(
|
||||||
container,
|
container,
|
||||||
st::boxLittleSkip));
|
st::boxLittleSkip));
|
||||||
|
|
||||||
const auto delegate = container->lifetime().make_state<ListDelegate>();
|
|
||||||
const auto controller = container->lifetime().make_state<ListController>(
|
|
||||||
session,
|
|
||||||
poll,
|
|
||||||
context,
|
|
||||||
answer.option);
|
|
||||||
const auto content = container->add(object_ptr<PeerListContent>(
|
const auto content = container->add(object_ptr<PeerListContent>(
|
||||||
container,
|
container,
|
||||||
controller,
|
controller,
|
||||||
|
@ -343,14 +396,16 @@ ListController *CreateAnswerRows(
|
||||||
container,
|
container,
|
||||||
tr::lng_polls_show_more(
|
tr::lng_polls_show_more(
|
||||||
lt_count_decimal,
|
lt_count_decimal,
|
||||||
rpl::single(answer.votes + 0.),
|
controller->leftToLoad() | rpl::map(_1 + 0.),
|
||||||
Ui::Text::Upper),
|
Ui::Text::Upper),
|
||||||
st::pollResultsShowMore)));
|
st::pollResultsShowMore)));
|
||||||
more->toggle(answer.votes > kFirstPage, anim::type::instant);
|
|
||||||
more->entity()->setClickedCallback([=] {
|
more->entity()->setClickedCallback([=] {
|
||||||
controller->allowLoadAll();
|
controller->allowLoadMore();
|
||||||
more->hide(anim::type::instant);
|
|
||||||
});
|
});
|
||||||
|
controller->leftToLoad(
|
||||||
|
) | rpl::map(_1 > 0) | rpl::start_with_next([=](bool visible) {
|
||||||
|
more->toggle(visible, anim::type::instant);
|
||||||
|
}, more->lifetime());
|
||||||
|
|
||||||
container->add(object_ptr<Ui::FixedHeightWidget>(
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
container,
|
container,
|
||||||
|
|
Loading…
Reference in New Issue