Export Winner via Email

This commit is contained in:
jugeeya 2024-08-23 12:06:53 -07:00
parent d9c2b17dc9
commit cb26ae69ad
6 changed files with 211 additions and 19 deletions

View file

@ -35,6 +35,7 @@
<NightOrderModal />
<VoteHistoryModal />
<GameStateModal />
<WinnerModal />
<Gradients />
<span id="version">v{{ version }}</span>
</div>
@ -56,6 +57,7 @@ import NightOrderModal from "./components/modals/NightOrderModal";
import FabledModal from "@/components/modals/FabledModal";
import VoteHistoryModal from "@/components/modals/VoteHistoryModal";
import GameStateModal from "@/components/modals/GameStateModal";
import WinnerModal from "@/components/modals/WinnerModal";
export default {
components: {
@ -71,7 +73,8 @@ export default {
Menu,
EditionModal,
RolesModal,
Gradients
Gradients,
WinnerModal,
},
computed: {
...mapState(["grimoire", "session"]),
@ -85,6 +88,12 @@ export default {
methods: {
keyup({ key, ctrlKey, metaKey }) {
if (ctrlKey || metaKey) return;
// Check if the active element is an input, textarea, or select
const activeElement = document.activeElement;
const isInputField = ["INPUT", "TEXTAREA", "SELECT"].includes(
activeElement.tagName,
);
if (isInputField) return;
switch (key.toLocaleLowerCase()) {
case "g":
this.$store.commit("toggleGrimoire");

View file

@ -204,6 +204,10 @@
Game State JSON
<em><font-awesome-icon icon="file-code"/></em>
</li>
<li @click="toggleModal('winner')">
Export Winner
<em><font-awesome-icon icon="file-export"/></em>
</li>
<li>
<a href="https://discord.gg/Gd7ybwWbFk" target="_blank">
Join Discord

View file

@ -31,25 +31,29 @@ export default {
Modal
},
computed: {
gamestate: function() {
return JSON.stringify({
bluffs: this.players.bluffs.map(({ id }) => id),
edition: this.edition.isOfficial
? { id: this.edition.id }
: this.edition,
roles: this.edition.isOfficial
? ""
: this.$store.getters.customRolesStripped,
fabled: this.players.fabled.map(fabled =>
fabled.isCustom ? fabled : { id: fabled.id }
),
players: this.players.players.map(player => ({
...player,
role: player.role.id || {}
}))
});
gamestate: function () {
return JSON.stringify(
{
bluffs: this.players.bluffs.map(({ id }) => id),
edition: this.edition.isOfficial
? { id: this.edition.id }
: this.edition,
roles: this.edition.isOfficial
? ""
: this.$store.getters.customRolesStripped,
fabled: this.players.fabled.map((fabled) =>
fabled.isCustom ? fabled : { id: fabled.id },
),
players: this.players.players.map((player) => ({
...player,
role: player.role.id || {},
})),
},
null,
2,
);
},
...mapState(["modals", "players", "edition", "roles", "session"])
...mapState(["modals", "players", "edition", "roles", "session"]),
},
data() {
return {

View file

@ -0,0 +1,173 @@
<template>
<Modal class="winner" v-if="modals.winner" @close="toggleModal('winner')">
<h3>Export Winner</h3>
<div class="button-container">
<div
class="button townsfolk"
:class="{ selected: winner === 'townsfolk' }"
@click="setWinner('townsfolk')"
>
<img src="../../assets/icons/virgin.png" class="icon" /> Town
</div>
<div
class="button demon"
:class="{ selected: winner === 'demon' }"
@click="setWinner('demon')"
>
<img src="../../assets/icons/imp.png" class="icon" /> Demon
</div>
</div>
<div>
<label for="email">Email Address: </label>
<input type="email" v-model="email" id="email" />
</div>
<h4>Preview</h4>
<textarea
:value="gameresults"
@input.stop="input = $event.target.value"
@click="$event.target.select()"
@keyup.stop=""
></textarea>
<div class="export-button-container">
<div class="button fabled" @click="exportResult">Export Result</div>
</div>
<p class="note">
Ensure mailto is enabled in your browser.
<a href="https://stackoverflow.com/a/17647243" target="_blank"
>See here for more details.</a
>
</p>
</Modal>
</template>
<script>
import Modal from "./Modal";
import { mapMutations, mapState } from "vuex";
export default {
components: {
Modal,
},
computed: {
gameresults: function () {
const gameState = {
bluffs: this.players.bluffs.map(({ id }) => id),
edition: this.edition.isOfficial
? { id: this.edition.id }
: this.edition,
roles: this.edition.isOfficial
? ""
: this.$store.getters.customRolesStripped,
fabled: this.players.fabled.map((fabled) =>
fabled.isCustom ? fabled : { id: fabled.id }
),
players: this.players.players.map((player) => ({
...player,
role: player.role.id || {},
})),
};
const tableHeader = "Player\tRole\tWinner\n";
const tableRows = gameState.players
.map(
(player) =>
`${player.name}\t${
this.$store.state.roles.get(player.role).name
}\t${
this.$store.state.roles.get(player.role).team === this.winner
? "Yes"
: "No"
}`,
)
.join("\n");
const table = tableHeader + tableRows;
return table;
},
...mapState(["modals", "players", "edition", "roles", "session"]),
},
data() {
return {
winner: "",
email: "",
};
},
methods: {
setWinner: function (team) {
if (this.winner === team) {
this.winner = "";
} else {
this.winner = team;
}
},
exportResult: function () {
if (this.email && this.winner) {
const table = this.gameresults;
const currentDate = new Date();
const subject = encodeURIComponent(
"Blood on the Clocktower Results: " + currentDate.toLocaleString()
);
const body = encodeURIComponent(`${table}`);
const mailtoLink = `mailto:${this.email}?subject=${subject}&body=${body}`;
window.open(mailtoLink);
} else {
alert("Please select a winner and enter an email address.");
}
},
...mapMutations(["toggleModal"]),
},
};
</script>
<style lang="scss" scoped>
@import "../../vars.scss";
h3 {
margin: 0 40px;
}
textarea {
background: transparent;
color: white;
white-space: pre-wrap;
word-break: break-all;
border: 1px solid rgba(255, 255, 255, 0.5);
width: 60vw;
height: 30vh;
max-width: 100%;
margin: 5px 0;
}
.button-container {
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
}
.button {
display: inline-block;
color: gray;
display: flex;
}
.button.selected {
background-color: #007bff;
color: white;
}
.icon {
width: 40px;
height: 40px;
}
.export-button-container {
display: flex;
justify-content: center;
margin-top: 1rem;
}
</style>

View file

@ -22,6 +22,7 @@ const faIcons = [
"ExchangeAlt",
"ExclamationTriangle",
"FileCode",
"FileExport",
"FileUpload",
"HandPaper",
"HandPointRight",

View file

@ -112,6 +112,7 @@ export default new Vuex.Store({
edition: false,
fabled: false,
gameState: false,
winner: false,
nightOrder: false,
reference: false,
reminder: false,