Support for polls (can be created by room admins)

This commit is contained in:
N Pex 2022-05-03 09:40:02 +00:00
parent 2a064f4a06
commit 0d1ac1d441
11 changed files with 676 additions and 6 deletions

View file

@ -0,0 +1,185 @@
import util from "../../plugins/utils";
export default {
data() {
return {
pollQuestion: "",
pollAnswers: [],
pollResponseRelations: null,
pollEndRelations: null,
pollEndTs: null,
pollIsDisclosed: true,
}
},
mounted() {
this.$matrix.on("Room.timeline", this.pollMixinOnEvent);
this.pollQuestion = (this.event && this.event.getContent()["org.matrix.msc3381.poll.start"]["question"]["body"]) || "";
this.updateAnswers();
},
destroyed() {
this.$matrix.off("Room.timeline", this.pollMixinOnEvent);
},
beforeDestroy() {
if (this.pollResponseRelations) {
this.pollResponseRelations.off('Relations.add', this.onAddRelation);
this.pollResponseRelations = null;
}
if (this.pollEndRelations) {
this.pollEndRelations.off('Relations.add', this.onAddRelation);
this.pollEndRelations = null;
}
},
methods: {
updateAnswers() {
let answers = (this.event && this.event.getContent()["org.matrix.msc3381.poll.start"]["answers"]) || [];
var answerMap = {};
var answerArray = [];
answers.forEach(a => {
let text = a["org.matrix.msc1767.text"];
let answer = {id: a.id, text: text, numVotes: 0, percentage: 0}
answerMap[a.id] = answer;
answerArray.push(answer);
});
// Kind of poll
this.pollIsDisclosed = (this.event && this.event.getContent()["org.matrix.msc3381.poll.start"]["kind"] != "org.matrix.msc3381.poll.undisclosed") || false;
// Look for poll end
this.pollEndRelations = this.timelineSet.getRelationsForEvent(
this.event.getId(),
'm.reference',
'org.matrix.msc3381.poll.end'
);
if (this.pollEndRelations) {
const endMessages = this.pollEndRelations.getRelations() || [];
if (endMessages.length > 0) {
this.pollEndTs = endMessages[endMessages.length - 1].getTs();
}
}
// Process votes
this.pollResponseRelations = this.timelineSet.getRelationsForEvent(
this.event.getId(),
'm.reference',
'org.matrix.msc3381.poll.response'
);
var userVotes = {};
if (this.pollResponseRelations) {
const votes = this.pollResponseRelations.getRelations() || [];
for (const vote of votes) {
//const emoji = r.getRelation().key;
if (this.pollEndTs && vote.getTs() > this.pollEndTs) {
continue; // Invalid vote, after poll was closed.
}
const sender = vote.getSender();
const answersFromThisUser = vote.getContent()["org.matrix.msc3381.poll.response"]["answers"] || [];
if (answersFromThisUser.length == 0) {
delete userVotes[sender];
} else {
userVotes[sender] = answersFromThisUser;
}
}
}
var totalVotes = 0;
for (const [user, answersFromThisUser] of Object.entries(userVotes)) {
for (const a of answersFromThisUser) {
if (answerMap[a]) {
answerMap[a].numVotes += 1;
totalVotes += 1;
if (user == this.$matrix.currentUserId) {
answerMap[a].hasMyVote = true;
}
}
}
}
// Update percentage
answerArray.forEach(a => {
a.percentage = parseInt(((100 * a.numVotes) / totalVotes).toFixed(0));
});
this.pollAnswers = answerArray;
},
pollAnswer(id) {
if (this.pollIsClosed) {
return;
}
util
.sendPollAnswer(
this.$matrix.matrixClient,
this.room.roomId,
[id],
this.event
)
.catch((err) => {
console.log("Failed to send:", err);
});
},
pollClose() {
util
.closePoll(
this.$matrix.matrixClient,
this.room.roomId,
this.event
)
.catch((err) => {
console.log("Failed to send:", err);
});
},
onAddRelation(ignoredevent) {
this.updateAnswers();
},
pollMixinOnEvent(event) {
if (event.getRoomId() !== this.room.roomId) {
return; // Not for this room
}
if (
event.getType().startsWith("org.matrix.msc3381.poll.")) {
this.updateAnswers();
}
},
},
computed: {
pollIsClosed() {
return this.pollEndTs != null && this.pollEndTs !== undefined;
},
userCanClosePoll() {
return this.room && this.room.currentState && this.room.currentState.maySendRedactionForEvent(this.event, this.$matrix.currentUserId);
},
userHasVoted() {
return this.pollAnswers.some(a => a.hasMyVote);
},
pollIsAdmin() {
// Admins can view results of not-yet-closed undisclosed polls.
const me = this.room && this.room.getMember(this.$matrix.currentUserId);
let isAdmin = me && this.room.currentState && this.room.currentState.hasSufficientPowerLevelFor("redact", me.powerLevel);
return isAdmin;
}
},
watch: {
pollResponseRelations: {
handler(newValue, oldValue) {
if (oldValue) {
oldValue.off('Relations.add', this.onAddRelation);
}
if (newValue) {
newValue.on('Relations.add', this.onAddRelation);
}
this.updateAnswers();
},
immediate: true
},
pollEndRelations: {
handler(newValue, oldValue) {
if (oldValue) {
oldValue.off('Relations.add', this.onAddRelation);
}
if (newValue) {
newValue.on('Relations.add', this.onAddRelation);
}
this.updateAnswers();
},
immediate: true
}
}
}