File indexing completed on 2024-10-06 10:23:57

0001 // SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "pollhandler.h"
0005 
0006 #include "events/pollevent.h"
0007 #include "neochatroom.h"
0008 
0009 #include <Quotient/csapi/relations.h>
0010 #include <Quotient/events/roompowerlevelsevent.h>
0011 #include <Quotient/user.h>
0012 
0013 #include <algorithm>
0014 
0015 using namespace Quotient;
0016 
0017 PollHandler::PollHandler(QObject *parent)
0018     : QObject(parent)
0019 {
0020     connect(this, &PollHandler::roomChanged, this, &PollHandler::checkLoadRelations);
0021     connect(this, &PollHandler::pollStartEventIdChanged, this, &PollHandler::checkLoadRelations);
0022 }
0023 
0024 NeoChatRoom *PollHandler::room() const
0025 {
0026     return m_room;
0027 }
0028 
0029 void PollHandler::setRoom(NeoChatRoom *room)
0030 {
0031     if (m_room == room) {
0032         return;
0033     }
0034     if (m_room) {
0035         disconnect(m_room, nullptr, this, nullptr);
0036     }
0037     connect(room, &NeoChatRoom::aboutToAddNewMessages, this, [this](Quotient::RoomEventsRange events) {
0038         for (const auto &event : events) {
0039             if (event->is<PollEndEvent>()) {
0040                 auto plEvent = m_room->currentState().get<RoomPowerLevelsEvent>();
0041                 if (!plEvent) {
0042                     continue;
0043                 }
0044                 auto userPl = plEvent->powerLevelForUser(event->senderId());
0045                 if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= plEvent->redact()) {
0046                     m_hasEnded = true;
0047                     m_endedTimestamp = event->originTimestamp();
0048                     Q_EMIT hasEndedChanged();
0049                 }
0050             }
0051             if (event->is<PollResponseEvent>()) {
0052                 handleAnswer(event->contentJson(), event->senderId(), event->originTimestamp());
0053             }
0054         }
0055     });
0056     m_room = room;
0057     Q_EMIT roomChanged();
0058 }
0059 
0060 QString PollHandler::pollStartEventId() const
0061 {
0062     return m_pollStartEventId;
0063 }
0064 
0065 void PollHandler::setPollStartEventId(const QString &eventId)
0066 {
0067     if (eventId == m_pollStartEventId) {
0068         return;
0069     }
0070     m_pollStartEventId = eventId;
0071     Q_EMIT pollStartEventIdChanged();
0072 }
0073 
0074 void PollHandler::checkLoadRelations()
0075 {
0076     if (!m_room || m_pollStartEventId.isEmpty()) {
0077         return;
0078     }
0079     m_maxVotes = eventCast<const PollStartEvent>(&**m_room->findInTimeline(m_pollStartEventId))->maxSelections();
0080     auto job = m_room->connection()->callApi<GetRelatingEventsJob>(m_room->id(), m_pollStartEventId);
0081     connect(job, &BaseJob::success, this, [this, job]() {
0082         for (const auto &event : job->chunk()) {
0083             if (event->is<PollEndEvent>()) {
0084                 auto plEvent = m_room->currentState().get<RoomPowerLevelsEvent>();
0085                 if (!plEvent) {
0086                     continue;
0087                 }
0088                 auto userPl = plEvent->powerLevelForUser(event->senderId());
0089                 if (event->senderId() == (*m_room->findInTimeline(m_pollStartEventId))->senderId() || userPl >= plEvent->redact()) {
0090                     m_hasEnded = true;
0091                     m_endedTimestamp = event->originTimestamp();
0092                     Q_EMIT hasEndedChanged();
0093                 }
0094             }
0095             if (event->is<PollResponseEvent>()) {
0096                 handleAnswer(event->contentJson(), event->senderId(), event->originTimestamp());
0097             }
0098         }
0099     });
0100 }
0101 
0102 void PollHandler::handleAnswer(const QJsonObject &content, const QString &sender, QDateTime timestamp)
0103 {
0104     if (timestamp > m_answerTimestamps[sender] && (!m_hasEnded || timestamp < m_endedTimestamp)) {
0105         m_answerTimestamps[sender] = timestamp;
0106         m_answers[sender] = {};
0107         int i = 0;
0108         for (const auto &answer : content["org.matrix.msc3381.poll.response"]["answers"].toArray()) {
0109             auto array = m_answers[sender].toArray();
0110             array.insert(0, answer);
0111             m_answers[sender] = array;
0112             i++;
0113             if (i == m_maxVotes) {
0114                 break;
0115             }
0116         }
0117         for (const auto &key : m_answers.keys()) {
0118             if (m_answers[key].toArray().isEmpty()) {
0119                 m_answers.remove(key);
0120             }
0121         }
0122     }
0123     Q_EMIT answersChanged();
0124 }
0125 
0126 QJsonObject PollHandler::answers() const
0127 {
0128     return m_answers;
0129 }
0130 
0131 QJsonObject PollHandler::counts() const
0132 {
0133     QJsonObject counts;
0134     for (const auto &answer : m_answers) {
0135         for (const auto &id : answer.toArray()) {
0136             counts[id.toString()] = counts[id.toString()].toInt() + 1;
0137         }
0138     }
0139     return counts;
0140 }
0141 
0142 void PollHandler::sendPollAnswer(const QString &eventId, const QString &answerId)
0143 {
0144     Q_ASSERT(eventId.length() > 0);
0145     Q_ASSERT(answerId.length() > 0);
0146     QStringList ownAnswers;
0147     for (const auto &answer : m_answers[m_room->localUser()->id()].toArray()) {
0148         ownAnswers += answer.toString();
0149     }
0150     if (ownAnswers.contains(answerId)) {
0151         ownAnswers.erase(std::remove_if(ownAnswers.begin(), ownAnswers.end(), [answerId](const auto &it) {
0152             return answerId == it;
0153         }));
0154     } else {
0155         while (ownAnswers.size() >= m_maxVotes) {
0156             ownAnswers.pop_front();
0157         }
0158         ownAnswers.insert(0, answerId);
0159     }
0160 
0161     auto response = new PollResponseEvent(eventId, ownAnswers);
0162     handleAnswer(response->contentJson(), m_room->localUser()->id(), QDateTime::currentDateTime());
0163     m_room->postEvent(response);
0164 }
0165 
0166 bool PollHandler::hasEnded() const
0167 {
0168     return m_hasEnded;
0169 }
0170 
0171 int PollHandler::answerCount() const
0172 {
0173     return m_answers.size();
0174 }
0175 
0176 #include "moc_pollhandler.cpp"