keanu-weblite/src/components/messages/pollMixin.js

243 lines
8.2 KiB
JavaScript

import util from "../../plugins/utils";
export default {
data() {
return {
pollQuestion: "",
pollAnswers: [],
pollTotalVotes: 0,
pollResponseRelations: null,
pollEndRelations: null,
pollEndTs: null,
pollIsDisclosed: true,
pollTentativeAnswer: null,
pollViewResults: false,
};
},
mounted() {
this.$matrix.on("Room.timeline", this.pollMixinOnEvent);
this.pollQuestion =
(this.event &&
this.event.getContent()["m.poll.start"] &&
this.event.getContent()["m.poll.start"]["question"]["body"]) ||
(this.event &&
this.event.getContent()["org.matrix.msc3381.poll.start"] &&
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()["m.poll.start"] && this.event.getContent()["m.poll.start"]["answers"]) ||
(this.event &&
this.event.getContent()["org.matrix.msc3381.poll.start"] &&
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 = false;
if (this.event) {
if (this.event.getContent()["m.poll.start"]) {
this.pollIssDisclosed = this.event.getContent()["m.poll.start"]["kind"] != "m.poll.undisclosed" || false;
} else if (this.event.getContent()["org.matrix.msc3381.poll.start"]) {
this.pollIssDisclosed =
this.event.getContent()["org.matrix.msc3381.poll.start"]["kind"] != "org.matrix.msc3381.poll.undisclosed" ||
false;
}
}
// Look for poll end
this.pollEndRelations =
this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), "m.reference", "m.poll.end") ||
this.timelineSet.relations.getChildEventsForEvent(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.relations.getChildEventsForEvent(this.event.getId(), "m.reference", "m.poll.response") ||
this.timelineSet.relations.getChildEventsForEvent(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()["m.poll.response"] && vote.getContent()["m.poll.response"]["answers"]) ||
(vote.getContent()["org.matrix.msc3381.poll.response"] &&
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 percentages. For algorithm, see here: https://revs.runtime-revolution.com/getting-100-with-rounded-percentages-273ffa70252b
// (need to add up to 100%)
if (totalVotes > 0) {
let a = answerArray.map((a) => {
let votes = (100 * a.numVotes) / totalVotes;
return {
answer: a,
unrounded: votes,
floor: Math.floor(votes),
};
});
a.sort((item1, item2) => {
Math.abs(item2.floor) - Math.abs(item1.floor);
});
var diff =
100 -
a.reduce((previousValue, currentValue) => {
return previousValue + currentValue.floor;
}, 0);
const maxVotes = Math.max(...a.map((item) => item.unrounded));
a.map((answer, index) => {
answer.answer.percentage = index < diff ? answer.floor + 1 : answer.floor;
answer.answer.winner = answer.unrounded == maxVotes;
});
}
this.pollAnswers = answerArray;
this.pollTotalVotes = totalVotes;
},
pollAnswer(id) {
// Only if not voted and not currently viewing poll results.
if (!this.userHasVoted && !this.pollViewResults) {
if (id == this.pollTentativeAnswer) {
this.pollTentativeAnswer = null;
} else {
this.pollTentativeAnswer = id;
}
}
},
pollAnswerSubmit() {
if (!this.pollTentativeAnswer || this.pollIsClosed) {
return;
}
util
.sendPollAnswer(this.$matrix.matrixClient, this.room.roomId, [this.pollTentativeAnswer], this.event)
.catch((err) => {
console.log("Failed to send:", err);
})
.finally(() => {
this.pollTentativeAnswer = null;
});
},
pollClose() {
util.closePoll(this.$matrix.matrixClient, this.room.roomId, this.event).then(() => {
this.$emit("poll-closed", this);
}).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
}
this.$matrix.matrixClient.decryptEventIfNeeded(event).then(() => {
if (event.getType().startsWith("m.poll.") || 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);
},
pollNumAnswers() {
return this.pollTotalVotes;
},
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,
},
},
};