Enable ending polls
This commit is contained in:
@@ -58,6 +58,14 @@ PollEndEvent::PollEndEvent(const QJsonObject &obj)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PollEndEvent::PollEndEvent(const QString &pollStartEventId, const QString &endText)
|
||||||
|
: RoomEvent(basicJson(TypeId,
|
||||||
|
{{"org.matrix.msc1767.text"_L1, endText},
|
||||||
|
{"org.matrix.msc3381.poll.end"_L1, QJsonObject{}},
|
||||||
|
{"m.relates_to"_L1, QJsonObject{{"rel_type"_L1, "m.reference"_L1}, {"event_id"_L1, pollStartEventId}}}}))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<EventRelation> PollEndEvent::relatesTo() const
|
std::optional<EventRelation> PollEndEvent::relatesTo() const
|
||||||
{
|
{
|
||||||
return contentPart<std::optional<EventRelation>>(RelatesToKey);
|
return contentPart<std::optional<EventRelation>>(RelatesToKey);
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ class PollEndEvent : public RoomEvent
|
|||||||
public:
|
public:
|
||||||
QUO_EVENT(PollEndEvent, "org.matrix.msc3381.poll.end");
|
QUO_EVENT(PollEndEvent, "org.matrix.msc3381.poll.end");
|
||||||
explicit PollEndEvent(const QJsonObject &obj);
|
explicit PollEndEvent(const QJsonObject &obj);
|
||||||
|
explicit PollEndEvent(const QString &pollStartEventId, const QString &endText);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The EventRelation pointing to the PollStartEvent.
|
* @brief The EventRelation pointing to the PollStartEvent.
|
||||||
|
|||||||
@@ -238,6 +238,10 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == IsPollRole) {
|
||||||
|
return event->get().is<PollStartEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
if (role == ShowSectionRole) {
|
if (role == ShowSectionRole) {
|
||||||
for (auto r = row + 1; r < rowCount(); ++r) {
|
for (auto r = row + 1; r < rowCount(); ++r) {
|
||||||
auto i = index(r);
|
auto i = index(r);
|
||||||
@@ -316,6 +320,7 @@ QHash<int, QByteArray> MessageModel::roleNames() const
|
|||||||
roles[ProgressInfoRole] = "progressInfo";
|
roles[ProgressInfoRole] = "progressInfo";
|
||||||
roles[IsThreadedRole] = "isThreaded";
|
roles[IsThreadedRole] = "isThreaded";
|
||||||
roles[ThreadRootRole] = "threadRoot";
|
roles[ThreadRootRole] = "threadRoot";
|
||||||
|
roles[IsPollRole] = "isPoll";
|
||||||
roles[ShowSectionRole] = "showSection";
|
roles[ShowSectionRole] = "showSection";
|
||||||
roles[ReadMarkersRole] = "readMarkers";
|
roles[ReadMarkersRole] = "readMarkers";
|
||||||
roles[ShowReadMarkersRole] = "showReadMarkers";
|
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public:
|
|||||||
|
|
||||||
IsThreadedRole, /**< Whether the message is in a thread. */
|
IsThreadedRole, /**< Whether the message is in a thread. */
|
||||||
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
|
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
|
||||||
|
IsPollRole, /**< Whether the message is a poll. */
|
||||||
|
|
||||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||||
|
|
||||||
|
|||||||
@@ -502,7 +502,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @sa PollHandler
|
* @sa PollHandler
|
||||||
*/
|
*/
|
||||||
PollHandler *poll(const QString &eventId);
|
Q_INVOKABLE PollHandler *poll(const QString &eventId);
|
||||||
|
|
||||||
Q_INVOKABLE void postPoll(PollKind::Kind kind, const QString &question, const QList<QString> &answers);
|
Q_INVOKABLE void postPoll(PollKind::Kind kind, const QString &question, const QList<QString> &answers);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "pollhandler.h"
|
#include "pollhandler.h"
|
||||||
|
|
||||||
|
#include <KLocalization>
|
||||||
|
|
||||||
#include "events/pollevent.h"
|
#include "events/pollevent.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "pollanswermodel.h"
|
#include "pollanswermodel.h"
|
||||||
@@ -24,6 +26,7 @@ PollHandler::PollHandler(NeoChatRoom *room, const QString &pollStartId)
|
|||||||
|
|
||||||
if (room != nullptr) {
|
if (room != nullptr) {
|
||||||
connect(room, &NeoChatRoom::aboutToAddNewMessages, this, &PollHandler::updatePoll);
|
connect(room, &NeoChatRoom::aboutToAddNewMessages, this, &PollHandler::updatePoll);
|
||||||
|
connect(room, &NeoChatRoom::pendingEventAboutToAdd, this, &PollHandler::handleEvent);
|
||||||
checkLoadRelations();
|
checkLoadRelations();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,34 +40,8 @@ void PollHandler::updatePoll(Quotient::RoomEventsRange events)
|
|||||||
if (pollStartEvent == nullptr) {
|
if (pollStartEvent == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &event : events) {
|
for (const auto &event : events) {
|
||||||
if (event->is<PollEndEvent>()) {
|
handleEvent(event.get());
|
||||||
const auto endEvent = eventCast<const PollEndEvent>(event);
|
|
||||||
if (endEvent->relatesTo()->eventId != m_pollStartId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
|
|
||||||
if (!plEvent) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto userPl = plEvent->powerLevelForUser(event->senderId());
|
|
||||||
if (event->senderId() == pollStartEvent->senderId() || userPl >= plEvent->redact()) {
|
|
||||||
m_hasEnded = true;
|
|
||||||
m_endedTimestamp = event->originTimestamp();
|
|
||||||
Q_EMIT hasEndedChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (event->is<PollResponseEvent>()) {
|
|
||||||
handleResponse(eventCast<const PollResponseEvent>(event));
|
|
||||||
}
|
|
||||||
if (event->contentPart<QJsonObject>("m.relates_to"_L1).contains("rel_type"_L1)
|
|
||||||
&& event->contentPart<QJsonObject>("m.relates_to"_L1)["rel_type"_L1].toString() == "m.replace"_L1
|
|
||||||
&& event->contentPart<QJsonObject>("m.relates_to"_L1)["event_id"_L1].toString() == pollStartEvent->id()) {
|
|
||||||
Q_EMIT questionChanged();
|
|
||||||
Q_EMIT answersChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,32 +56,51 @@ void PollHandler::checkLoadRelations()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto job = room->connection()->callApi<GetRelatingEventsJob>(room->id(), pollStartEvent->id());
|
auto job = room->connection()->callApi<GetRelatingEventsJob>(room->id(), pollStartEvent->id());
|
||||||
connect(job, &BaseJob::success, this, [this, job, room, pollStartEvent]() {
|
connect(job, &BaseJob::success, this, [this, job]() {
|
||||||
for (const auto &event : job->chunk()) {
|
for (const auto &event : job->chunk()) {
|
||||||
if (event->is<PollEndEvent>()) {
|
handleEvent(event.get());
|
||||||
const auto endEvent = eventCast<const PollEndEvent>(event);
|
|
||||||
if (endEvent->relatesTo()->eventId != m_pollStartId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
|
|
||||||
if (!plEvent) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto userPl = plEvent->powerLevelForUser(event->senderId());
|
|
||||||
if (event->senderId() == pollStartEvent->senderId() || userPl >= plEvent->redact()) {
|
|
||||||
m_hasEnded = true;
|
|
||||||
m_endedTimestamp = event->originTimestamp();
|
|
||||||
Q_EMIT hasEndedChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (event->is<PollResponseEvent>()) {
|
|
||||||
handleResponse(eventCast<const PollResponseEvent>(event));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PollHandler::handleEvent(Quotient::RoomEvent *event)
|
||||||
|
{
|
||||||
|
// This function will never be called if the PollHandler was not initialized with
|
||||||
|
// a NeoChatRoom as parent and a PollStartEvent so no need to null check.
|
||||||
|
const auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||||
|
auto pollStartEvent = eventCast<const PollStartEvent>(room->getEvent(m_pollStartId).first);
|
||||||
|
if (pollStartEvent == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->is<PollEndEvent>()) {
|
||||||
|
const auto endEvent = eventCast<const PollEndEvent>(event);
|
||||||
|
if (endEvent->relatesTo()->eventId != m_pollStartId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
|
||||||
|
if (!plEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto userPl = plEvent->powerLevelForUser(event->senderId());
|
||||||
|
if (event->senderId() == pollStartEvent->senderId() || userPl >= plEvent->redact()) {
|
||||||
|
m_hasEnded = true;
|
||||||
|
m_endedTimestamp = event->originTimestamp();
|
||||||
|
Q_EMIT hasEndedChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event->is<PollResponseEvent>()) {
|
||||||
|
handleResponse(eventCast<const PollResponseEvent>(event));
|
||||||
|
}
|
||||||
|
if (event->contentPart<QJsonObject>("m.relates_to"_L1).contains("rel_type"_L1)
|
||||||
|
&& event->contentPart<QJsonObject>("m.relates_to"_L1)["rel_type"_L1].toString() == "m.replace"_L1
|
||||||
|
&& event->contentPart<QJsonObject>("m.relates_to"_L1)["event_id"_L1].toString() == pollStartEvent->id()) {
|
||||||
|
Q_EMIT questionChanged();
|
||||||
|
Q_EMIT answersChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PollHandler::handleResponse(const Quotient::PollResponseEvent *event)
|
void PollHandler::handleResponse(const Quotient::PollResponseEvent *event)
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (event == nullptr) {
|
||||||
@@ -292,4 +288,32 @@ bool PollHandler::hasEnded() const
|
|||||||
return m_hasEnded;
|
return m_hasEnded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PollHandler::endPoll() const
|
||||||
|
{
|
||||||
|
room()->post<PollEndEvent>(m_pollStartId, endText());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PollHandler::endText() const
|
||||||
|
{
|
||||||
|
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||||
|
if (room == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto pollStartEvent = eventCast<const PollStartEvent>(room->getEvent(m_pollStartId).first);
|
||||||
|
if (pollStartEvent == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
int maxCount = 0;
|
||||||
|
QString answerText = {};
|
||||||
|
for (const auto &answer : pollStartEvent->answers()) {
|
||||||
|
const auto currentCount = answerCountAtId(answer.id);
|
||||||
|
if (currentCount > maxCount) {
|
||||||
|
maxCount = currentCount;
|
||||||
|
answerText = answer.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18nc("%1 is the poll answer that had the most votes", "The poll has ended. Top answer: %1", answerText);
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_pollhandler.cpp"
|
#include "moc_pollhandler.cpp"
|
||||||
|
|||||||
@@ -106,6 +106,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void sendPollAnswer(const QString &eventId, const QString &answerId);
|
Q_INVOKABLE void sendPollAnswer(const QString &eventId, const QString &answerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send the PollEndEvent.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void endPoll() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void questionChanged();
|
void questionChanged();
|
||||||
void hasEndedChanged();
|
void hasEndedChanged();
|
||||||
@@ -123,12 +128,14 @@ private:
|
|||||||
void updatePoll(Quotient::RoomEventsRange events);
|
void updatePoll(Quotient::RoomEventsRange events);
|
||||||
|
|
||||||
void checkLoadRelations();
|
void checkLoadRelations();
|
||||||
|
void handleEvent(Quotient::RoomEvent *event);
|
||||||
void handleResponse(const Quotient::PollResponseEvent *event);
|
void handleResponse(const Quotient::PollResponseEvent *event);
|
||||||
QHash<QString, QDateTime> m_selectionTimestamps;
|
QHash<QString, QDateTime> m_selectionTimestamps;
|
||||||
QHash<QString, QStringList> m_selections;
|
QHash<QString, QStringList> m_selections;
|
||||||
|
|
||||||
bool m_hasEnded = false;
|
bool m_hasEnded = false;
|
||||||
QDateTime m_endedTimestamp;
|
QDateTime m_endedTimestamp;
|
||||||
|
QString endText() const;
|
||||||
|
|
||||||
QPointer<PollAnswerModel> m_answerModel;
|
QPointer<PollAnswerModel> m_answerModel;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Button {
|
QQC2.Button {
|
||||||
visible: NeoChatConfig.threads && !root.currentRoom.readOnly
|
visible: NeoChatConfig.threads && !root.currentRoom.readOnly && !root.delegate?.isPoll
|
||||||
text: i18n("Reply in Thread")
|
text: i18n("Reply in Thread")
|
||||||
icon.name: "dialog-messages"
|
icon.name: "dialog-messages"
|
||||||
display: QQC2.Button.IconOnly
|
display: QQC2.Button.IconOnly
|
||||||
@@ -153,6 +153,18 @@ QQC2.Control {
|
|||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQC2.Button {
|
||||||
|
visible: (root.delegate?.isPoll ?? false) && !root.currentRoom.poll(root.delegate.eventId).hasEnded
|
||||||
|
text: i18n("End Poll")
|
||||||
|
icon.name: "gtk-stop"
|
||||||
|
display: QQC2.ToolButton.IconOnly
|
||||||
|
onClicked: root.currentRoom.poll(root.delegate.eventId).endPoll()
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
}
|
||||||
|
|
||||||
EmojiDialog {
|
EmojiDialog {
|
||||||
id: emojiDialog
|
id: emojiDialog
|
||||||
currentRoom: root.currentRoom
|
currentRoom: root.currentRoom
|
||||||
|
|||||||
@@ -92,6 +92,11 @@ TimelineDelegate {
|
|||||||
*/
|
*/
|
||||||
required property string threadRoot
|
required property string threadRoot
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the message in a poll.
|
||||||
|
*/
|
||||||
|
required property bool isPoll
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether this message has a local user mention.
|
* @brief Whether this message has a local user mention.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user