mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
[poll-view] Added initial support of reply to poll answers.
This commit is contained in:
@@ -4973,6 +4973,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_reply_msg" = "Reply";
|
||||
"lng_context_quote_and_reply" = "Quote & Reply";
|
||||
"lng_context_reply_to_task" = "Reply to Task";
|
||||
"lng_context_reply_to_poll_option" = "Reply to Option";
|
||||
"lng_context_edit_msg" = "Edit";
|
||||
"lng_context_draw" = "Edit Image";
|
||||
"lng_context_add_factcheck" = "Add Fact Check";
|
||||
@@ -5214,6 +5215,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_preview_reply_to" = "Reply to {name}";
|
||||
"lng_preview_reply_to_quote" = "Reply to quote from {name}";
|
||||
"lng_preview_reply_to_task" = "Reply to task from {title}";
|
||||
"lng_preview_reply_to_poll_option" = "Reply to poll option from {title}";
|
||||
|
||||
"lng_suggest_bar_title" = "Suggest a Post Below";
|
||||
"lng_suggest_bar_text" = "Click to offer a price for publishing.";
|
||||
|
||||
@@ -220,10 +220,13 @@ void Polls::reloadResults(not_null<HistoryItem*> item) {
|
||||
if (!item->isRegular() || _pollReloadRequestIds.contains(itemId)) {
|
||||
return;
|
||||
}
|
||||
const auto media = item->media();
|
||||
const auto poll = media ? media->poll() : nullptr;
|
||||
const auto pollHash = poll ? poll->hash : uint64(0);
|
||||
const auto requestId = _api.request(MTPmessages_GetPollResults(
|
||||
item->history()->peer->input(),
|
||||
MTP_int(item->id),
|
||||
MTP_long(0)
|
||||
MTP_long(pollHash)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_pollReloadRequestIds.erase(itemId);
|
||||
_session->updates().applyUpdates(result);
|
||||
|
||||
@@ -19,7 +19,8 @@ constexpr auto kReactionsCountEmojiProperty = 0x05;
|
||||
constexpr auto kDocumentFilenameTooltipProperty = 0x06;
|
||||
constexpr auto kPhoneNumberLinkProperty = 0x07;
|
||||
constexpr auto kTodoListItemIdProperty = 0x08;
|
||||
constexpr auto kFastShareProperty = 0x09;
|
||||
constexpr auto kPollOptionProperty = 0x09;
|
||||
constexpr auto kFastShareProperty = 0x0a;
|
||||
|
||||
namespace Ui {
|
||||
class Show;
|
||||
|
||||
@@ -91,7 +91,10 @@ MTPInputReplyTo ReplyToForMTP(
|
||||
| (quoteEntities.v.isEmpty()
|
||||
? Flag()
|
||||
: Flag::f_quote_entities)
|
||||
| (replyTo.todoItemId ? Flag::f_todo_item_id : Flag())),
|
||||
| (replyTo.todoItemId ? Flag::f_todo_item_id : Flag())
|
||||
| (replyTo.pollOption.isEmpty()
|
||||
? Flag()
|
||||
: Flag::f_poll_option)),
|
||||
MTP_int(replyTo.messageId ? replyTo.messageId.msg : 0),
|
||||
MTP_int(replyTo.topicRootId),
|
||||
(external
|
||||
@@ -104,7 +107,7 @@ MTPInputReplyTo ReplyToForMTP(
|
||||
? history->owner().peer(replyToMonoforumPeerId)->input()
|
||||
: MTPInputPeer()),
|
||||
MTP_int(replyTo.todoItemId),
|
||||
MTPbytes()); // poll_option
|
||||
MTP_bytes(replyTo.pollOption));
|
||||
} else if (history->peer->amMonoforumAdmin()
|
||||
&& replyTo.monoforumPeerId) {
|
||||
const auto replyToMonoforumPeer = replyTo.monoforumPeerId
|
||||
|
||||
@@ -176,9 +176,10 @@ struct MessageHighlightId {
|
||||
TextWithEntities quote;
|
||||
int quoteOffset = 0;
|
||||
int todoItemId = 0;
|
||||
QByteArray pollOption;
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return quote.empty() && !todoItemId;
|
||||
return quote.empty() && !todoItemId && pollOption.isEmpty();
|
||||
}
|
||||
[[nodiscard]] friend inline bool operator==(
|
||||
const MessageHighlightId &a,
|
||||
@@ -193,9 +194,10 @@ struct FullReplyTo {
|
||||
PeerId monoforumPeerId = 0;
|
||||
int quoteOffset = 0;
|
||||
int todoItemId = 0;
|
||||
QByteArray pollOption;
|
||||
|
||||
[[nodiscard]] MessageHighlightId highlight() const {
|
||||
return { quote, quoteOffset, todoItemId };
|
||||
return { quote, quoteOffset, todoItemId, pollOption };
|
||||
}
|
||||
[[nodiscard]] bool replying() const {
|
||||
return messageId || (storyId && storyId.peer);
|
||||
|
||||
@@ -161,6 +161,7 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
|
||||
}
|
||||
}
|
||||
}
|
||||
hash = poll.vhash().v;
|
||||
++version;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ struct PollData {
|
||||
TimeId closeDate = 0;
|
||||
int totalVoters = 0;
|
||||
int version = 0;
|
||||
uint64 hash = 0;
|
||||
|
||||
static constexpr auto kMaxOptions = 32;
|
||||
|
||||
|
||||
@@ -2501,6 +2501,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
const auto todoListTaskId = link
|
||||
? link->property(kTodoListItemIdProperty).toInt()
|
||||
: 0;
|
||||
const auto pollOptionLink = link
|
||||
? link->property(kPollOptionProperty).toByteArray()
|
||||
: QByteArray();
|
||||
const auto session = &this->session();
|
||||
_whoReactedMenuLifetime.destroy();
|
||||
if (!clickedReaction.empty() && leaderOrSelf) {
|
||||
@@ -2909,6 +2912,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
? tr::lng_context_quote_and_reply
|
||||
: todoListTaskId
|
||||
? tr::lng_context_reply_to_task
|
||||
: !pollOptionLink.isEmpty()
|
||||
? tr::lng_context_reply_to_poll_option
|
||||
: tr::lng_context_reply_msg)(
|
||||
tr::now,
|
||||
Ui::Text::FixAmpersandInAction);
|
||||
@@ -2920,6 +2925,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
.quote = selected.highlight.quote,
|
||||
.quoteOffset = selected.highlight.quoteOffset,
|
||||
.todoItemId = todoListTaskId,
|
||||
.pollOption = pollOptionLink,
|
||||
});
|
||||
if (!selected.highlight.quote.empty()) {
|
||||
_widget->clearSelected();
|
||||
|
||||
@@ -361,6 +361,8 @@ ReplyFields ReplyFields::clone(not_null<HistoryItem*> parent) const {
|
||||
.messageId = messageId,
|
||||
.topMessageId = topMessageId,
|
||||
.storyId = storyId,
|
||||
.todoItemId = todoItemId,
|
||||
.pollOption = pollOption,
|
||||
.quoteOffset = quoteOffset,
|
||||
.manualQuote = manualQuote,
|
||||
.topicPost = topicPost,
|
||||
@@ -389,6 +391,7 @@ ReplyFields ReplyFieldsFromMTP(
|
||||
result.topicPost = data.is_forum_topic() ? 1 : 0;
|
||||
}
|
||||
result.todoItemId = data.vtodo_item_id().value_or_empty();
|
||||
result.pollOption = data.vpoll_option().value_or_empty();
|
||||
if (const auto header = data.vreply_from()) {
|
||||
const auto &data = header->data();
|
||||
result.externalPostAuthor
|
||||
@@ -443,6 +446,8 @@ FullReplyTo ReplyToFromMTP(
|
||||
data.vquote_entities().value_or_empty()),
|
||||
};
|
||||
result.quoteOffset = data.vquote_offset().value_or_empty();
|
||||
result.todoItemId = data.vtodo_item_id().value_or_empty();
|
||||
result.pollOption = data.vpoll_option().value_or_empty();
|
||||
return result;
|
||||
}, [&](const MTPDinputReplyToStory &data) {
|
||||
if (const auto parsed = Data::PeerFromInputMTP(
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "data/data_poll.h"
|
||||
#include "history/history_item.h"
|
||||
#include "spellcheck/spellcheck_types.h" // LanguageId.
|
||||
#include "ui/empty_userpic.h"
|
||||
@@ -286,6 +287,7 @@ struct ReplyFields {
|
||||
MsgId topMessageId = 0;
|
||||
StoryId storyId = 0;
|
||||
int todoItemId = 0;
|
||||
QByteArray pollOption;
|
||||
uint32 quoteOffset : 30 = 0;
|
||||
uint32 manualQuote : 1 = 0;
|
||||
uint32 topicPost : 1 = 0;
|
||||
|
||||
@@ -906,6 +906,9 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
|
||||
? Flag()
|
||||
: Flag::f_quote_entities)
|
||||
| (replyTo.todoItemId ? Flag::f_todo_item_id : Flag())
|
||||
| (replyTo.pollOption.isEmpty()
|
||||
? Flag()
|
||||
: Flag::f_poll_option)
|
||||
| (topicPost ? Flag::f_forum_topic : Flag())),
|
||||
MTP_int(replyTo.messageId.msg),
|
||||
peerToMTP(externalPeerId),
|
||||
@@ -916,7 +919,7 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
|
||||
quoteEntities,
|
||||
MTP_int(replyTo.quoteOffset),
|
||||
MTP_int(replyTo.todoItemId),
|
||||
MTPbytes()); // poll_option
|
||||
MTP_bytes(replyTo.pollOption));
|
||||
}
|
||||
return MTPMessageReplyHeader();
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ Ui::ChatPaintHighlight ElementHighlighter::state(
|
||||
auto result = _animation.state();
|
||||
result.range = _highlighted.part;
|
||||
result.todoItemId = _highlighted.todoListId;
|
||||
result.pollOption = _highlighted.pollOption;
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
@@ -90,20 +91,37 @@ ElementHighlighter::Highlight ElementHighlighter::computeHighlight(
|
||||
leaderId,
|
||||
leaderView->selectionFromQuote(quote),
|
||||
quote.highlight.todoItemId,
|
||||
quote.highlight.pollOption,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { leaderId, {}, quote.highlight.todoItemId };
|
||||
return {
|
||||
leaderId,
|
||||
{},
|
||||
quote.highlight.todoItemId,
|
||||
quote.highlight.pollOption,
|
||||
};
|
||||
} else if (quote.highlight.quote.empty()) {
|
||||
return { item->fullId(), {}, quote.highlight.todoItemId };
|
||||
return {
|
||||
item->fullId(),
|
||||
{},
|
||||
quote.highlight.todoItemId,
|
||||
quote.highlight.pollOption,
|
||||
};
|
||||
} else if (const auto view = _viewForItem(item)) {
|
||||
return {
|
||||
item->fullId(),
|
||||
view->selectionFromQuote(quote),
|
||||
quote.highlight.todoItemId,
|
||||
quote.highlight.pollOption,
|
||||
};
|
||||
}
|
||||
return { item->fullId(), {}, quote.highlight.todoItemId };
|
||||
return {
|
||||
item->fullId(),
|
||||
{},
|
||||
quote.highlight.todoItemId,
|
||||
quote.highlight.pollOption,
|
||||
};
|
||||
}
|
||||
|
||||
void ElementHighlighter::highlight(Highlight data) {
|
||||
|
||||
@@ -66,6 +66,7 @@ private:
|
||||
FullMsgId itemId;
|
||||
TextSelection part;
|
||||
int todoListId = 0;
|
||||
QByteArray pollOption;
|
||||
|
||||
explicit operator bool() const {
|
||||
return itemId.operator bool();
|
||||
|
||||
@@ -1592,12 +1592,18 @@ int HistoryWidget::itemTopForHighlight(
|
||||
if (heightLeft >= 0) {
|
||||
return std::max(itemTop - (heightLeft / 2), 0);
|
||||
} else if (const auto highlight = itemHighlight(item)
|
||||
; (!highlight.range.empty() || highlight.todoItemId)
|
||||
; (!highlight.range.empty()
|
||||
|| highlight.todoItemId
|
||||
|| !highlight.pollOption.isEmpty())
|
||||
&& !IsSubGroupSelection(highlight.range)) {
|
||||
const auto sel = highlight.range;
|
||||
const auto single = st::messageTextStyle.font->height;
|
||||
const auto todoy = sel.empty()
|
||||
? HistoryView::FindViewTaskY(view, highlight.todoItemId)
|
||||
? (highlight.todoItemId
|
||||
? HistoryView::FindViewTaskY(view, highlight.todoItemId)
|
||||
: !highlight.pollOption.isEmpty()
|
||||
? HistoryView::FindViewPollOptionY(view, highlight.pollOption)
|
||||
: 0)
|
||||
: 0;
|
||||
const auto begin = sel.empty()
|
||||
? (todoy - 4 * single)
|
||||
@@ -9656,7 +9662,10 @@ void HistoryWidget::updateReplyEditText(not_null<HistoryItem*> item) {
|
||||
.repaint = [=] { updateField(); },
|
||||
});
|
||||
const auto text = [&] {
|
||||
const auto media = _replyTo.todoItemId ? item->media() : nullptr;
|
||||
const auto media = (_replyTo.todoItemId
|
||||
|| !_replyTo.pollOption.isEmpty())
|
||||
? item->media()
|
||||
: nullptr;
|
||||
if (const auto todolist = media ? media->todolist() : nullptr) {
|
||||
const auto i = ranges::find(
|
||||
todolist->items,
|
||||
@@ -9666,6 +9675,12 @@ void HistoryWidget::updateReplyEditText(not_null<HistoryItem*> item) {
|
||||
return i->text;
|
||||
}
|
||||
}
|
||||
if (const auto poll = media ? media->poll() : nullptr) {
|
||||
if (const auto answer = poll->answerByOption(
|
||||
_replyTo.pollOption)) {
|
||||
return answer->text;
|
||||
}
|
||||
}
|
||||
return (_editMsgId || _replyTo.quote.empty())
|
||||
? item->inReplyText()
|
||||
: _replyTo.quote;
|
||||
|
||||
@@ -1065,6 +1065,7 @@ void ChatWidget::setupSwipeReplyAndBack() {
|
||||
.quote = selected.highlight.quote,
|
||||
.quoteOffset = selected.highlight.quoteOffset,
|
||||
.todoItemId = selected.highlight.todoItemId,
|
||||
.pollOption = selected.highlight.pollOption,
|
||||
});
|
||||
};
|
||||
return result;
|
||||
|
||||
@@ -623,9 +623,14 @@ bool AddReplyToMessageAction(
|
||||
const auto todoListTaskId = request.link
|
||||
? request.link->property(kTodoListItemIdProperty).toInt()
|
||||
: 0;
|
||||
const auto pollOption = request.link
|
||||
? request.link->property(kPollOptionProperty).toByteArray()
|
||||
: QByteArray();
|
||||
const auto "e = request.quote;
|
||||
auto text = (todoListTaskId
|
||||
? tr::lng_context_reply_to_task
|
||||
: !pollOption.isEmpty()
|
||||
? tr::lng_context_reply_to_poll_option
|
||||
: quote.highlight.quote.empty()
|
||||
? tr::lng_context_reply_msg
|
||||
: tr::lng_context_quote_and_reply)(
|
||||
@@ -637,6 +642,7 @@ bool AddReplyToMessageAction(
|
||||
.quote = quote.highlight.quote,
|
||||
.quoteOffset = quote.highlight.quoteOffset,
|
||||
.todoItemId = todoListTaskId,
|
||||
.pollOption = pollOption,
|
||||
}, base::IsCtrlPressed());
|
||||
}, &st::menuIconReply);
|
||||
return true;
|
||||
|
||||
@@ -3059,6 +3059,72 @@ int FindViewTaskY(not_null<Element*> view, int taskId, int yfrom) {
|
||||
return origin.y() + (yfrom + ytill) / 2;
|
||||
}
|
||||
|
||||
int FindViewPollOptionY(
|
||||
not_null<Element*> view,
|
||||
const QByteArray &option,
|
||||
int yfrom) {
|
||||
auto request = HistoryView::StateRequest();
|
||||
request.flags = Ui::Text::StateRequest::Flag::LookupLink;
|
||||
const auto single = st::messageTextStyle.font->height;
|
||||
const auto inner = view->innerGeometry();
|
||||
const auto origin = inner.topLeft();
|
||||
if (origin.y() < 0
|
||||
|| origin.y() + inner.height() > view->height()
|
||||
|| inner.height() <= 0) {
|
||||
return yfrom;
|
||||
}
|
||||
const auto media = view->data()->media();
|
||||
const auto poll = media ? media->poll() : nullptr;
|
||||
if (!poll) {
|
||||
return yfrom;
|
||||
}
|
||||
const auto &answers = poll->answers;
|
||||
const auto indexOf = [&](const QByteArray &opt) -> int {
|
||||
return int(ranges::find(
|
||||
answers, opt, &PollAnswer::option) - begin(answers));
|
||||
};
|
||||
const auto index = indexOf(option);
|
||||
const auto count = int(answers.size());
|
||||
if (index == count) {
|
||||
return yfrom;
|
||||
}
|
||||
yfrom = std::max(yfrom - origin.y(), 0);
|
||||
auto ytill = inner.height() - 1;
|
||||
const auto middle = (yfrom + ytill) / 2;
|
||||
const auto fory = [&](int y) {
|
||||
const auto state = view->textState(origin + QPoint(0, y), request);
|
||||
const auto &link = state.link;
|
||||
const auto opt = link
|
||||
? link->property(kPollOptionProperty).toByteArray()
|
||||
: QByteArray();
|
||||
const auto idx = !opt.isEmpty() ? indexOf(opt) : count;
|
||||
return (idx < count) ? idx : (y < middle) ? -1 : count;
|
||||
};
|
||||
auto indexfrom = fory(yfrom);
|
||||
auto indextill = fory(ytill);
|
||||
if ((yfrom >= ytill) || (indexfrom >= index)) {
|
||||
return origin.y() + yfrom;
|
||||
} else if (indextill <= index) {
|
||||
return origin.y() + ytill;
|
||||
}
|
||||
while (ytill - yfrom >= 2 * single) {
|
||||
const auto mid = (yfrom + ytill) / 2;
|
||||
const auto found = fory(mid);
|
||||
if (found == index
|
||||
|| indexfrom > found
|
||||
|| indextill < found) {
|
||||
return origin.y() + mid;
|
||||
} else if (found < index) {
|
||||
yfrom = mid;
|
||||
indexfrom = found;
|
||||
} else {
|
||||
ytill = mid;
|
||||
indextill = found;
|
||||
}
|
||||
}
|
||||
return origin.y() + (yfrom + ytill) / 2;
|
||||
}
|
||||
|
||||
Window::SessionController *ExtractController(const ClickContext &context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
|
||||
@@ -789,6 +789,11 @@ private:
|
||||
int taskId,
|
||||
int yfrom = 0);
|
||||
|
||||
[[nodiscard]] int FindViewPollOptionY(
|
||||
not_null<Element*> view,
|
||||
const QByteArray &option,
|
||||
int yfrom = 0);
|
||||
|
||||
[[nodiscard]] Window::SessionController *ExtractController(
|
||||
const ClickContext &context);
|
||||
|
||||
|
||||
@@ -764,12 +764,18 @@ std::optional<int> ListWidget::scrollTopForView(
|
||||
if (heightLeft >= 0) {
|
||||
return std::max(top - (heightLeft / 2), 0);
|
||||
} else if (const auto highlight = _highlighter.state(view->data())
|
||||
; (!highlight.range.empty() || highlight.todoItemId)
|
||||
; (!highlight.range.empty()
|
||||
|| highlight.todoItemId
|
||||
|| !highlight.pollOption.isEmpty())
|
||||
&& !IsSubGroupSelection(highlight.range)) {
|
||||
const auto sel = highlight.range;
|
||||
const auto single = st::messageTextStyle.font->height;
|
||||
const auto todoy = sel.empty()
|
||||
? HistoryView::FindViewTaskY(view, highlight.todoItemId)
|
||||
? (highlight.todoItemId
|
||||
? HistoryView::FindViewTaskY(view, highlight.todoItemId)
|
||||
: !highlight.pollOption.isEmpty()
|
||||
? HistoryView::FindViewPollOptionY(view, highlight.pollOption)
|
||||
: 0)
|
||||
: 0;
|
||||
const auto begin = sel.empty()
|
||||
? (todoy - 4 * single)
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_poll.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_story.h"
|
||||
#include "data/data_todo_list.h"
|
||||
@@ -310,7 +311,8 @@ void Reply::update(
|
||||
const auto item = view->data();
|
||||
const auto &fields = data->fields();
|
||||
const auto message = data->resolvedMessage.get();
|
||||
const auto messageMedia = (message && fields.todoItemId)
|
||||
const auto messageMedia = (message
|
||||
&& (fields.todoItemId || !fields.pollOption.isEmpty()))
|
||||
? message->media()
|
||||
: nullptr;
|
||||
const auto messageTodoList = messageMedia
|
||||
@@ -326,6 +328,12 @@ void Reply::update(
|
||||
&& taskIndex < messageTodoList->items.size())
|
||||
? &messageTodoList->items[taskIndex]
|
||||
: nullptr;
|
||||
const auto messagePoll = messageMedia
|
||||
? messageMedia->poll()
|
||||
: nullptr;
|
||||
const auto pollAnswer = messagePoll
|
||||
? messagePoll->answerByOption(fields.pollOption)
|
||||
: nullptr;
|
||||
const auto story = data->resolvedStory.get();
|
||||
const auto externalMedia = fields.externalMedia.get();
|
||||
if (!_externalSender) {
|
||||
@@ -374,6 +382,11 @@ void Reply::update(
|
||||
.image = MakeTaskImage(),
|
||||
.margin = QMargins(0, st::lineWidth, st::lineWidth, 0),
|
||||
})).append(task->text)
|
||||
: pollAnswer
|
||||
? Ui::Text::Colorized(helper.image({
|
||||
.image = MakeTaskImage(),
|
||||
.margin = QMargins(0, st::lineWidth, st::lineWidth, 0),
|
||||
})).append(pollAnswer->text)
|
||||
: (message && (fields.quote.empty() || !fields.manualQuote))
|
||||
? message->inReplyText()
|
||||
: !fields.quote.empty()
|
||||
@@ -431,6 +444,7 @@ void Reply::setLinkFrom(
|
||||
.quote = fields.manualQuote ? fields.quote : TextWithEntities(),
|
||||
.quoteOffset = int(fields.quoteOffset),
|
||||
.todoItemId = fields.todoItemId,
|
||||
.pollOption = fields.pollOption,
|
||||
};
|
||||
const auto returnToId = view->data()->fullId();
|
||||
const auto externalLink = [=](ClickContext context) {
|
||||
@@ -1033,13 +1047,22 @@ TextWithEntities Reply::ComposePreviewName(
|
||||
}
|
||||
return to->author();
|
||||
}();
|
||||
if (const auto media = replyTo.todoItemId ? to->media() : nullptr) {
|
||||
if (const auto media = (replyTo.todoItemId
|
||||
|| !replyTo.pollOption.isEmpty())
|
||||
? to->media()
|
||||
: nullptr) {
|
||||
if (const auto todolist = media->todolist()) {
|
||||
return tr::lng_preview_reply_to_task(
|
||||
tr::now,
|
||||
lt_title,
|
||||
todolist->title,
|
||||
tr::marked);
|
||||
} else if (const auto poll = media->poll()) {
|
||||
return tr::lng_preview_reply_to_poll_option(
|
||||
tr::now,
|
||||
lt_title,
|
||||
poll->question,
|
||||
tr::marked);
|
||||
}
|
||||
}
|
||||
const auto toPeer = to->history()->peer;
|
||||
|
||||
@@ -282,6 +282,11 @@ PollThumbnailData MakePollThumbnail(
|
||||
});
|
||||
});
|
||||
}
|
||||
if (result.handler) {
|
||||
result.handler->setProperty(
|
||||
kPollOptionProperty,
|
||||
answer.option);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1202,17 +1207,29 @@ void Poll::updateAnswers() {
|
||||
ClickHandlerPtr Poll::createAnswerClickHandler(
|
||||
const Answer &answer) {
|
||||
const auto option = answer.option;
|
||||
auto result = ClickHandlerPtr();
|
||||
if (_flags & PollData::Flag::MultiChoice) {
|
||||
return std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
||||
toggleMultiOption(option);
|
||||
result = std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
||||
if (canVote()) {
|
||||
toggleMultiOption(option);
|
||||
} else if (showVotes()) {
|
||||
showAnswerVotesTooltip(option);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
result = std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
||||
if (canVote()) {
|
||||
_votedFromHere = true;
|
||||
history()->session().api().polls().sendVotes(
|
||||
_parent->data()->fullId(),
|
||||
{ option });
|
||||
} else if (showVotes()) {
|
||||
showAnswerVotesTooltip(option);
|
||||
}
|
||||
}));
|
||||
}
|
||||
return std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
||||
_votedFromHere = true;
|
||||
history()->session().api().polls().sendVotes(
|
||||
_parent->data()->fullId(),
|
||||
{ option });
|
||||
}));
|
||||
result->setProperty(kPollOptionProperty, option);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Poll::toggleMultiOption(const QByteArray &option) {
|
||||
@@ -1255,6 +1272,25 @@ void Poll::sendMultiOptions() {
|
||||
}
|
||||
}
|
||||
|
||||
void Poll::showAnswerVotesTooltip(const QByteArray &option) {
|
||||
const auto answer = _poll->answerByOption(option);
|
||||
if (!answer) {
|
||||
return;
|
||||
}
|
||||
const auto quiz = _poll->quiz();
|
||||
const auto text = answer->votes
|
||||
? (quiz
|
||||
? tr::lng_polls_answers_count
|
||||
: tr::lng_polls_votes_count)(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
answer->votes)
|
||||
: (quiz
|
||||
? tr::lng_polls_answers_none
|
||||
: tr::lng_polls_votes_none)(tr::now);
|
||||
_parent->delegate()->elementShowTooltip({ text }, [] {});
|
||||
}
|
||||
|
||||
void Poll::showResults() {
|
||||
_parent->delegate()->elementShowPollResults(
|
||||
_poll,
|
||||
@@ -1919,6 +1955,30 @@ int Poll::paintAnswer(
|
||||
int outerWidth,
|
||||
const PaintContext &context) const {
|
||||
const auto height = countAnswerHeight(answer, width);
|
||||
if (!context.highlight.pollOption.isEmpty()
|
||||
&& context.highlight.pollOption == answer.option
|
||||
&& context.highlight.collapsion > 0.) {
|
||||
const auto to = context.highlightInterpolateTo;
|
||||
const auto toProgress = (1. - context.highlight.collapsion);
|
||||
if (toProgress >= 1.) {
|
||||
context.highlightPathCache->addRect(to);
|
||||
} else if (toProgress <= 0.) {
|
||||
context.highlightPathCache->addRect(
|
||||
0,
|
||||
top,
|
||||
this->width(),
|
||||
height);
|
||||
} else {
|
||||
const auto lerp = [=](int from, int to) {
|
||||
return from + (to - from) * toProgress;
|
||||
};
|
||||
context.highlightPathCache->addRect(
|
||||
lerp(0, to.x()),
|
||||
lerp(top, to.y()),
|
||||
lerp(this->width(), to.width()),
|
||||
lerp(height, to.height()));
|
||||
}
|
||||
}
|
||||
const auto stm = context.messageStyle();
|
||||
const auto &answerPadding = answer.thumbnail
|
||||
? st::historyPollAnswerPadding
|
||||
@@ -2623,10 +2683,13 @@ TextState Poll::textState(QPoint point, StateRequest request) const {
|
||||
media,
|
||||
media).contains(point)) {
|
||||
result.link = answer.mediaHandler;
|
||||
} else if (can) {
|
||||
_lastLinkPoint = point;
|
||||
} else {
|
||||
if (can) {
|
||||
_lastLinkPoint = point;
|
||||
}
|
||||
result.link = answer.handler;
|
||||
} else if (show) {
|
||||
}
|
||||
if (!can && show) {
|
||||
result.customTooltip = true;
|
||||
using Flag = Ui::Text::StateRequest::Flag;
|
||||
if (request.flags & Flag::LookupCustomTooltip) {
|
||||
@@ -2734,7 +2797,9 @@ void Poll::clickHandlerPressedChanged(
|
||||
handler,
|
||||
&Answer::handler);
|
||||
if (i != end(_answers)) {
|
||||
toggleRipple(*i, pressed);
|
||||
if (canVote()) {
|
||||
toggleRipple(*i, pressed);
|
||||
}
|
||||
} else if (handler == _sendVotesLink || handler == _showResultsLink) {
|
||||
toggleLinkRipple(pressed);
|
||||
}
|
||||
|
||||
@@ -207,6 +207,7 @@ private:
|
||||
void toggleMultiOption(const QByteArray &option);
|
||||
void sendMultiOptions();
|
||||
void showResults();
|
||||
void showAnswerVotesTooltip(const QByteArray &option);
|
||||
void checkQuizAnswered();
|
||||
void showSolution() const;
|
||||
void solutionToggled(
|
||||
|
||||
@@ -183,6 +183,7 @@ struct ChatPaintHighlight {
|
||||
float64 collapsion = 0.;
|
||||
TextSelection range;
|
||||
int todoItemId = 0;
|
||||
QByteArray pollOption;
|
||||
};
|
||||
|
||||
struct ChatPaintContext {
|
||||
|
||||
Reference in New Issue
Block a user