From 443a49a975b0723c2abfb87c9c36f1df9c8ff53e Mon Sep 17 00:00:00 2001 From: conor Date: Sun, 18 Feb 2024 02:54:59 -0800 Subject: [PATCH 1/5] * remove div inside image --- client/src/components/responseMatching/ActiveMatching.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/responseMatching/ActiveMatching.vue b/client/src/components/responseMatching/ActiveMatching.vue index e96dfe6..eba90b2 100644 --- a/client/src/components/responseMatching/ActiveMatching.vue +++ b/client/src/components/responseMatching/ActiveMatching.vue @@ -59,9 +59,9 @@ const { t } = useI18n(); v-tooltip.left.ds900="t('tooltip.noMatch', { response: gameStore.selectedResponse })" class="btn btn-primary w-50 fs-4 d-flex justify-content-center align-items-center" @click="noMatch"> -
+ -
+ From 662dbe45e5c043b9cb7ca31e2c2c353ba77ab770 Mon Sep 17 00:00:00 2001 From: conor Date: Sun, 18 Feb 2024 07:00:22 -0800 Subject: [PATCH 2/5] * fix HMR (removing circular dependancy) * add HMR to stores --- client/src/stores/game.ts | 12 +++++++++++- client/src/stores/room.ts | 34 ++++++++++++++++++++++++---------- client/src/stores/settings.ts | 7 ++++++- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/client/src/stores/game.ts b/client/src/stores/game.ts index 2a12b81..d95802b 100644 --- a/client/src/stores/game.ts +++ b/client/src/stores/game.ts @@ -2,7 +2,7 @@ import socket from '@/socket/socket.js'; import { useRoomStore } from '@/stores/room.js'; -import { defineStore } from 'pinia'; +import { acceptHMRUpdate, defineStore } from 'pinia'; import type { Player, Match as ServerMatch, @@ -363,3 +363,13 @@ export const useGameStore = defineStore('game', { } } }); + +// allow hot-module reloading of the game store +if (import.meta.hot) { + import.meta.hot.accept((newModule) => { + acceptHMRUpdate(useGameStore, import.meta.hot)(newModule); + socket.off(); + useRoomStore().bindEvents(); + useGameStore().bindEvents(); + }); +} diff --git a/client/src/stores/room.ts b/client/src/stores/room.ts index fc6d1a4..0295523 100644 --- a/client/src/stores/room.ts +++ b/client/src/stores/room.ts @@ -1,8 +1,8 @@ // noinspection JSUnusedGlobalSymbols -import router from '@/router/index.js'; +import { type Router, useRouter } from 'vue-router'; import socket from '@/socket/socket.js'; -import { defineStore } from 'pinia'; -import type { Player } from ':common/stateTypes'; +import { acceptHMRUpdate, defineStore } from 'pinia'; +import type { Player } from ':common/stateTypes.js'; interface State { players: Player[]; @@ -11,6 +11,7 @@ interface State { error: string; receivedError: boolean; route: string; + router: Router; } export const useRoomStore = defineStore('room', { @@ -20,7 +21,8 @@ export const useRoomStore = defineStore('room', { roomName: '', error: '', receivedError: false, - route: 'home' + route: 'home', + router: useRouter() }), getters: { self(): Player | undefined { @@ -31,9 +33,6 @@ export const useRoomStore = defineStore('room', { setName(name: string) { this.name = name; }, - setRoomName(roomName: string) { - this.roomName = roomName; - }, setError(error: string) { this.error = error; }, @@ -58,13 +57,13 @@ export const useRoomStore = defineStore('room', { if (data.success) { this.roomName = data.roomName; this.error = ''; - await router.push({ + await this.router.push({ name: 'game', params: { roomName: data.roomName } }); } else { if (this.route !== 'home') { - await router.push({ + await this.router.push({ name: 'home', params: { error: data.error } }); @@ -79,8 +78,23 @@ export const useRoomStore = defineStore('room', { }); socket.on('kickPlayer', async (data: { error: string }) => { - await router.push({ name: 'home', query: { error: data.error } }); + await this.router.push({ name: 'home', query: { error: data.error } }); }); } } }); + +// allow hot module reloading of the room store +if (import.meta.hot) { + import.meta.hot.accept((newModule) => { + acceptHMRUpdate(useRoomStore, import.meta.hot)(newModule); + // unfortunately can't unbind all listeners because game would be effected + // and can't rebind game because of circular dependancy + // so each socket listener has to be offed manually + socket.off('connect'); + socket.off('updatePlayers'); + socket.off('joinRoom'); + socket.off('kickPlayer'); + useRoomStore().bindEvents(); + }); +} diff --git a/client/src/stores/settings.ts b/client/src/stores/settings.ts index 1ed23e5..7be595f 100644 --- a/client/src/stores/settings.ts +++ b/client/src/stores/settings.ts @@ -1,4 +1,4 @@ -import { defineStore } from 'pinia'; +import { acceptHMRUpdate, defineStore } from 'pinia'; interface State { volume: number; @@ -45,3 +45,8 @@ export const useSettingsStore = defineStore('settings', { } } }); + +// allow hot-module reloading of the settings store +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useSettingsStore, import.meta.hot)); +} From f4cc2220d07bfeb72fe2af976e51db136bab6f33 Mon Sep 17 00:00:00 2001 From: conor Date: Sun, 18 Feb 2024 07:29:09 -0800 Subject: [PATCH 3/5] * get HMR working for locales --- client/src/locales/index.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/locales/index.ts b/client/src/locales/index.ts index 9648c3c..d65d6bd 100644 --- a/client/src/locales/index.ts +++ b/client/src/locales/index.ts @@ -7,9 +7,19 @@ const messages = { } }; -export default createI18n<[typeof en], 'en'>({ +const i18n = createI18n<[typeof en], 'en'>({ locale: 'en', fallbackLocale: 'en', legacy: false, messages }); + +export default i18n; + +if (import.meta.hot) { + import.meta.hot.accept('./en.ts', (newEn) => { + if (newEn) { + i18n.global.setLocaleMessage('en', newEn.default); + } + }); +} From 772e1e7734af7e565d7bf46548d8dd81ea22d077 Mon Sep 17 00:00:00 2001 From: conor Date: Mon, 19 Feb 2024 02:44:58 -0800 Subject: [PATCH 4/5] * add callback to pollservice so it can send updates to the client if a vote changes due to disconnect --- client/.eslintrc.cjs | 2 +- client/src/stores/game.ts | 6 +++--- common/src/socketioTypes.ts | 13 +++++++++++-- common/src/stateTypes.ts | 2 ++ server/src/routes/gameHandlers.ts | 10 +++++++--- server/src/state/gameState.ts | 7 ++++++- server/src/state/pollService.ts | 13 ++++++++++--- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs index a8c687c..9d41601 100644 --- a/client/.eslintrc.cjs +++ b/client/.eslintrc.cjs @@ -36,7 +36,7 @@ module.exports = { 'vue/no-unused-properties': 2, 'vue/no-unused-refs': 2, '@intlify/vue-i18n/valid-message-syntax': 2, - '@intlify/vue-i18n/key-format-style': [2, 'camelCase', {allowArray: true}], + '@intlify/vue-i18n/key-format-style': [2, 'camelCase', { allowArray: true }], '@intlify/vue-i18n/no-duplicate-keys-in-locale': 2, '@intlify/vue-i18n/no-missing-keys-in-other-locales': 2, 'prettier/prettier': 2 diff --git a/client/src/stores/game.ts b/client/src/stores/game.ts index d95802b..cd2243b 100644 --- a/client/src/stores/game.ts +++ b/client/src/stores/game.ts @@ -6,11 +6,11 @@ import { acceptHMRUpdate, defineStore } from 'pinia'; import type { Player, Match as ServerMatch, - PollName, VoteCount, Score as ServerScore, Responses, - MidgameConnectData + MidgameConnectData, + PollVoteCount } from ':common/stateTypes'; import type { Options, VisibleOptions } from ':common/options'; import { defaultOptions } from ':common/options'; @@ -244,7 +244,7 @@ export const useGameStore = defineStore('game', { this.selectionType = selectionType; }); - socket.on('setVoteCount', (data: { pollName: PollName } & VoteCount) => { + socket.on('setVoteCount', (data: PollVoteCount) => { this.voteCounts[data.pollName] = { count: data.count, next: data.next }; }); socket.on('beginPrompt', (prompt: string) => { diff --git a/common/src/socketioTypes.ts b/common/src/socketioTypes.ts index 4a77029..5ed70f8 100644 --- a/common/src/socketioTypes.ts +++ b/common/src/socketioTypes.ts @@ -1,7 +1,16 @@ import { Server, Socket } from 'socket.io'; import { Socket as ClientSocket } from 'socket.io-client'; import { SettableOptions, VisibleOptions } from './options'; -import { Player, PollName, Match, MidgameConnectData, Responses, SelectionType, VoteCount, Score } from './stateTypes'; +import { + Player, + PollName, + Match, + MidgameConnectData, + Responses, + SelectionType, + Score, + PollVoteCount +} from './stateTypes'; import { Result } from './result'; interface ServerToClientRoomEvents { @@ -27,7 +36,7 @@ interface ServerToClientGameEvents { nextSelection(args: { selector: string; selectionType: SelectionType }): void; - setVoteCount(args: { pollName: PollName } & VoteCount): void; + setVoteCount(args: PollVoteCount): void; selectionTypeChosen(selectionType: SelectionType): void; diff --git a/common/src/stateTypes.ts b/common/src/stateTypes.ts index d122e9a..58ba271 100644 --- a/common/src/stateTypes.ts +++ b/common/src/stateTypes.ts @@ -36,6 +36,8 @@ export type VoteCount = { next: boolean; }; +export type PollVoteCount = { pollName: PollName } & VoteCount; + export const zPollName = z.enum(['skipPrompt', 'startNextRound', 'sikeDispute']); export type PollName = z.infer; diff --git a/server/src/routes/gameHandlers.ts b/server/src/routes/gameHandlers.ts index 1a183d5..ef7050d 100644 --- a/server/src/routes/gameHandlers.ts +++ b/server/src/routes/gameHandlers.ts @@ -9,7 +9,7 @@ import type { Result } from ':common/result'; import { isErr, isOk, isSuccess } from ':common/result'; import type { SettableOptions } from ':common/options'; import { getSettableOptionsSchema, getVisibleOptionsSchema } from ':common/options'; -import type { PollName } from ':common/stateTypes'; +import type { PollName, PollVoteCount } from ':common/stateTypes'; import { zPollName } from ':common/stateTypes'; import type { TypedServer, TypedSocket } from ':common/socketioTypes'; import type { Responses } from ':common/stateTypes'; @@ -216,11 +216,11 @@ function registerCallbacks(io: TypedServer, room: Room) { //continueSelection(io, room); //}); - state.registerDisputeCompleteCb((action) => { + state.registerDisputeCompleteCb((action: string) => { applyDisputeAction(io, room, action); }); - state.registerMatchingCompleteCb((selectorActive) => { + state.registerMatchingCompleteCb((selectorActive: boolean) => { // give a little time to show score before moving on to next selection if (!selectorActive) { state.promptTimeout = setTimeout(() => { @@ -228,6 +228,10 @@ function registerCallbacks(io: TypedServer, room: Room) { }, 5000); } }); + + state.registerPollVoteUpdateCb((pollVoteCount: PollVoteCount) => { + io.to(room.name).emit('setVoteCount', pollVoteCount); + }); } function beginPrompt(io: TypedServer, room: Room) { diff --git a/server/src/state/gameState.ts b/server/src/state/gameState.ts index 41c7907..f8c4925 100644 --- a/server/src/state/gameState.ts +++ b/server/src/state/gameState.ts @@ -15,7 +15,8 @@ import type { SelectionType, Stage, Player as RoomPlayer, - Score + Score, + PollVoteCount } from ':common/stateTypes'; type Player = { @@ -123,6 +124,10 @@ export class GameState { this._matchingCompleteCb = cb; } + registerPollVoteUpdateCb(cb: (pollVoteCounts: PollVoteCount) => void): void { + this.pollService.registerPollVoteUpdateCb(cb); + } + /*** PROMPT RESPONSE state changes ***/ hasNewPrompt(): boolean { // return false if no rounds left diff --git a/server/src/state/pollService.ts b/server/src/state/pollService.ts index 12a6fdb..a9ed3a2 100644 --- a/server/src/state/pollService.ts +++ b/server/src/state/pollService.ts @@ -1,7 +1,7 @@ import type { GameState } from './gameState'; import type { Result } from ':common/result'; import { Err, Ok, Warn } from ':common/result'; -import type { PollName, Stage } from ':common/stateTypes'; +import type { PollName, PollVoteCount, Stage } from ':common/stateTypes'; import logger from '../logger/logger'; type Poll = { @@ -15,12 +15,16 @@ type Poll = { export class PollService { private gameState: GameState; private readonly polls: Map; - + private _pollVoteUpdateCb: null | ((pollVoteCounts: PollVoteCount) => void) = null; constructor(gameState: GameState) { this.gameState = gameState; this.polls = new Map(); } + registerPollVoteUpdateCb(cb: (pollVoteCounts: PollVoteCount) => void): void { + this._pollVoteUpdateCb = cb; + } + registerPoll( pollName: PollName, completeCb: () => void, @@ -115,10 +119,13 @@ export class PollService { } disconnect(id: string): void { - for (const poll of this.polls.values()) { + for (const [pollName, poll] of this.polls.entries()) { const index = poll.inFavor.indexOf(id); if (index >= 0) { poll.inFavor.splice(index, 1); + if (this._pollVoteUpdateCb) { + this._pollVoteUpdateCb({ pollName, count: this._countVotes(poll), next: this._nextComplete(poll) }); + } } } } From ef9a8dc9b30345924f764713b889e8da8905ccf1 Mon Sep 17 00:00:00 2001 From: conor Date: Mon, 19 Feb 2024 05:52:44 -0800 Subject: [PATCH 5/5] * make logo clickable --- client/src/App.vue | 25 ++++++++++++++++++++++++- client/src/views/About.vue | 2 +- client/src/views/HowToPlay.vue | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/client/src/App.vue b/client/src/App.vue index 2117f2b..9c64602 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -1,13 +1,36 @@