File indexing completed on 2024-10-06 07:36:09
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 "neochatroom.h" 0007 0008 #include <Quotient/csapi/relations.h> 0009 #include <Quotient/events/roompowerlevelsevent.h> 0010 0011 #include <algorithm> 0012 0013 using namespace Quotient; 0014 0015 PollHandler::PollHandler(NeoChatRoom *room, const Quotient::PollStartEvent *pollStartEvent) 0016 : QObject(room) 0017 , m_pollStartEvent(pollStartEvent) 0018 { 0019 if (room != nullptr && m_pollStartEvent != nullptr) { 0020 connect(room, &NeoChatRoom::aboutToAddNewMessages, this, &PollHandler::updatePoll); 0021 checkLoadRelations(); 0022 } 0023 } 0024 0025 void PollHandler::updatePoll(Quotient::RoomEventsRange events) 0026 { 0027 // This function will never be called if the PollHandler was not initialized with 0028 // a NeoChatRoom as parent and a PollStartEvent so no need to null check. 0029 auto room = dynamic_cast<NeoChatRoom *>(parent()); 0030 for (const auto &event : events) { 0031 if (event->is<PollEndEvent>()) { 0032 auto plEvent = room->currentState().get<RoomPowerLevelsEvent>(); 0033 if (!plEvent) { 0034 continue; 0035 } 0036 auto userPl = plEvent->powerLevelForUser(event->senderId()); 0037 if (event->senderId() == m_pollStartEvent->senderId() || userPl >= plEvent->redact()) { 0038 m_hasEnded = true; 0039 m_endedTimestamp = event->originTimestamp(); 0040 Q_EMIT hasEndedChanged(); 0041 } 0042 } 0043 if (event->is<PollResponseEvent>()) { 0044 handleAnswer(event->contentJson(), event->senderId(), event->originTimestamp()); 0045 } 0046 if (event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls) 0047 && event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.replace"_ls 0048 && event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString() == m_pollStartEvent->id()) { 0049 Q_EMIT questionChanged(); 0050 Q_EMIT optionsChanged(); 0051 } 0052 } 0053 } 0054 0055 void PollHandler::checkLoadRelations() 0056 { 0057 // This function will never be called if the PollHandler was not initialized with 0058 // a NeoChatRoom as parent and a PollStartEvent so no need to null check. 0059 auto room = dynamic_cast<NeoChatRoom *>(parent()); 0060 m_maxVotes = m_pollStartEvent->maxSelections(); 0061 auto job = room->connection()->callApi<GetRelatingEventsJob>(room->id(), m_pollStartEvent->id()); 0062 connect(job, &BaseJob::success, this, [this, job, room]() { 0063 for (const auto &event : job->chunk()) { 0064 if (event->is<PollEndEvent>()) { 0065 auto plEvent = room->currentState().get<RoomPowerLevelsEvent>(); 0066 if (!plEvent) { 0067 continue; 0068 } 0069 auto userPl = plEvent->powerLevelForUser(event->senderId()); 0070 if (event->senderId() == m_pollStartEvent->senderId() || userPl >= plEvent->redact()) { 0071 m_hasEnded = true; 0072 m_endedTimestamp = event->originTimestamp(); 0073 Q_EMIT hasEndedChanged(); 0074 } 0075 } 0076 if (event->is<PollResponseEvent>()) { 0077 handleAnswer(event->contentJson(), event->senderId(), event->originTimestamp()); 0078 } 0079 } 0080 }); 0081 } 0082 0083 void PollHandler::handleAnswer(const QJsonObject &content, const QString &sender, QDateTime timestamp) 0084 { 0085 if (timestamp > m_answerTimestamps[sender] && (!m_hasEnded || timestamp < m_endedTimestamp)) { 0086 m_answerTimestamps[sender] = timestamp; 0087 m_answers[sender] = {}; 0088 int i = 0; 0089 for (const auto &answer : content["org.matrix.msc3381.poll.response"_ls]["answers"_ls].toArray()) { 0090 auto array = m_answers[sender].toArray(); 0091 array.insert(0, answer); 0092 m_answers[sender] = array; 0093 i++; 0094 if (i == m_maxVotes) { 0095 break; 0096 } 0097 } 0098 for (const auto &key : m_answers.keys()) { 0099 if (m_answers[key].toArray().isEmpty()) { 0100 m_answers.remove(key); 0101 } 0102 } 0103 } 0104 Q_EMIT answersChanged(); 0105 } 0106 0107 QString PollHandler::question() const 0108 { 0109 if (m_pollStartEvent == nullptr) { 0110 return {}; 0111 } 0112 return m_pollStartEvent->contentPart<QJsonObject>("org.matrix.msc3381.poll.start"_ls)["question"_ls].toObject()["body"_ls].toString(); 0113 } 0114 0115 QJsonArray PollHandler::options() const 0116 { 0117 if (m_pollStartEvent == nullptr) { 0118 return {}; 0119 } 0120 return m_pollStartEvent->contentPart<QJsonObject>("org.matrix.msc3381.poll.start"_ls)["answers"_ls].toArray(); 0121 } 0122 0123 QJsonObject PollHandler::answers() const 0124 { 0125 return m_answers; 0126 } 0127 0128 QJsonObject PollHandler::counts() const 0129 { 0130 QJsonObject counts; 0131 for (const auto &answer : m_answers) { 0132 for (const auto &id : answer.toArray()) { 0133 counts[id.toString()] = counts[id.toString()].toInt() + 1; 0134 } 0135 } 0136 return counts; 0137 } 0138 0139 QString PollHandler::kind() const 0140 { 0141 if (m_pollStartEvent == nullptr) { 0142 return {}; 0143 } 0144 return m_pollStartEvent->contentPart<QJsonObject>("org.matrix.msc3381.poll.start"_ls)["kind"_ls].toString(); 0145 } 0146 0147 void PollHandler::sendPollAnswer(const QString &eventId, const QString &answerId) 0148 { 0149 Q_ASSERT(eventId.length() > 0); 0150 Q_ASSERT(answerId.length() > 0); 0151 auto room = dynamic_cast<NeoChatRoom *>(parent()); 0152 if (room == nullptr) { 0153 qWarning() << "PollHandler is empty, cannot send an answer."; 0154 return; 0155 } 0156 QStringList ownAnswers; 0157 for (const auto &answer : m_answers[room->localUser()->id()].toArray()) { 0158 ownAnswers += answer.toString(); 0159 } 0160 if (ownAnswers.contains(answerId)) { 0161 ownAnswers.erase(std::remove_if(ownAnswers.begin(), ownAnswers.end(), [answerId](const auto &it) { 0162 return answerId == it; 0163 })); 0164 } else { 0165 while (ownAnswers.size() >= m_maxVotes) { 0166 ownAnswers.pop_front(); 0167 } 0168 ownAnswers.insert(0, answerId); 0169 } 0170 0171 auto response = new PollResponseEvent(eventId, ownAnswers); 0172 handleAnswer(response->contentJson(), room->localUser()->id(), QDateTime::currentDateTime()); 0173 room->postEvent(response); 0174 } 0175 0176 bool PollHandler::hasEnded() const 0177 { 0178 return m_hasEnded; 0179 } 0180 0181 int PollHandler::answerCount() const 0182 { 0183 return m_answers.size(); 0184 } 0185 0186 #include "moc_pollhandler.cpp"