Skip to content

Commit

Permalink
Share types across client and server (#190)
Browse files Browse the repository at this point in the history
* * pushing what I have from laptop
 * definitely not working but want to work on desktop

* * move types to common package
 * get server dev tools working with common package
 * still need to get production build working

* * get everything working about build
 * except resources aren't copied over to dist folder

* * copy the resources over properly

* * replace typescript enums with typescript unions as I have learned typescript enums have some downsides and unions are preferred

* * remove node_modules from the buildspec.yml because I webpack includes them in the bundle

* * migrate most of client over to common types
  * still need to migrate options, midgameReconnect
  * add typechecking to socketioClient

* * use same options types across the server and client
 * refactor options a bit to make both sides agree
 * fix client build (resolve common imports)

* * used TypedSocketClient instead of standard SocketIO client
 * fix typing errors that ensued from that

* * convert enum to string union

* * update linting

* * minor renaming and prettier
  • Loading branch information
ConorMurphy21 committed Jan 24, 2024
1 parent 1e2eded commit 50191fc
Show file tree
Hide file tree
Showing 60 changed files with 1,837 additions and 1,230 deletions.
2 changes: 2 additions & 0 deletions .idea/HitOrMiss.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/runConfigurations/server_tests.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions buildspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ phases:
commands:
- echo Build started on `date`
- npm run build
- npm prune --omit=dev
post_build:
commands:
- echo Build completed on `date`
Expand All @@ -20,6 +19,5 @@ artifacts:
files:
- '.ebextensions/**/*'
- 'dist/**/*'
- 'node_modules/**/*'
- 'package.json'
- 'package-lock.json'
1 change: 1 addition & 0 deletions client/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = {
parser: '@typescript-eslint/parser'
},
rules: {
'@typescript-eslint/consistent-type-imports': ['error'],
'@typescript-eslint/no-unused-vars': ['off'],
'@typescript-eslint/no-unsafe-assignment': ['off'],
'vue/multi-word-component-names': ['off'],
Expand Down
21 changes: 6 additions & 15 deletions client/src/components/lobby/Options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,11 @@ import { useGameStore } from '@/stores/game.js';
import { mapState } from 'pinia';
import { defineComponent } from 'vue';
type Options = {
promptTimer: number;
numRounds: number;
autoNumRounds: boolean;
promptSkipping: boolean;
sikeDispute: boolean;
sikeRetries: number;
packs: Record<string, boolean>;
customPrompts: string;
};
import type { SettableOptions } from ':common/options';
type NumericKeyOfOptions = {
[K in keyof Options]: Options[K] extends number ? K : never;
}[keyof Options];
[K in keyof SettableOptions]: SettableOptions[K] extends number ? K : never;
}[keyof SettableOptions];
export default defineComponent({
props: {
Expand Down Expand Up @@ -167,14 +158,14 @@ export default defineComponent({
onNumRoundChange(event: Event) {
this.onNumChange(event, 'numRounds', { autoNumRounds: false });
},
onNumChange(event: Event, label: NumericKeyOfOptions, options?: Partial<Options>) {
onNumChange(event: Event, label: NumericKeyOfOptions, options?: Partial<SettableOptions>) {
const inputValue = parseInt((event.currentTarget! as HTMLInputElement).value);
const actualValue = this.options[label];
if (Math.abs(inputValue - actualValue) !== 0) {
this.validateNum(event, label, options);
}
},
validateNum(event: Event, label: NumericKeyOfOptions, options?: Partial<Options>) {
validateNum(event: Event, label: NumericKeyOfOptions, options?: Partial<SettableOptions>) {
const input = event.currentTarget! as HTMLInputElement;
const inputValue = parseInt(input.value);
const max = parseInt(input.max);
Expand All @@ -197,7 +188,7 @@ export default defineComponent({
},
validateSikeDispute(event: Event) {
const input = event.currentTarget! as HTMLInputElement;
const options: Partial<Options> = {};
const options: Partial<SettableOptions> = {};
options.sikeDispute = input.checked;
if (!input.checked) {
options.sikeRetries = 0;
Expand Down
7 changes: 2 additions & 5 deletions client/src/components/lobby/PlayerCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { defineComponent, type PropType } from 'vue';
import type { Player } from ':common/stateTypes';
type Player = {
active: boolean;
name: string;
};
export default defineComponent({
props: {
player: {
Expand Down
8 changes: 1 addition & 7 deletions client/src/components/lobby/PlayerList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ import PlayerCard from '@/components/lobby/PlayerCard.vue';
import { useRoomStore } from '@/stores/room.js';
import { mapState } from 'pinia';
import { defineComponent } from 'vue';
type Player = {
id: string;
name: string;
leader: boolean;
active: boolean;
};
import type { Player } from ':common/stateTypes';
export default defineComponent({
components: {
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/responseMatching/ActiveMatching.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default defineComponent({
...mapState(useGameStore, ['prompt', 'selectionType', 'selectedResponse', 'selector', 'unmatched', 'sikeDispute'])
},
watch: {
matchedResponse: function (val) {
matchedResponse: function (val: string) {
socket.emit('selectMatch', val);
}
},
Expand Down
18 changes: 3 additions & 15 deletions client/src/components/responseMatching/MatchCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,13 @@
import ClickMp3 from '@/assets/audio/click2.mp3';
import { AudioWrap } from '@/mixins/audiowrap.js';
import { useRoomStore } from '@/stores/room.js';
import { useGameStore } from '@/stores/game.js';
import { useGameStore, type Match } from '@/stores/game.js';
import { mapState, mapActions } from 'pinia';
import { defineComponent, PropType } from 'vue';
import { defineComponent, type PropType } from 'vue';
import { type Player } from ':common/stateTypes';
const click = new AudioWrap(ClickMp3);
type Player = {
id: string;
name: string;
leader: boolean;
active: boolean;
};
type Match = {
player: Player;
response: string;
exact: boolean;
};
export default defineComponent({
props: {
player: {
Expand Down
8 changes: 1 addition & 7 deletions client/src/components/responseMatching/MatchingSummary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,7 @@ import { useRoomStore } from '@/stores/room.js';
import { useGameStore } from '@/stores/game.js';
import { mapState } from 'pinia';
import { defineComponent } from 'vue';
type Player = {
id: string;
name: string;
leader: boolean;
active: boolean;
};
import type { Player } from ':common/stateTypes';
const click = new AudioWrap(ClickMp3);
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/responseSelection/Selection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default defineComponent({
...mapState(useGameStore, ['prompt', 'selectionType', 'selector', 'firstSelection', 'isSelector'])
},
watch: {
response: function (val) {
response: function (val: string) {
socket.emit('selectResponse', val);
}
},
Expand Down
3 changes: 2 additions & 1 deletion client/src/directives/tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DirectiveBinding, nextTick } from 'vue';
import type { DirectiveBinding } from 'vue';
import { nextTick } from 'vue';
import { Tooltip } from 'bootstrap';
import { useSettingsStore } from '@/stores/settings.js';

Expand Down
3 changes: 2 additions & 1 deletion client/src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createWebHistory, createRouter, RouteLocation } from 'vue-router';
import type { RouteLocation } from 'vue-router';
import { createWebHistory, createRouter } from 'vue-router';
import Home from '@/views/Home.vue';
const Game = () => import('@/views/Game.vue');
const About = () => import('@/views/About.vue');
Expand Down
5 changes: 3 additions & 2 deletions client/src/socket/socket.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import io, { Socket } from 'socket.io-client';
import io from 'socket.io-client';
import type { TypedClientSocket } from ':common/socketioTypes';

let socket: Socket;
let socket: TypedClientSocket;
if (process.env.NODE_ENV !== 'production') {
socket = io('http://localhost:5000', { withCredentials: false });
} else {
Expand Down
98 changes: 23 additions & 75 deletions client/src/stores/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import socket from '@/socket/socket.js';
import { useRoomStore } from '@/stores/room.js';
import { defineStore } from 'pinia';
import type {
Player,
Match as ServerMatch,
PollName,
VoteCount,
Score as ServerScore,
Responses,
MidgameConnectData
} from ':common/stateTypes';
import type { Options, VisibleOptions } from ':common/options';
import { defaultOptions } from ':common/options';
import type { Result } from ':common/result';
import { isOk } from ':common/result';

type Options = {
promptTimer: number;
numRounds: number;
autoNumRounds: boolean;
promptSkipping: boolean;
sikeDispute: boolean;
sikeRetries: number;
packs: Record<string, boolean>;
customPrompts: string[];
};
type Scene =
| 'lobby'
| 'countdown'
Expand All @@ -24,56 +27,9 @@ type Scene =
| 'endRound'
| 'endGame';

type PollName = 'skipPrompt' | 'sikeDispute' | 'startNextRound';
export type Match = Omit<ServerMatch, 'player'> & { player: Player };

type ServerMatch = {
player: string;
response: string;
exact: boolean;
};

type MidgameConnectData = {
id: string;
stage: string;
selectionType: string;
responses: Responses;
selector: string;
selectedResponse: string;
prompt: string;
options: Options;
timer: number;
matches: ServerMatch[];
voteCounts: Record<PollName, { count: number; next: boolean }>;
};

type Player = {
id: string;
name: string;
leader: boolean;
active: boolean;
};
type Responses = {
all: string[];
used: string[];
selectedStrike: string;
selectedSike: string;
};

type Match = {
player: Player;
response: string;
exact: boolean;
};

type Score = {
player: Player;
points: number;
};

type VoteCount = {
count: number;
next: boolean;
};
type Score = Omit<ServerScore, 'player'> & { player: Player };

interface State {
scene: Scene;
Expand Down Expand Up @@ -122,16 +78,7 @@ export const useGameStore = defineStore('game', {
startNextRound: { count: 0, next: false },
sikeDispute: { count: 0, next: false }
},
options: {
promptTimer: 35,
numRounds: 0,
autoNumRounds: true,
sikeDispute: true,
promptSkipping: true,
sikeRetries: 0,
packs: {},
customPrompts: []
},
options: defaultOptions,
firstSelection: true,
hasNextRound: true,
unmatched: false
Expand Down Expand Up @@ -201,6 +148,7 @@ export const useGameStore = defineStore('game', {
const selfId = room.self!.id;
this.responses = {};
this.responses[selfId] = {
id: selfId,
all: [],
used: [],
selectedStrike: '',
Expand Down Expand Up @@ -277,9 +225,9 @@ export const useGameStore = defineStore('game', {
resolve();
return;
}
socket.emit('getResponses', id, (data: { success: boolean; responses: Responses }) => {
if (data.success) {
this.responses[id] = data.responses;
socket.emit('getResponses', id, (data: Result<Responses>) => {
if (isOk(data)) {
this.responses[id] = data;
resolve();
} else {
reject();
Expand All @@ -288,8 +236,8 @@ export const useGameStore = defineStore('game', {
});
},
bindEvents() {
socket.on('setOptions', (options: Options) => {
this.options = options;
socket.on('setOptions', (options: Partial<VisibleOptions>) => {
this.options = { ...this.options, ...options };
});

socket.on('selectionTypeChosen', (selectionType: string) => {
Expand Down Expand Up @@ -378,7 +326,7 @@ export const useGameStore = defineStore('game', {
if (data.selectionType === 'choice') {
this.selectionTypeChoice = true;
}
this.options = data.options;
this.options = { ...this.options, ...data.options };
this.responses[data.id] = data.responses;
this.selector = room.players.find((player) => player.id === data.selector);
this.selectedResponse = data.selectedResponse;
Expand Down
8 changes: 1 addition & 7 deletions client/src/stores/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@
import router from '@/router/index.js';
import socket from '@/socket/socket.js';
import { defineStore } from 'pinia';

type Player = {
id: string;
name: string;
leader: boolean;
active: boolean;
};
import type { Player } from ':common/stateTypes';

interface State {
players: Player[];
Expand Down
2 changes: 1 addition & 1 deletion client/src/types/shims-vue.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// noinspection JSUnusedGlobalSymbols

declare module '*.vue' {
import { defineComponent } from 'vue';
import type { defineComponent } from 'vue';
const Component: ReturnType<typeof defineComponent>;
export default Component;
}
Loading

0 comments on commit 50191fc

Please sign in to comment.