mirror of
				https://github.com/bra1n/townsquare.git
				synced 2025-10-21 16:55:12 +00:00 
			
		
		
		
	Add a timer
This commit is contained in:
		
							parent
							
								
									9bafcc2c61
								
							
						
					
					
						commit
						045f7112e0
					
				
					 9 changed files with 330 additions and 38 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,8 @@
 | 
			
		|||
# Release Notes
 | 
			
		||||
 | 
			
		||||
### Version 2.15.4
 | 
			
		||||
- add timer
 | 
			
		||||
 | 
			
		||||
### Version 2.15.3
 | 
			
		||||
- add Huntsman/Damsel to list of available characters
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -121,6 +121,10 @@ export default {
 | 
			
		|||
          if (this.session.isSpectator) return;
 | 
			
		||||
          this.$refs.menu.toggleNight();
 | 
			
		||||
          break;
 | 
			
		||||
        case "t":
 | 
			
		||||
          if (this.session.isSpectator) return;
 | 
			
		||||
          this.$refs.menu.toggleTimer();
 | 
			
		||||
          break;
 | 
			
		||||
        case "escape":
 | 
			
		||||
          this.$store.commit("toggleModal");
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										173
									
								
								src/components/CountdownTimer.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/components/CountdownTimer.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,173 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="countdown-timer">
 | 
			
		||||
    <div
 | 
			
		||||
      id="timer"
 | 
			
		||||
      v-bind:style="[
 | 
			
		||||
        remainingSeconds <= 30 ? { color: 'red' } : { color: 'white' }
 | 
			
		||||
      ]"
 | 
			
		||||
    >
 | 
			
		||||
      {{ formattedMinutes }}:{{ formattedSeconds }}
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="timer-control" v-if="!session.isSpectator">
 | 
			
		||||
      <div class="timer-button" @click="startTimer" v-if="!isTicking">
 | 
			
		||||
        <font-awesome-icon icon="play"></font-awesome-icon>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="timer-button" @click="pauseTimer" v-if="isTicking">
 | 
			
		||||
        <font-awesome-icon icon="pause"></font-awesome-icon>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="timer-button" @click="stopTimer">
 | 
			
		||||
        <font-awesome-icon icon="stop"></font-awesome-icon>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="timer-button" @click="addMinute" v-if="!isTicking">
 | 
			
		||||
        <font-awesome-icon icon="plus"></font-awesome-icon>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="timer-button" @click="subtractMinute" v-if="!isTicking">
 | 
			
		||||
        <font-awesome-icon icon="minus"></font-awesome-icon>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapState } from "vuex";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "CountdownTimer",
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapState(["session"]),
 | 
			
		||||
    remainingSeconds: {
 | 
			
		||||
      get: function() {
 | 
			
		||||
        return this.$store.state.countdownTimer.remainingSeconds;
 | 
			
		||||
      },
 | 
			
		||||
      set: function(newValue) {
 | 
			
		||||
        this.$store.state.countdownTimer.remainingSeconds = newValue;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    totalSeconds: {
 | 
			
		||||
      get: function() {
 | 
			
		||||
        return this.$store.state.countdownTimer.totalSeconds;
 | 
			
		||||
      },
 | 
			
		||||
      set: function(newValue) {
 | 
			
		||||
        this.$store.state.countdownTimer.totalSeconds = newValue;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    isTicking: {
 | 
			
		||||
      get: function() {
 | 
			
		||||
        return this.$store.state.countdownTimer.isTicking;
 | 
			
		||||
      },
 | 
			
		||||
      set: function(newValue) {
 | 
			
		||||
        this.$store.state.countdownTimer.isTicking = newValue;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    formattedMinutes() {
 | 
			
		||||
      let minutes = Math.floor(this.remainingSeconds / 60);
 | 
			
		||||
      if (minutes < 10) {
 | 
			
		||||
        minutes = "0" + minutes;
 | 
			
		||||
      }
 | 
			
		||||
      return minutes;
 | 
			
		||||
    },
 | 
			
		||||
    formattedSeconds() {
 | 
			
		||||
      let seconds = this.remainingSeconds % 60;
 | 
			
		||||
      if (seconds < 10) {
 | 
			
		||||
        seconds = "0" + seconds;
 | 
			
		||||
      }
 | 
			
		||||
      return seconds;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  updated() {
 | 
			
		||||
    clearInterval(this.timerInterval);
 | 
			
		||||
    if (this.isTicking) {
 | 
			
		||||
      this.timerInterval = setInterval(this.elapseTimer, 1000);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  data: () => {
 | 
			
		||||
    return {
 | 
			
		||||
      timerInterval: null
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    startTimer() {
 | 
			
		||||
      clearInterval(this.timerInterval);
 | 
			
		||||
      if (this.remainingSeconds === 0) {
 | 
			
		||||
        this.remainingSeconds = this.totalSeconds;
 | 
			
		||||
      }
 | 
			
		||||
      this.isTicking = true;
 | 
			
		||||
      this.sendTimerUpdate();
 | 
			
		||||
      this.timerInterval = setInterval(this.elapseTimer, 1000);
 | 
			
		||||
    },
 | 
			
		||||
    pauseTimer() {
 | 
			
		||||
      clearInterval(this.timerInterval);
 | 
			
		||||
      this.isTicking = false;
 | 
			
		||||
      this.sendTimerUpdate();
 | 
			
		||||
    },
 | 
			
		||||
    stopTimer() {
 | 
			
		||||
      clearInterval(this.timerInterval);
 | 
			
		||||
      this.remainingSeconds = this.totalSeconds;
 | 
			
		||||
      this.pauseTimer();
 | 
			
		||||
    },
 | 
			
		||||
    sendTimerUpdate() {
 | 
			
		||||
      if (this.session.isSpectator) return;
 | 
			
		||||
      let payload = {
 | 
			
		||||
        remainingSeconds: this.remainingSeconds,
 | 
			
		||||
        totalSeconds: this.totalSeconds,
 | 
			
		||||
        isTicking: this.isTicking
 | 
			
		||||
      };
 | 
			
		||||
      this.$store.commit("session/distributeTimerAction", payload);
 | 
			
		||||
    },
 | 
			
		||||
    elapseTimer() {
 | 
			
		||||
      if (this.remainingSeconds <= 0) {
 | 
			
		||||
        clearInterval(this.timerInterval);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      this.remainingSeconds--;
 | 
			
		||||
 | 
			
		||||
      // Update local state on ST, so if a player joins while timer is ticking, they will get updated timer state.
 | 
			
		||||
      if (!this.session.isSpectator) {
 | 
			
		||||
        let payload = {
 | 
			
		||||
          remainingSeconds: this.remainingSeconds,
 | 
			
		||||
          totalSeconds: this.totalSeconds,
 | 
			
		||||
          isTicking: this.isTicking
 | 
			
		||||
        };
 | 
			
		||||
        this.$store.commit("session/updateTimerState", payload);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    addMinute() {
 | 
			
		||||
      this.totalSeconds += 60;
 | 
			
		||||
      this.remainingSeconds += 60;
 | 
			
		||||
      this.sendTimerUpdate();
 | 
			
		||||
    },
 | 
			
		||||
    subtractMinute() {
 | 
			
		||||
      this.totalSeconds = Math.max(0, this.totalSeconds - 60);
 | 
			
		||||
      this.remainingSeconds = Math.max(0, this.remainingSeconds - 60);
 | 
			
		||||
      this.sendTimerUpdate();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.countdown-timer {
 | 
			
		||||
  border-color: white;
 | 
			
		||||
  border-style: solid;
 | 
			
		||||
  border-width: thick;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  padding: 5px 5px 5px;
 | 
			
		||||
}
 | 
			
		||||
.timer-control {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  align-items: flex-start;
 | 
			
		||||
  justify-content: space-around;
 | 
			
		||||
}
 | 
			
		||||
.timer-button {
 | 
			
		||||
  margin: 0 5px 0 5px;
 | 
			
		||||
  filter: drop-shadow(0 2px 1px black);
 | 
			
		||||
}
 | 
			
		||||
#timer {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  text-shadow: 0 2px 1px black, 0 -2px 1px black, 2px 0 1px black,
 | 
			
		||||
    -2px 0 1px black;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -138,6 +138,10 @@
 | 
			
		|||
              Send Characters
 | 
			
		||||
              <em><font-awesome-icon icon="theater-masks"/></em>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li v-if="!session.isSpectator" @click="toggleTimer">
 | 
			
		||||
              Toggle timer
 | 
			
		||||
              <em>[T]</em>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li
 | 
			
		||||
              v-if="session.voteHistory.length || !session.isSpectator"
 | 
			
		||||
              @click="toggleModal('voteHistory')"
 | 
			
		||||
| 
						 | 
				
			
			@ -345,6 +349,9 @@ export default {
 | 
			
		|||
        this.$store.commit("session/setMarkedPlayer", -1);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    toggleTimer() {
 | 
			
		||||
      this.$store.commit("toggleTimer");
 | 
			
		||||
    },
 | 
			
		||||
    ...mapMutations([
 | 
			
		||||
      "toggleGrimoire",
 | 
			
		||||
      "toggleMenu",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,39 +46,47 @@
 | 
			
		|||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="fabled" :class="{ closed: !isFabledOpen }" v-if="fabled.length">
 | 
			
		||||
      <h3>
 | 
			
		||||
        <span>Fabled</span>
 | 
			
		||||
        <font-awesome-icon icon="times-circle" @click.stop="toggleFabled" />
 | 
			
		||||
        <font-awesome-icon icon="plus-circle" @click.stop="toggleFabled" />
 | 
			
		||||
      </h3>
 | 
			
		||||
      <ul>
 | 
			
		||||
        <li
 | 
			
		||||
          v-for="(role, index) in fabled"
 | 
			
		||||
          :key="index"
 | 
			
		||||
          @click="removeFabled(index)"
 | 
			
		||||
        >
 | 
			
		||||
          <div
 | 
			
		||||
            class="night-order first"
 | 
			
		||||
            v-if="nightOrder.get(role).first && grimoire.isNightOrder"
 | 
			
		||||
    <div id="top-left">
 | 
			
		||||
      <div
 | 
			
		||||
        class="fabled"
 | 
			
		||||
        :class="{ closed: !isFabledOpen }"
 | 
			
		||||
        v-if="fabled.length"
 | 
			
		||||
      >
 | 
			
		||||
        <h3>
 | 
			
		||||
          <span>Fabled</span>
 | 
			
		||||
          <font-awesome-icon icon="times-circle" @click.stop="toggleFabled" />
 | 
			
		||||
          <font-awesome-icon icon="plus-circle" @click.stop="toggleFabled" />
 | 
			
		||||
        </h3>
 | 
			
		||||
        <ul>
 | 
			
		||||
          <li
 | 
			
		||||
            v-for="(role, index) in fabled"
 | 
			
		||||
            :key="index"
 | 
			
		||||
            @click="removeFabled(index)"
 | 
			
		||||
          >
 | 
			
		||||
            <em>{{ nightOrder.get(role).first }}.</em>
 | 
			
		||||
            <span v-if="role.firstNightReminder">{{
 | 
			
		||||
              role.firstNightReminder
 | 
			
		||||
            }}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div
 | 
			
		||||
            class="night-order other"
 | 
			
		||||
            v-if="nightOrder.get(role).other && grimoire.isNightOrder"
 | 
			
		||||
          >
 | 
			
		||||
            <em>{{ nightOrder.get(role).other }}.</em>
 | 
			
		||||
            <span v-if="role.otherNightReminder">{{
 | 
			
		||||
              role.otherNightReminder
 | 
			
		||||
            }}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <Token :role="role"></Token>
 | 
			
		||||
        </li>
 | 
			
		||||
      </ul>
 | 
			
		||||
            <div
 | 
			
		||||
              class="night-order first"
 | 
			
		||||
              v-if="nightOrder.get(role).first && grimoire.isNightOrder"
 | 
			
		||||
            >
 | 
			
		||||
              <em>{{ nightOrder.get(role).first }}.</em>
 | 
			
		||||
              <span v-if="role.firstNightReminder">{{
 | 
			
		||||
                role.firstNightReminder
 | 
			
		||||
              }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
              class="night-order other"
 | 
			
		||||
              v-if="nightOrder.get(role).other && grimoire.isNightOrder"
 | 
			
		||||
            >
 | 
			
		||||
              <em>{{ nightOrder.get(role).other }}.</em>
 | 
			
		||||
              <span v-if="role.otherNightReminder">{{
 | 
			
		||||
                role.otherNightReminder
 | 
			
		||||
              }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <Token :role="role"></Token>
 | 
			
		||||
          </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <CountdownTimer v-if="grimoire.isTimerEnabled" />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <ReminderModal :player-index="selectedPlayer"></ReminderModal>
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +98,7 @@
 | 
			
		|||
import { mapGetters, mapState } from "vuex";
 | 
			
		||||
import Player from "./Player";
 | 
			
		||||
import Token from "./Token";
 | 
			
		||||
import CountdownTimer from "./CountdownTimer";
 | 
			
		||||
import ReminderModal from "./modals/ReminderModal";
 | 
			
		||||
import RoleModal from "./modals/RoleModal";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +106,7 @@ export default {
 | 
			
		|||
  components: {
 | 
			
		||||
    Player,
 | 
			
		||||
    Token,
 | 
			
		||||
    CountdownTimer,
 | 
			
		||||
    RoleModal,
 | 
			
		||||
    ReminderModal
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			@ -394,16 +404,32 @@ export default {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***** Demon bluffs / Fabled *******/
 | 
			
		||||
#townsquare > .bluffs,
 | 
			
		||||
#townsquare > .fabled {
 | 
			
		||||
#top-left {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 10px;
 | 
			
		||||
  left: 10px;
 | 
			
		||||
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  align-items: flex-start;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***** Demon bluffs / Fabled / Countdown Timer *******/
 | 
			
		||||
#townsquare > .bluffs,
 | 
			
		||||
#top-left > .fabled,
 | 
			
		||||
#top-left > .countdown-timer {
 | 
			
		||||
  &.bluffs {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    bottom: 10px;
 | 
			
		||||
  }
 | 
			
		||||
  &.fabled {
 | 
			
		||||
    top: 10px;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
  &.countdown-timer {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
  margin: 5px 0 0 0;
 | 
			
		||||
  left: 10px;
 | 
			
		||||
  background: rgba(0, 0, 0, 0.5);
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,8 +27,12 @@ const faIcons = [
 | 
			
		|||
  "Heartbeat",
 | 
			
		||||
  "Image",
 | 
			
		||||
  "Link",
 | 
			
		||||
  "Minus",
 | 
			
		||||
  "MinusCircle",
 | 
			
		||||
  "Pause",
 | 
			
		||||
  "PeopleArrows",
 | 
			
		||||
  "Play",
 | 
			
		||||
  "Plus",
 | 
			
		||||
  "PlusCircle",
 | 
			
		||||
  "Question",
 | 
			
		||||
  "Random",
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +41,7 @@ const faIcons = [
 | 
			
		|||
  "SearchPlus",
 | 
			
		||||
  "Skull",
 | 
			
		||||
  "Square",
 | 
			
		||||
  "Stop",
 | 
			
		||||
  "TheaterMasks",
 | 
			
		||||
  "Times",
 | 
			
		||||
  "TimesCircle",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,9 +105,15 @@ export default new Vuex.Store({
 | 
			
		|||
      isStatic: false,
 | 
			
		||||
      isMuted: false,
 | 
			
		||||
      isImageOptIn: false,
 | 
			
		||||
      isTimerEnabled: false,
 | 
			
		||||
      zoom: 0,
 | 
			
		||||
      background: ""
 | 
			
		||||
    },
 | 
			
		||||
    countdownTimer: {
 | 
			
		||||
      totalSeconds: 300,
 | 
			
		||||
      remainingSeconds: 300,
 | 
			
		||||
      isTicking: false
 | 
			
		||||
    },
 | 
			
		||||
    modals: {
 | 
			
		||||
      edition: false,
 | 
			
		||||
      fabled: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -171,6 +177,7 @@ export default new Vuex.Store({
 | 
			
		|||
    toggleNight: toggle("isNight"),
 | 
			
		||||
    toggleGrimoire: toggle("isPublic"),
 | 
			
		||||
    toggleImageOptIn: toggle("isImageOptIn"),
 | 
			
		||||
    toggleTimer: toggle("isTimerEnabled"),
 | 
			
		||||
    toggleModal({ modals }, name) {
 | 
			
		||||
      if (name) {
 | 
			
		||||
        modals[name] = !modals[name];
 | 
			
		||||
| 
						 | 
				
			
			@ -260,6 +267,14 @@ export default new Vuex.Store({
 | 
			
		|||
        state.edition = edition;
 | 
			
		||||
      }
 | 
			
		||||
      state.modals.edition = false;
 | 
			
		||||
    },
 | 
			
		||||
    /**
 | 
			
		||||
     * Set timer state
 | 
			
		||||
     * @param state
 | 
			
		||||
     * @param payload Object with keys: `remainingSeconds`, `totalSeconds`, and `isTicking`
 | 
			
		||||
     */
 | 
			
		||||
    setTimerState(state, payload) {
 | 
			
		||||
      state.countdownTimer = payload;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  plugins: [persistence, socket]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,12 @@ const state = () => ({
 | 
			
		|||
  voteHistory: [],
 | 
			
		||||
  markedPlayer: -1,
 | 
			
		||||
  isVoteHistoryAllowed: true,
 | 
			
		||||
  isRolesDistributed: false
 | 
			
		||||
  isRolesDistributed: false,
 | 
			
		||||
  countdownTimer: {
 | 
			
		||||
    totalSeconds: 300,
 | 
			
		||||
    remainingSeconds: 300,
 | 
			
		||||
    isTicking: false
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const getters = {};
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +99,12 @@ const mutations = {
 | 
			
		|||
  clearVoteHistory(state) {
 | 
			
		||||
    state.voteHistory = [];
 | 
			
		||||
  },
 | 
			
		||||
  updateTimerState(state, payload) {
 | 
			
		||||
    state.countdownTimer = payload;
 | 
			
		||||
  },
 | 
			
		||||
  distributeTimerAction(state, payload) {
 | 
			
		||||
    state.countdownTimer = payload;
 | 
			
		||||
  },
 | 
			
		||||
  /**
 | 
			
		||||
   * Store a vote with and without syncing it to the live session.
 | 
			
		||||
   * This is necessary in order to prevent infinite voting loops.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -205,6 +205,13 @@ class LiveSession {
 | 
			
		|||
      case "pronouns":
 | 
			
		||||
        this._updatePlayerPronouns(params);
 | 
			
		||||
        break;
 | 
			
		||||
      case "timer":
 | 
			
		||||
        this._handleTimerAction(params);
 | 
			
		||||
        break;
 | 
			
		||||
      case "isTimerEnabled":
 | 
			
		||||
        if (!this._isSpectator) return;
 | 
			
		||||
        this._store.commit("toggleTimer", params);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +291,8 @@ class LiveSession {
 | 
			
		|||
        isVoteInProgress: session.isVoteInProgress,
 | 
			
		||||
        markedPlayer: session.markedPlayer,
 | 
			
		||||
        fabled: fabled.map(f => (f.isCustom ? f : { id: f.id })),
 | 
			
		||||
        isTimerEnabled: grimoire.isTimerEnabled,
 | 
			
		||||
        countdownTimer: session.countdownTimer,
 | 
			
		||||
        ...(session.nomination ? { votes: session.votes } : {})
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -307,7 +316,9 @@ class LiveSession {
 | 
			
		|||
      lockedVote,
 | 
			
		||||
      isVoteInProgress,
 | 
			
		||||
      markedPlayer,
 | 
			
		||||
      fabled
 | 
			
		||||
      fabled,
 | 
			
		||||
      isTimerEnabled,
 | 
			
		||||
      countdownTimer
 | 
			
		||||
    } = data;
 | 
			
		||||
    const players = this._store.state.players.players;
 | 
			
		||||
    // adjust number of players
 | 
			
		||||
| 
						 | 
				
			
			@ -365,6 +376,8 @@ class LiveSession {
 | 
			
		|||
      this._store.commit("players/setFabled", {
 | 
			
		||||
        fabled: fabled.map(f => this._store.state.fabled.get(f.id) || f)
 | 
			
		||||
      });
 | 
			
		||||
      this._store.commit("toggleTimer", isTimerEnabled);
 | 
			
		||||
      this._store.commit("setTimerState", countdownTimer);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -668,6 +681,25 @@ class LiveSession {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Distribute new timer action to each player.
 | 
			
		||||
   */
 | 
			
		||||
  distributeTimerAction(payload) {
 | 
			
		||||
    if (this._isSpectator) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this._send("timer", payload);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Handle a timer action.
 | 
			
		||||
   * @param payload
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  _handleTimerAction(payload) {
 | 
			
		||||
    this._store.commit("setTimerState", payload);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A player nomination. ST only
 | 
			
		||||
   * This also syncs the voting speed to the players.
 | 
			
		||||
| 
						 | 
				
			
			@ -703,6 +735,14 @@ class LiveSession {
 | 
			
		|||
    this._send("isNight", this._store.state.grimoire.isNight);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Send the isTimerEnabled status. ST only
 | 
			
		||||
   */
 | 
			
		||||
  setIsTimerEnabled() {
 | 
			
		||||
    if (this._isSpectator) return;
 | 
			
		||||
    this._send("isTimerEnabled", this._store.state.grimoire.isTimerEnabled);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Send the isVoteHistoryAllowed state. ST only
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -859,6 +899,11 @@ export default store => {
 | 
			
		|||
          session.distributeRoles();
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case "session/distributeTimerAction":
 | 
			
		||||
        if (payload) {
 | 
			
		||||
          session.distributeTimerAction(payload);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case "session/nomination":
 | 
			
		||||
      case "session/setNomination":
 | 
			
		||||
        session.nomination(payload);
 | 
			
		||||
| 
						 | 
				
			
			@ -914,6 +959,9 @@ export default store => {
 | 
			
		|||
          session.sendPlayer(payload);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case "toggleTimer":
 | 
			
		||||
        session.setIsTimerEnabled();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue