Display correct poll subtitle and quiz answer.

This commit is contained in:
John Preston 2020-01-09 17:13:35 +03:00
parent d57905c2b3
commit 95b2886bad
6 changed files with 118 additions and 28 deletions

View File

@ -2166,6 +2166,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_launch_exe_dont_ask" = "Don't ask me again";
"lng_polls_anonymous" = "Anonymous Poll";
"lng_polls_public" = "Poll";
"lng_polls_anonymous_quiz" = "Anonymous Quiz";
"lng_polls_public_quiz" = "Quiz";
"lng_polls_closed" = "Final results";
"lng_polls_votes_count#one" = "{count} vote";
"lng_polls_votes_count#other" = "{count} votes";

View File

@ -41,7 +41,10 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
Expects(poll.vid().v == id);
const auto newQuestion = qs(poll.vquestion());
const auto newClosed = poll.is_closed();
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));
auto newAnswers = ranges::view::all(
poll.vanswers().v
) | ranges::view::transform([](const MTPPollAnswer &data) {
@ -56,14 +59,14 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
) | ranges::to_vector;
const auto changed1 = (question != newQuestion)
|| (closed != newClosed);
|| (_flags != newFlags);
const auto changed2 = (answers != newAnswers);
if (!changed1 && !changed2) {
return false;
}
if (changed1) {
question = newQuestion;
closed = newClosed;
_flags = newFlags;
}
if (changed2) {
std::swap(answers, newAnswers);
@ -71,6 +74,7 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
if (const auto current = answerByOption(old.option)) {
current->votes = old.votes;
current->chosen = old.chosen;
current->correct = old.correct;
}
}
}
@ -104,7 +108,7 @@ bool PollData::applyResults(const MTPPollResults &results) {
void PollData::checkResultsReload(not_null<HistoryItem*> item, crl::time now) {
if (lastResultsUpdate && lastResultsUpdate + kShortPollTimeout > now) {
return;
} else if (closed) {
} else if (closed()) {
return;
}
lastResultsUpdate = now;
@ -137,17 +141,42 @@ bool PollData::applyResultToAnswers(
answer->chosen = voters.is_chosen();
changed = true;
}
if (answer->correct != voters.is_correct()) {
answer->correct = voters.is_correct();
changed = true;
}
} else if (const auto existing = answerByOption(option)) {
answer->chosen = existing->chosen;
answer->correct = existing->correct;
}
return changed;
});
}
PollData::Flags PollData::flags() const {
return _flags;
}
bool PollData::voted() const {
return ranges::find(answers, true, &PollAnswer::chosen) != end(answers);
}
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) {
const auto convert = [](const PollAnswer &answer) {
return MTP_pollAnswer(

View File

@ -12,6 +12,7 @@ struct PollAnswer {
QByteArray option;
int votes = 0;
bool chosen = false;
bool correct = false;
};
inline bool operator==(const PollAnswer &a, const PollAnswer &b) {
@ -26,20 +27,34 @@ inline bool operator!=(const PollAnswer &a, const PollAnswer &b) {
struct PollData {
explicit PollData(PollId id);
enum class Flag {
Closed = 0x01,
PublicVotes = 0x02,
MultiChoice = 0x04,
Quiz = 0x08,
};
friend inline constexpr bool is_flag_type(Flag) { return true; };
using Flags = base::flags<Flag>;
bool applyChanges(const MTPDpoll &poll);
bool applyResults(const MTPPollResults &results);
void checkResultsReload(not_null<HistoryItem*> item, crl::time now);
PollAnswer *answerByOption(const QByteArray &option);
const PollAnswer *answerByOption(const QByteArray &option) const;
[[nodiscard]] PollAnswer *answerByOption(const QByteArray &option);
[[nodiscard]] const PollAnswer *answerByOption(
const QByteArray &option) const;
bool voted() const;
[[nodiscard]] Flags flags() const;
[[nodiscard]] bool voted() const;
[[nodiscard]] bool closed() const;
[[nodiscard]] bool publicVotes() const;
[[nodiscard]] bool multiChoice() const;
[[nodiscard]] bool quiz() const;
PollId id = 0;
QString question;
std::vector<PollAnswer> answers;
int totalVoters = 0;
bool closed = false;
QByteArray sendingVote;
crl::time lastResultsUpdate = 0;
@ -52,6 +67,8 @@ private:
const MTPPollAnswerVoters &result,
bool isMinResults);
Flags _flags = Flags();
};
MTPPoll PollDataToMTP(not_null<const PollData*> poll);

View File

@ -1719,8 +1719,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
}
if (const auto media = item->media()) {
if (const auto poll = media->poll()) {
if (!poll->closed) {
if (poll->voted()) {
if (!poll->closed()) {
if (poll->voted() && !poll->quiz()) {
_menu->addAction(tr::lng_polls_retract(tr::now), [=] {
session().api().sendPollVotes(itemId, {});
});

View File

@ -112,6 +112,8 @@ struct Poll::AnswerAnimation {
anim::value percent;
anim::value filling;
anim::value opacity;
bool chosen = false;
bool correct = false;
};
struct Poll::AnswersAnimation {
@ -132,7 +134,7 @@ struct Poll::SendingAnimation {
struct Poll::Answer {
Answer();
void fillText(const PollAnswer &original);
void fillData(not_null<PollData*> poll, const PollAnswer &original);
Ui::Text::String text;
QByteArray option;
@ -142,6 +144,7 @@ struct Poll::Answer {
float64 filling = 0.;
QString votesPercentString;
bool chosen = false;
bool correct = false;
ClickHandlerPtr handler;
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
};
@ -159,7 +162,11 @@ Poll::SendingAnimation::SendingAnimation(
Poll::Answer::Answer() : text(st::msgMinWidth / 2) {
}
void Poll::Answer::fillText(const PollAnswer &original) {
void Poll::Answer::fillData(
not_null<PollData*> poll,
const PollAnswer &original) {
chosen = original.chosen;
correct = poll->quiz() ? original.correct : chosen;
if (!text.isEmpty() && text.toString() == original.text) {
return;
}
@ -218,7 +225,7 @@ QSize Poll::countOptimalSize() {
}
bool Poll::showVotes() const {
return _voted || _closed;
return _voted || (_flags & PollData::Flag::Closed);
}
bool Poll::canVote() const {
@ -309,11 +316,20 @@ void Poll::updateTexts() {
_poll->question,
options);
}
if (_closed != _poll->closed || _subtitle.isEmpty()) {
_closed = _poll->closed;
if (_flags != _poll->flags() || _subtitle.isEmpty()) {
using Flag = PollData::Flag;
_flags = _poll->flags();
_subtitle.setText(
st::msgDateTextStyle,
_closed ? tr::lng_polls_closed(tr::now) : tr::lng_polls_anonymous(tr::now));
((_flags & Flag::Closed)
? tr::lng_polls_closed(tr::now)
: (_flags & Flag::Quiz)
? ((_flags & Flag::PublicVotes)
? tr::lng_polls_public_quiz(tr::now)
: tr::lng_polls_anonymous_quiz(tr::now))
: ((_flags & Flag::PublicVotes)
? tr::lng_polls_public(tr::now)
: tr::lng_polls_anonymous(tr::now))));
}
updateAnswers();
@ -334,16 +350,16 @@ void Poll::updateAnswers() {
if (!changed) {
auto &&answers = ranges::view::zip(_answers, _poll->answers);
for (auto &&[answer, original] : answers) {
answer.fillText(original);
answer.fillData(_poll, original);
}
return;
}
_answers = ranges::view::all(
_poll->answers
) | ranges::view::transform([](const PollAnswer &answer) {
) | ranges::view::transform([&](const PollAnswer &answer) {
auto result = Answer();
result.option = answer.option;
result.fillText(answer);
result.fillData(_poll, answer);
return result;
}) | ranges::to_vector;
@ -592,6 +608,8 @@ int Poll::paintAnswer(
p.setOpacity(sqrt(opacity));
paintFilling(
p,
animation->chosen,
animation->correct,
animation->filling.current(),
left,
top,
@ -613,6 +631,8 @@ int Poll::paintAnswer(
selection);
paintFilling(
p,
answer.chosen,
answer.correct,
answer.filling,
left,
top,
@ -696,6 +716,8 @@ void Poll::paintPercent(
void Poll::paintFilling(
Painter &p,
bool chosen,
bool correct,
float64 filling,
int left,
int top,
@ -712,15 +734,29 @@ void Poll::paintFilling(
top += st::historyPollAnswerPadding.top();
const auto bar = outbg ? (selected ? st::msgWaveformOutActiveSelected : st::msgWaveformOutActive) : (selected ? st::msgWaveformInActiveSelected : st::msgWaveformInActive);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(bar);
const auto thickness = st::historyPollFillingHeight;
const auto max = awidth - st::historyPollFillingRight;
const auto size = anim::interpolate(st::historyPollFillingMin, max, filling);
const auto radius = st::historyPollFillingRadius;
const auto ftop = bottom - st::historyPollFillingBottom - st::historyPollFillingHeight;
p.drawRoundedRect(aleft, ftop, size, st::historyPollFillingHeight, radius, radius);
const auto ftop = bottom - st::historyPollFillingBottom - thickness;
if (chosen && !correct) {
p.setBrush(st::boxTextFgError);
} else {
const auto bar = outbg ? (selected ? st::msgWaveformOutActiveSelected : st::msgWaveformOutActive) : (selected ? st::msgWaveformInActiveSelected : st::msgWaveformInActive);
p.setBrush(bar);
}
auto barleft = aleft;
auto barwidth = size;
if (chosen || correct) {
p.drawEllipse(aleft, ftop - thickness, thickness * 3, thickness * 3);
barleft += thickness * 3 - radius;
barwidth -= thickness * 3 - radius;
}
p.drawRoundedRect(barleft, ftop, barwidth, thickness, radius, radius);
}
bool Poll::answerVotesChanged() const {
@ -748,6 +784,8 @@ void Poll::saveStateInAnimation() const {
result.percent = show ? float64(answer.votesPercent) : 0.;
result.filling = show ? answer.filling : 0.;
result.opacity = show ? 1. : 0.;
result.chosen = answer.chosen;
result.correct = answer.correct;
return result;
};
ranges::transform(
@ -761,7 +799,7 @@ bool Poll::checkAnimationStart() const {
// Skip initial changes.
return false;
}
const auto result = (showVotes() != (_poll->voted() || _poll->closed))
const auto result = (showVotes() != (_poll->voted() || _poll->closed()))
|| answerVotesChanged();
if (result) {
saveStateInAnimation();
@ -780,6 +818,8 @@ void Poll::startAnswersAnimation() const {
data.percent.start(show ? float64(answer.votesPercent) : 0.);
data.filling.start(show ? answer.filling : 0.);
data.opacity.start(show ? 1. : 0.);
data.chosen = data.chosen || answer.chosen;
data.correct = data.correct || answer.correct;
}
_answersAnimation->progress.start(
[=] { history()->owner().requestViewRepaint(_parent); },

View File

@ -8,8 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "history/view/media/history_view_media.h"
struct PollAnswer;
#include "data/data_poll.h"
namespace HistoryView {
@ -99,6 +98,8 @@ private:
TextSelection selection) const;
void paintFilling(
Painter &p,
bool chosen,
bool correct,
float64 filling,
int left,
int top,
@ -115,11 +116,11 @@ private:
void toggleRipple(Answer &answer, bool pressed);
not_null<PollData*> _poll;
const not_null<PollData*> _poll;
int _pollVersion = 0;
int _totalVotes = 0;
bool _voted = false;
bool _closed = false;
PollData::Flags _flags = PollData::Flags();
Ui::Text::String _question;
Ui::Text::String _subtitle;