Added api support for custom emoji in polls.

This commit is contained in:
23rd 2024-04-22 13:12:26 +03:00 committed by John Preston
parent b3ae843f0e
commit e6c22ec1ca
8 changed files with 83 additions and 52 deletions

View file

@ -456,10 +456,16 @@ void Options::Option::removePlaceholder() const {
PollAnswer Options::Option::toPollAnswer(int index) const {
Expects(index >= 0 && index < kMaxOptionsCount);
const auto text = field()->getTextWithTags();
auto result = PollAnswer{
field()->getLastText().trimmed(),
QByteArray(1, ('0' + index))
TextWithEntities{
.text = text.text,
.entities = TextUtilities::ConvertTextTagsToEntities(text.tags),
},
QByteArray(1, ('0' + index)),
};
TextUtilities::Trim(result.text);
result.correct = _correct ? _correct->entity()->Checkbox::checked() : false;
return result;
}
@ -1029,9 +1035,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
};
const auto collectResult = [=] {
const auto textWithTags = question->getTextWithTags();
using Flag = PollData::Flag;
auto result = PollData(&_controller->session().data(), id);
result.question = question->getLastText().trimmed();
result.question.text = textWithTags.text;
result.question.entities = TextUtilities::ConvertTextTagsToEntities(
textWithTags.tags);
TextUtilities::Trim(result.question);
result.answers = options->toPollAnswers();
const auto solutionWithTags = quiz->checked()
? solution->getTextWithAppliedMarkdown()

View file

@ -1857,23 +1857,21 @@ TextWithEntities MediaPoll::notificationText() const {
}
QString MediaPoll::pinnedTextSubstring() const {
return QChar(171) + _poll->question + QChar(187);
return QChar(171) + _poll->question.text + QChar(187);
}
TextForMimeData MediaPoll::clipboardText() const {
const auto text = u"[ "_q
+ tr::lng_in_dlg_poll(tr::now)
+ u" : "_q
+ _poll->question
+ u" ]"_q
+ ranges::accumulate(
ranges::views::all(
_poll->answers
) | ranges::views::transform([](const PollAnswer &answer) {
return "\n- " + answer.text;
}),
QString());
return TextForMimeData::Simple(text);
auto result = TextWithEntities();
result
.append(u"[ "_q)
.append(tr::lng_in_dlg_poll(tr::now))
.append(u" : "_q)
.append(_poll->question)
.append(u" ]"_q);
for (const auto &answer : _poll->answers) {
result.append(u"\n- "_q).append(answer.text);
}
return TextForMimeData::Rich(std::move(result));
}
bool MediaPoll::updateInlineResultMedia(const MTPMessageMedia &media) {

View file

@ -7,6 +7,7 @@ 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"
@ -69,7 +70,12 @@ bool PollData::closeByTimer() {
bool PollData::applyChanges(const MTPDpoll &poll) {
Expects(poll.vid().v == id);
const auto newQuestion = qs(poll.vquestion().data().vtext());
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))
@ -78,11 +84,16 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
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) {
) | ranges::views::transform([&](const MTPPollAnswer &data) {
return data.match([&](const MTPDpollAnswer &answer) {
auto result = PollAnswer();
result.option = answer.voption().v;
result.text = qs(answer.vtext().data().vtext());
result.text = TextWithEntities{
.text = qs(answer.vtext().data().vtext()),
.entities = Api::EntitiesFromMTP(
&session(),
answer.vtext().data().ventities().v),
};
return result;
});
}) | ranges::views::take(
@ -251,11 +262,11 @@ bool PollData::quiz() const {
}
MTPPoll PollDataToMTP(not_null<const PollData*> poll, bool close) {
const auto convert = [](const PollAnswer &answer) {
const auto convert = [&](const PollAnswer &answer) {
return MTP_pollAnswer(
MTP_textWithEntities(
MTP_string(answer.text),
MTP_vector<MTPMessageEntity>()),
MTP_string(answer.text.text),
Api::EntitiesToMTP(&poll->session(), answer.text.entities)),
MTP_bytes(answer.option));
};
auto answers = QVector<MTPPollAnswer>();
@ -275,8 +286,8 @@ MTPPoll PollDataToMTP(not_null<const PollData*> poll, bool close) {
MTP_long(poll->id),
MTP_flags(flags),
MTP_textWithEntities(
MTP_string(poll->question),
MTP_vector<MTPMessageEntity>()),
MTP_string(poll->question.text),
Api::EntitiesToMTP(&poll->session(), poll->question.entities)),
MTP_vector<MTPPollAnswer>(answers),
MTP_int(poll->closePeriod),
MTP_int(poll->closeDate));

View file

@ -16,7 +16,7 @@ class Session;
} // namespace Main
struct PollAnswer {
QString text;
TextWithEntities text;
QByteArray option;
int votes = 0;
bool chosen = false;
@ -65,7 +65,7 @@ struct PollData {
[[nodiscard]] bool quiz() const;
PollId id = 0;
QString question;
TextWithEntities question;
std::vector<PollAnswer> answers;
std::vector<not_null<PeerData*>> recentVoters;
std::vector<QByteArray> sendingVotes;

View file

@ -1307,15 +1307,15 @@ void AddPollActions(
const auto radio = QString::fromUtf8(kRadio);
auto text = poll->question;
for (const auto &answer : poll->answers) {
text += '\n' + radio + answer.text;
text.append('\n').append(radio).append(answer.text);
}
if (!Ui::SkipTranslate({ text })) {
if (!Ui::SkipTranslate(text)) {
menu->addAction(tr::lng_context_translate(tr::now), [=] {
controller->show(Box(
Ui::TranslateBox,
item->history()->peer,
MsgId(),
TextWithEntities{ .text = text },
std::move(text),
item->forbidsForward()));
}, &st::menuIconTranslate);
}

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/media/history_view_poll.h"
#include "core/ui_integration.h" // Core::MarkedTextContext.
#include "lang/lang_keys.h"
#include "history/history.h"
#include "history/history_item.h"
@ -154,7 +155,10 @@ struct Poll::SendingAnimation {
struct Poll::Answer {
Answer();
void fillData(not_null<PollData*> poll, const PollAnswer &original);
void fillData(
not_null<PollData*> poll,
const PollAnswer &original,
Core::MarkedTextContext context);
Ui::Text::String text;
QByteArray option;
@ -201,16 +205,18 @@ Poll::Answer::Answer() : text(st::msgMinWidth / 2) {
void Poll::Answer::fillData(
not_null<PollData*> poll,
const PollAnswer &original) {
const PollAnswer &original,
Core::MarkedTextContext context) {
chosen = original.chosen;
correct = poll->quiz() ? original.correct : chosen;
if (!text.isEmpty() && text.toString() == original.text) {
if (!text.isEmpty() && text.toTextWithEntities() == original.text) {
return;
}
text.setText(
text.setMarkedText(
st::historyPollAnswerStyle,
original.text,
Ui::WebpageTextTitleOptions());
Ui::WebpageTextTitleOptions(),
context);
}
Poll::CloseInformation::CloseInformation(
@ -383,13 +389,18 @@ void Poll::updateTexts() {
const auto willStartAnimation = checkAnimationStart();
const auto voted = _voted;
if (_question.toString() != _poll->question) {
if (_question.toTextWithEntities() != _poll->question) {
auto options = Ui::WebpageTextTitleOptions();
options.maxw = options.maxh = 0;
_question.setText(
_question.setMarkedText(
st::historyPollQuestionStyle,
_poll->question,
options);
options,
Core::MarkedTextContext{
.session = &_poll->session(),
.customEmojiRepaint = [=] { repaint(); },
.customEmojiLoopLimit = 2,
});
}
if (_flags != _poll->flags() || _subtitle.isEmpty()) {
using Flag = PollData::Flag;
@ -514,6 +525,11 @@ void Poll::updateRecentVoters() {
}
void Poll::updateAnswers() {
const auto context = Core::MarkedTextContext{
.session = &_poll->session(),
.customEmojiRepaint = [=] { repaint(); },
.customEmojiLoopLimit = 2,
};
const auto changed = !ranges::equal(
_answers,
_poll->answers,
@ -523,7 +539,7 @@ void Poll::updateAnswers() {
if (!changed) {
auto &&answers = ranges::views::zip(_answers, _poll->answers);
for (auto &&[answer, original] : answers) {
answer.fillData(_poll, original);
answer.fillData(_poll, original, context);
}
return;
}
@ -532,7 +548,7 @@ void Poll::updateAnswers() {
) | ranges::views::transform([&](const PollAnswer &answer) {
auto result = Answer();
result.option = answer.option;
result.fillData(_poll, answer);
result.fillData(_poll, answer, context);
return result;
}) | ranges::to_vector;

View file

@ -8,17 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/polls/info_polls_results_inner_widget.h"
#include "info/polls/info_polls_results_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/controls/peer_list_dummy.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"
@ -26,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
namespace Info {
@ -461,10 +456,11 @@ ListController *CreateAnswerRows(
container.get(),
object_ptr<Ui::FlatLabel>(
container,
(answer.text
+ QString::fromUtf8(" \xe2\x80\x94 ")
+ QString::number(percent)
+ "%"),
rpl::single(
TextWithEntities(answer.text)
.append(QString::fromUtf8(" \xe2\x80\x94 "))
.append(QString::number(percent))
.append('%')),
st::boxDividerLabel),
style::margins(
st::pollResultsHeaderPadding.left(),
@ -613,7 +609,7 @@ void InnerWidget::setupContent() {
_content->add(
object_ptr<Ui::FlatLabel>(
_content,
_poll->question,
rpl::single(_poll->question),
st::pollResultsQuestion),
style::margins{
st::boxRowPadding.left(),

View file

@ -994,7 +994,7 @@ TextWithEntities Manager::ComposeReactionNotification(
lt_reaction,
reactionWithEntities,
lt_title,
Ui::Text::WithEntities(poll->question),
poll->question,
Ui::Text::WithEntities);
} else if (media->game()) {
return simple(tr::lng_reaction_game);