| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /*
- 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 "data/data_poll.h"
- #include "api/api_text_entities.h"
- #include "data/data_user.h"
- #include "data/data_session.h"
- #include "base/call_delayed.h"
- #include "main/main_session.h"
- #include "api/api_text_entities.h"
- #include "ui/text/text_options.h"
- namespace {
- constexpr auto kShortPollTimeout = 30 * crl::time(1000);
- constexpr auto kReloadAfterAutoCloseDelay = crl::time(1000);
- const PollAnswer *AnswerByOption(
- const std::vector<PollAnswer> &list,
- const QByteArray &option) {
- const auto i = ranges::find(
- list,
- option,
- [](const PollAnswer &a) { return a.option; });
- return (i != end(list)) ? &*i : nullptr;
- }
- PollAnswer *AnswerByOption(
- std::vector<PollAnswer> &list,
- const QByteArray &option) {
- return const_cast<PollAnswer*>(AnswerByOption(
- std::as_const(list),
- option));
- }
- } // namespace
- PollData::PollData(not_null<Data::Session*> owner, PollId id)
- : id(id)
- , _owner(owner) {
- }
- Data::Session &PollData::owner() const {
- return *_owner;
- }
- Main::Session &PollData::session() const {
- return _owner->session();
- }
- bool PollData::closeByTimer() {
- if (closed()) {
- return false;
- }
- _flags |= Flag::Closed;
- ++version;
- base::call_delayed(kReloadAfterAutoCloseDelay, &_owner->session(), [=] {
- _lastResultsUpdate = -1; // Force reload results.
- ++version;
- _owner->notifyPollUpdateDelayed(this);
- });
- return true;
- }
- bool PollData::applyChanges(const MTPDpoll &poll) {
- Expects(poll.vid().v == id);
- const auto newQuestion = TextWithEntities{
- .text = qs(poll.vquestion().data().vtext()),
- .entities = Api::EntitiesFromMTP(
- &session(),
- poll.vquestion().data().ventities().v),
- };
- const auto newFlags = (poll.is_closed() ? Flag::Closed : Flag(0))
- | (poll.is_public_voters() ? Flag::PublicVotes : Flag(0))
- | (poll.is_multiple_choice() ? Flag::MultiChoice : Flag(0))
- | (poll.is_quiz() ? Flag::Quiz : Flag(0));
- const auto newCloseDate = poll.vclose_date().value_or_empty();
- const auto newClosePeriod = poll.vclose_period().value_or_empty();
- auto newAnswers = ranges::views::all(
- poll.vanswers().v
- ) | ranges::views::transform([&](const MTPPollAnswer &data) {
- return data.match([&](const MTPDpollAnswer &answer) {
- auto result = PollAnswer();
- result.option = answer.voption().v;
- result.text = TextWithEntities{
- .text = qs(answer.vtext().data().vtext()),
- .entities = Api::EntitiesFromMTP(
- &session(),
- answer.vtext().data().ventities().v),
- };
- return result;
- });
- }) | ranges::views::take(
- kMaxOptions
- ) | ranges::to_vector;
- const auto changed1 = (question != newQuestion)
- || (closeDate != newCloseDate)
- || (closePeriod != newClosePeriod)
- || (_flags != newFlags);
- const auto changed2 = (answers != newAnswers);
- if (!changed1 && !changed2) {
- return false;
- }
- if (changed1) {
- question = newQuestion;
- closeDate = newCloseDate;
- closePeriod = newClosePeriod;
- _flags = newFlags;
- }
- if (changed2) {
- std::swap(answers, newAnswers);
- for (const auto &old : newAnswers) {
- if (const auto current = answerByOption(old.option)) {
- current->votes = old.votes;
- current->chosen = old.chosen;
- current->correct = old.correct;
- }
- }
- }
- ++version;
- return true;
- }
- bool PollData::applyResults(const MTPPollResults &results) {
- return results.match([&](const MTPDpollResults &results) {
- _lastResultsUpdate = crl::now();
- const auto newTotalVoters
- = results.vtotal_voters().value_or(totalVoters);
- auto changed = (newTotalVoters != totalVoters);
- if (const auto list = results.vresults()) {
- for (const auto &result : list->v) {
- if (applyResultToAnswers(result, results.is_min())) {
- changed = true;
- }
- }
- }
- if (const auto recent = results.vrecent_voters()) {
- const auto recentChanged = !ranges::equal(
- recentVoters,
- recent->v,
- ranges::equal_to(),
- &PeerData::id,
- peerFromMTP);
- if (recentChanged) {
- changed = true;
- recentVoters = ranges::views::all(
- recent->v
- ) | ranges::views::transform([&](MTPPeer peerId) {
- const auto peer = _owner->peer(peerFromMTP(peerId));
- return peer->isMinimalLoaded() ? peer.get() : nullptr;
- }) | ranges::views::filter([](PeerData *peer) {
- return peer != nullptr;
- }) | ranges::views::transform([](PeerData *peer) {
- return not_null(peer);
- }) | ranges::to_vector;
- }
- }
- if (results.vsolution()) {
- auto newSolution = TextWithEntities{
- results.vsolution().value_or_empty(),
- Api::EntitiesFromMTP(
- &_owner->session(),
- results.vsolution_entities().value_or_empty())
- };
- if (solution != newSolution) {
- solution = std::move(newSolution);
- changed = true;
- }
- }
- if (!changed) {
- return false;
- }
- totalVoters = newTotalVoters;
- ++version;
- return changed;
- });
- }
- bool PollData::checkResultsReload(crl::time now) {
- if (_lastResultsUpdate > 0
- && _lastResultsUpdate + kShortPollTimeout > now) {
- return false;
- } else if (closed() && _lastResultsUpdate >= 0) {
- return false;
- }
- _lastResultsUpdate = now;
- return true;
- }
- PollAnswer *PollData::answerByOption(const QByteArray &option) {
- return AnswerByOption(answers, option);
- }
- const PollAnswer *PollData::answerByOption(const QByteArray &option) const {
- return AnswerByOption(answers, option);
- }
- bool PollData::applyResultToAnswers(
- const MTPPollAnswerVoters &result,
- bool isMinResults) {
- return result.match([&](const MTPDpollAnswerVoters &voters) {
- const auto &option = voters.voption().v;
- const auto answer = answerByOption(option);
- if (!answer) {
- return false;
- }
- auto changed = (answer->votes != voters.vvoters().v);
- if (changed) {
- answer->votes = voters.vvoters().v;
- }
- if (!isMinResults) {
- if (answer->chosen != voters.is_chosen()) {
- answer->chosen = voters.is_chosen();
- changed = true;
- }
- }
- if (voters.is_correct() && !answer->correct) {
- answer->correct = voters.is_correct();
- changed = true;
- }
- return changed;
- });
- }
- void PollData::setFlags(Flags flags) {
- if (_flags != flags) {
- _flags = flags;
- ++version;
- }
- }
- PollData::Flags PollData::flags() const {
- return _flags;
- }
- bool PollData::voted() const {
- return ranges::contains(answers, true, &PollAnswer::chosen);
- }
- bool PollData::closed() const {
- return (_flags & Flag::Closed);
- }
- bool PollData::publicVotes() const {
- return (_flags & Flag::PublicVotes);
- }
- bool PollData::multiChoice() const {
- return (_flags & Flag::MultiChoice);
- }
- bool PollData::quiz() const {
- return (_flags & Flag::Quiz);
- }
- MTPPoll PollDataToMTP(not_null<const PollData*> poll, bool close) {
- const auto convert = [&](const PollAnswer &answer) {
- return MTP_pollAnswer(
- MTP_textWithEntities(
- MTP_string(answer.text.text),
- Api::EntitiesToMTP(&poll->session(), answer.text.entities)),
- MTP_bytes(answer.option));
- };
- auto answers = QVector<MTPPollAnswer>();
- answers.reserve(poll->answers.size());
- ranges::transform(
- poll->answers,
- ranges::back_inserter(answers),
- convert);
- using Flag = MTPDpoll::Flag;
- const auto flags = ((poll->closed() || close) ? Flag::f_closed : Flag(0))
- | (poll->multiChoice() ? Flag::f_multiple_choice : Flag(0))
- | (poll->publicVotes() ? Flag::f_public_voters : Flag(0))
- | (poll->quiz() ? Flag::f_quiz : Flag(0))
- | (poll->closePeriod > 0 ? Flag::f_close_period : Flag(0))
- | (poll->closeDate > 0 ? Flag::f_close_date : Flag(0));
- return MTP_poll(
- MTP_long(poll->id),
- MTP_flags(flags),
- MTP_textWithEntities(
- MTP_string(poll->question.text),
- Api::EntitiesToMTP(&poll->session(), poll->question.entities)),
- MTP_vector<MTPPollAnswer>(answers),
- MTP_int(poll->closePeriod),
- MTP_int(poll->closeDate));
- }
- MTPInputMedia PollDataToInputMedia(
- not_null<const PollData*> poll,
- bool close) {
- auto inputFlags = MTPDinputMediaPoll::Flag(0)
- | (poll->quiz()
- ? MTPDinputMediaPoll::Flag::f_correct_answers
- : MTPDinputMediaPoll::Flag(0));
- auto correct = QVector<MTPbytes>();
- for (const auto &answer : poll->answers) {
- if (answer.correct) {
- correct.push_back(MTP_bytes(answer.option));
- }
- }
- auto solution = poll->solution;
- const auto prepareFlags = Ui::ItemTextDefaultOptions().flags;
- TextUtilities::PrepareForSending(solution, prepareFlags);
- TextUtilities::Trim(solution);
- const auto sentEntities = Api::EntitiesToMTP(
- &poll->session(),
- solution.entities,
- Api::ConvertOption::SkipLocal);
- if (!solution.text.isEmpty()) {
- inputFlags |= MTPDinputMediaPoll::Flag::f_solution;
- }
- if (!sentEntities.v.isEmpty()) {
- inputFlags |= MTPDinputMediaPoll::Flag::f_solution_entities;
- }
- return MTP_inputMediaPoll(
- MTP_flags(inputFlags),
- PollDataToMTP(poll, close),
- MTP_vector<MTPbytes>(correct),
- MTP_string(solution.text),
- sentEntities);
- }
|