upgrade packages + improve typing

This commit is contained in:
seavor 2026-04-14 11:34:29 -05:00
parent fd55f4fb7f
commit 19f5eefdd2
138 changed files with 4504 additions and 11015 deletions

View file

@ -1,8 +1,14 @@
import { ArrowInfo, CardInfo, CounterInfo, PlayerProperties } from 'types';
import { ArrowInfo, CardInfo, CounterInfo, PlayerProperties, ProtoInit } from 'types';
import { create } from '@bufbuild/protobuf';
import { ServerInfo_CardSchema } from 'generated/proto/serverinfo_card_pb';
import { ServerInfo_CounterSchema } from 'generated/proto/serverinfo_counter_pb';
import { colorSchema } from 'generated/proto/color_pb';
import { ServerInfo_ArrowSchema } from 'generated/proto/serverinfo_arrow_pb';
import { ServerInfo_PlayerPropertiesSchema } from 'generated/proto/serverinfo_playerproperties_pb';
import { GameEntry, GamesState, PlayerEntry, ZoneEntry } from '../game.interfaces';
export function makeCard(overrides: Partial<CardInfo> = {}): CardInfo {
return {
export function makeCard(overrides: ProtoInit<CardInfo> = {}): CardInfo {
return create(ServerInfo_CardSchema, {
id: 1,
name: 'Test Card',
x: 0,
@ -21,22 +27,22 @@ export function makeCard(overrides: Partial<CardInfo> = {}): CardInfo {
attachCardId: -1,
providerId: '',
...overrides,
};
});
}
export function makeCounter(overrides: Partial<CounterInfo> = {}): CounterInfo {
return {
export function makeCounter(overrides: ProtoInit<CounterInfo> = {}): CounterInfo {
return create(ServerInfo_CounterSchema, {
id: 1,
name: 'Life',
counterColor: { r: 0, g: 0, b: 0, a: 255 },
counterColor: create(colorSchema, { r: 0, g: 0, b: 0, a: 255 }),
radius: 1,
count: 20,
...overrides,
};
});
}
export function makeArrow(overrides: Partial<ArrowInfo> = {}): ArrowInfo {
return {
export function makeArrow(overrides: ProtoInit<ArrowInfo> = {}): ArrowInfo {
return create(ServerInfo_ArrowSchema, {
id: 1,
startPlayerId: 1,
startZone: 'table',
@ -44,9 +50,9 @@ export function makeArrow(overrides: Partial<ArrowInfo> = {}): ArrowInfo {
targetPlayerId: 1,
targetZone: 'table',
targetCardId: 2,
arrowColor: { r: 255, g: 0, b: 0, a: 255 },
arrowColor: create(colorSchema, { r: 255, g: 0, b: 0, a: 255 }),
...overrides,
};
});
}
export function makeZoneEntry(overrides: Partial<ZoneEntry> = {}): ZoneEntry {
@ -62,10 +68,9 @@ export function makeZoneEntry(overrides: Partial<ZoneEntry> = {}): ZoneEntry {
};
}
export function makePlayerProperties(overrides: Partial<PlayerProperties> = {}): PlayerProperties {
return {
export function makePlayerProperties(overrides: ProtoInit<PlayerProperties> = {}): PlayerProperties {
return create(ServerInfo_PlayerPropertiesSchema, {
playerId: 1,
userInfo: null,
spectator: false,
conceded: false,
readyStart: false,
@ -74,7 +79,7 @@ export function makePlayerProperties(overrides: Partial<PlayerProperties> = {}):
sideboardLocked: false,
judge: false,
...overrides,
};
});
}
export function makePlayerEntry(overrides: Partial<PlayerEntry> = {}): PlayerEntry {

View file

@ -1,3 +1,4 @@
import { create } from '@bufbuild/protobuf';
import { Actions } from './game.actions';
import { Types } from './game.types';
import {
@ -6,8 +7,26 @@ import {
makeCounter,
makeGameEntry,
makePlayerProperties,
makeZoneEntry,
} from './__mocks__/fixtures';
import { Event_GameStateChangedSchema } from 'generated/proto/event_game_state_changed_pb';
import { Event_MoveCardSchema } from 'generated/proto/event_move_card_pb';
import { Event_FlipCardSchema } from 'generated/proto/event_flip_card_pb';
import { Event_DestroyCardSchema } from 'generated/proto/event_destroy_card_pb';
import { Event_AttachCardSchema } from 'generated/proto/event_attach_card_pb';
import { Event_CreateTokenSchema } from 'generated/proto/event_create_token_pb';
import { Event_SetCardAttrSchema } from 'generated/proto/event_set_card_attr_pb';
import { Event_SetCardCounterSchema } from 'generated/proto/event_set_card_counter_pb';
import { Event_CreateArrowSchema } from 'generated/proto/event_create_arrow_pb';
import { Event_DeleteArrowSchema } from 'generated/proto/event_delete_arrow_pb';
import { Event_CreateCounterSchema } from 'generated/proto/event_create_counter_pb';
import { Event_SetCounterSchema } from 'generated/proto/event_set_counter_pb';
import { Event_DelCounterSchema } from 'generated/proto/event_del_counter_pb';
import { Event_DrawCardsSchema } from 'generated/proto/event_draw_cards_pb';
import { Event_RevealCardsSchema } from 'generated/proto/event_reveal_cards_pb';
import { Event_ShuffleSchema } from 'generated/proto/event_shuffle_pb';
import { Event_RollDieSchema } from 'generated/proto/event_roll_die_pb';
import { Event_DumpZoneSchema } from 'generated/proto/event_dump_zone_pb';
import { Event_ChangeZonePropertiesSchema } from 'generated/proto/event_change_zone_properties_pb';
describe('Actions', () => {
it('clearStore', () => {
@ -32,7 +51,9 @@ describe('Actions', () => {
});
it('gameStateChanged', () => {
const data = { playerList: [], gameStarted: true, activePlayerId: 1, activePhase: 0, secondsElapsed: 0 };
const data = create(Event_GameStateChangedSchema, {
playerList: [], gameStarted: true, activePlayerId: 1, activePhase: 0, secondsElapsed: 0
});
expect(Actions.gameStateChanged(1, data)).toEqual({ type: Types.GAME_STATE_CHANGED, gameId: 1, data });
});
@ -60,85 +81,85 @@ describe('Actions', () => {
});
it('cardMoved', () => {
const data = { cardId: 1 } as any;
const data = create(Event_MoveCardSchema, { cardId: 1 });
expect(Actions.cardMoved(1, 2, data)).toEqual({ type: Types.CARD_MOVED, gameId: 1, playerId: 2, data });
});
it('cardFlipped', () => {
const data = { cardId: 1 } as any;
const data = create(Event_FlipCardSchema, { cardId: 1 });
expect(Actions.cardFlipped(1, 2, data)).toEqual({ type: Types.CARD_FLIPPED, gameId: 1, playerId: 2, data });
});
it('cardDestroyed', () => {
const data = { cardId: 1 } as any;
const data = create(Event_DestroyCardSchema, { cardId: 1 });
expect(Actions.cardDestroyed(1, 2, data)).toEqual({ type: Types.CARD_DESTROYED, gameId: 1, playerId: 2, data });
});
it('cardAttached', () => {
const data = { cardId: 1 } as any;
const data = create(Event_AttachCardSchema, { cardId: 1 });
expect(Actions.cardAttached(1, 2, data)).toEqual({ type: Types.CARD_ATTACHED, gameId: 1, playerId: 2, data });
});
it('tokenCreated', () => {
const data = { cardId: 1 } as any;
const data = create(Event_CreateTokenSchema, { cardId: 1 });
expect(Actions.tokenCreated(1, 2, data)).toEqual({ type: Types.TOKEN_CREATED, gameId: 1, playerId: 2, data });
});
it('cardAttrChanged', () => {
const data = { cardId: 1 } as any;
const data = create(Event_SetCardAttrSchema, { cardId: 1 });
expect(Actions.cardAttrChanged(1, 2, data)).toEqual({ type: Types.CARD_ATTR_CHANGED, gameId: 1, playerId: 2, data });
});
it('cardCounterChanged', () => {
const data = { cardId: 1 } as any;
const data = create(Event_SetCardCounterSchema, { cardId: 1 });
expect(Actions.cardCounterChanged(1, 2, data)).toEqual({ type: Types.CARD_COUNTER_CHANGED, gameId: 1, playerId: 2, data });
});
it('arrowCreated', () => {
const arrow = makeArrow();
const data = { arrowInfo: arrow };
const data = create(Event_CreateArrowSchema, { arrowInfo: arrow });
expect(Actions.arrowCreated(1, 2, data)).toEqual({ type: Types.ARROW_CREATED, gameId: 1, playerId: 2, data });
});
it('arrowDeleted', () => {
const data = { arrowId: 3 };
const data = create(Event_DeleteArrowSchema, { arrowId: 3 });
expect(Actions.arrowDeleted(1, 2, data)).toEqual({ type: Types.ARROW_DELETED, gameId: 1, playerId: 2, data });
});
it('counterCreated', () => {
const counter = makeCounter();
const data = { counterInfo: counter };
const data = create(Event_CreateCounterSchema, { counterInfo: counter });
expect(Actions.counterCreated(1, 2, data)).toEqual({ type: Types.COUNTER_CREATED, gameId: 1, playerId: 2, data });
});
it('counterSet', () => {
const data = { counterId: 1, value: 10 };
const data = create(Event_SetCounterSchema, { counterId: 1, value: 10 });
expect(Actions.counterSet(1, 2, data)).toEqual({ type: Types.COUNTER_SET, gameId: 1, playerId: 2, data });
});
it('counterDeleted', () => {
const data = { counterId: 1 };
const data = create(Event_DelCounterSchema, { counterId: 1 });
expect(Actions.counterDeleted(1, 2, data)).toEqual({ type: Types.COUNTER_DELETED, gameId: 1, playerId: 2, data });
});
it('cardsDrawn', () => {
const card = makeCard();
const data = { number: 2, cards: [card] };
const data = create(Event_DrawCardsSchema, { number: 2, cards: [card] });
expect(Actions.cardsDrawn(1, 2, data)).toEqual({ type: Types.CARDS_DRAWN, gameId: 1, playerId: 2, data });
});
it('cardsRevealed', () => {
const data = { zoneName: 'hand', cards: [] } as any;
const data = create(Event_RevealCardsSchema, { zoneName: 'hand', cards: [] });
expect(Actions.cardsRevealed(1, 2, data)).toEqual({ type: Types.CARDS_REVEALED, gameId: 1, playerId: 2, data });
});
it('zoneShuffled', () => {
const data = { zoneName: 'deck', start: 0, end: 39 };
const data = create(Event_ShuffleSchema, { zoneName: 'deck', start: 0, end: 39 });
expect(Actions.zoneShuffled(1, 2, data)).toEqual({ type: Types.ZONE_SHUFFLED, gameId: 1, playerId: 2, data });
});
it('dieRolled', () => {
const data = { sides: 6, value: 4, values: [4] };
const data = create(Event_RollDieSchema, { sides: 6, value: 4, values: [4] });
expect(Actions.dieRolled(1, 2, data)).toEqual({ type: Types.DIE_ROLLED, gameId: 1, playerId: 2, data });
});
@ -155,12 +176,12 @@ describe('Actions', () => {
});
it('zoneDumped', () => {
const data = { zoneOwnerId: 1, zoneName: 'hand', numberCards: 3, isReversed: false };
const data = create(Event_DumpZoneSchema, { zoneOwnerId: 1, zoneName: 'hand', numberCards: 3, isReversed: false });
expect(Actions.zoneDumped(1, 2, data)).toEqual({ type: Types.ZONE_DUMPED, gameId: 1, playerId: 2, data });
});
it('zonePropertiesChanged', () => {
const data = { zoneName: 'deck', alwaysRevealTopCard: true, alwaysLookAtTopCard: false };
const data = create(Event_ChangeZonePropertiesSchema, { zoneName: 'deck', alwaysRevealTopCard: true, alwaysLookAtTopCard: false });
expect(Actions.zonePropertiesChanged(1, 2, data)).toEqual({
type: Types.ZONE_PROPERTIES_CHANGED,
gameId: 1,

View file

@ -232,3 +232,5 @@ export const Actions = {
message,
}),
};
export type GameAction = ReturnType<typeof Actions[keyof typeof Actions]>;

View file

@ -1,5 +1,6 @@
vi.mock('store/store', () => ({ store: { dispatch: vi.fn() } }));
import { create } from '@bufbuild/protobuf';
import { store } from 'store/store';
import { Actions } from './game.actions';
import { Dispatch } from './game.dispatch';
@ -10,6 +11,25 @@ import {
makeGameEntry,
makePlayerProperties,
} from './__mocks__/fixtures';
import { Event_GameStateChangedSchema } from 'generated/proto/event_game_state_changed_pb';
import { Event_MoveCardSchema } from 'generated/proto/event_move_card_pb';
import { Event_FlipCardSchema } from 'generated/proto/event_flip_card_pb';
import { Event_DestroyCardSchema } from 'generated/proto/event_destroy_card_pb';
import { Event_AttachCardSchema } from 'generated/proto/event_attach_card_pb';
import { Event_CreateTokenSchema } from 'generated/proto/event_create_token_pb';
import { Event_SetCardAttrSchema } from 'generated/proto/event_set_card_attr_pb';
import { Event_SetCardCounterSchema } from 'generated/proto/event_set_card_counter_pb';
import { Event_CreateArrowSchema } from 'generated/proto/event_create_arrow_pb';
import { Event_DeleteArrowSchema } from 'generated/proto/event_delete_arrow_pb';
import { Event_CreateCounterSchema } from 'generated/proto/event_create_counter_pb';
import { Event_SetCounterSchema } from 'generated/proto/event_set_counter_pb';
import { Event_DelCounterSchema } from 'generated/proto/event_del_counter_pb';
import { Event_DrawCardsSchema } from 'generated/proto/event_draw_cards_pb';
import { Event_RevealCardsSchema } from 'generated/proto/event_reveal_cards_pb';
import { Event_ShuffleSchema } from 'generated/proto/event_shuffle_pb';
import { Event_RollDieSchema } from 'generated/proto/event_roll_die_pb';
import { Event_DumpZoneSchema } from 'generated/proto/event_dump_zone_pb';
import { Event_ChangeZonePropertiesSchema } from 'generated/proto/event_change_zone_properties_pb';
beforeEach(() => vi.clearAllMocks());
@ -41,7 +61,9 @@ describe('Dispatch', () => {
});
it('gameStateChanged dispatches Actions.gameStateChanged()', () => {
const data = { playerList: [], gameStarted: false, activePlayerId: 0, activePhase: 0, secondsElapsed: 0 };
const data = create(Event_GameStateChangedSchema, {
playerList: [], gameStarted: false, activePlayerId: 0, activePhase: 0, secondsElapsed: 0
});
Dispatch.gameStateChanged(1, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.gameStateChanged(1, data));
});
@ -69,97 +91,97 @@ describe('Dispatch', () => {
});
it('cardMoved dispatches Actions.cardMoved()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_MoveCardSchema, { cardId: 1 });
Dispatch.cardMoved(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardMoved(1, 2, data));
});
it('cardFlipped dispatches Actions.cardFlipped()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_FlipCardSchema, { cardId: 1 });
Dispatch.cardFlipped(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardFlipped(1, 2, data));
});
it('cardDestroyed dispatches Actions.cardDestroyed()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_DestroyCardSchema, { cardId: 1 });
Dispatch.cardDestroyed(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardDestroyed(1, 2, data));
});
it('cardAttached dispatches Actions.cardAttached()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_AttachCardSchema, { cardId: 1 });
Dispatch.cardAttached(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardAttached(1, 2, data));
});
it('tokenCreated dispatches Actions.tokenCreated()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_CreateTokenSchema, { cardId: 1 });
Dispatch.tokenCreated(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.tokenCreated(1, 2, data));
});
it('cardAttrChanged dispatches Actions.cardAttrChanged()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_SetCardAttrSchema, { cardId: 1 });
Dispatch.cardAttrChanged(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardAttrChanged(1, 2, data));
});
it('cardCounterChanged dispatches Actions.cardCounterChanged()', () => {
const data = { cardId: 1 } as any;
const data = create(Event_SetCardCounterSchema, { cardId: 1 });
Dispatch.cardCounterChanged(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardCounterChanged(1, 2, data));
});
it('arrowCreated dispatches Actions.arrowCreated()', () => {
const data = { arrowInfo: makeArrow() };
const data = create(Event_CreateArrowSchema, { arrowInfo: makeArrow() });
Dispatch.arrowCreated(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.arrowCreated(1, 2, data));
});
it('arrowDeleted dispatches Actions.arrowDeleted()', () => {
const data = { arrowId: 3 };
const data = create(Event_DeleteArrowSchema, { arrowId: 3 });
Dispatch.arrowDeleted(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.arrowDeleted(1, 2, data));
});
it('counterCreated dispatches Actions.counterCreated()', () => {
const data = { counterInfo: makeCounter() };
const data = create(Event_CreateCounterSchema, { counterInfo: makeCounter() });
Dispatch.counterCreated(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.counterCreated(1, 2, data));
});
it('counterSet dispatches Actions.counterSet()', () => {
const data = { counterId: 1, value: 10 };
const data = create(Event_SetCounterSchema, { counterId: 1, value: 10 });
Dispatch.counterSet(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.counterSet(1, 2, data));
});
it('counterDeleted dispatches Actions.counterDeleted()', () => {
const data = { counterId: 1 };
const data = create(Event_DelCounterSchema, { counterId: 1 });
Dispatch.counterDeleted(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.counterDeleted(1, 2, data));
});
it('cardsDrawn dispatches Actions.cardsDrawn()', () => {
const data = { number: 2, cards: [makeCard()] };
const data = create(Event_DrawCardsSchema, { number: 2, cards: [makeCard()] });
Dispatch.cardsDrawn(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardsDrawn(1, 2, data));
});
it('cardsRevealed dispatches Actions.cardsRevealed()', () => {
const data = { zoneName: 'hand', cards: [] } as any;
const data = create(Event_RevealCardsSchema, { zoneName: 'hand', cards: [] });
Dispatch.cardsRevealed(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.cardsRevealed(1, 2, data));
});
it('zoneShuffled dispatches Actions.zoneShuffled()', () => {
const data = { zoneName: 'deck', start: 0, end: 39 };
const data = create(Event_ShuffleSchema, { zoneName: 'deck', start: 0, end: 39 });
Dispatch.zoneShuffled(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.zoneShuffled(1, 2, data));
});
it('dieRolled dispatches Actions.dieRolled()', () => {
const data = { sides: 6, value: 4, values: [4] };
const data = create(Event_RollDieSchema, { sides: 6, value: 4, values: [4] });
Dispatch.dieRolled(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.dieRolled(1, 2, data));
});
@ -180,13 +202,13 @@ describe('Dispatch', () => {
});
it('zoneDumped dispatches Actions.zoneDumped()', () => {
const data = { zoneOwnerId: 1, zoneName: 'hand', numberCards: 3, isReversed: false };
const data = create(Event_DumpZoneSchema, { zoneOwnerId: 1, zoneName: 'hand', numberCards: 3, isReversed: false });
Dispatch.zoneDumped(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.zoneDumped(1, 2, data));
});
it('zonePropertiesChanged dispatches Actions.zonePropertiesChanged()', () => {
const data = { zoneName: 'deck', alwaysRevealTopCard: true, alwaysLookAtTopCard: false };
const data = create(Event_ChangeZonePropertiesSchema, { zoneName: 'deck', alwaysRevealTopCard: true, alwaysLookAtTopCard: false });
Dispatch.zonePropertiesChanged(1, 2, data);
expect(store.dispatch).toHaveBeenCalledWith(Actions.zonePropertiesChanged(1, 2, data));
});

View file

@ -1,3 +1,4 @@
import { create } from '@bufbuild/protobuf';
import { CardAttribute, PlayerInfo } from 'types';
import { gamesReducer } from './game.reducer';
import { Types } from './game.types';
@ -11,6 +12,7 @@ import {
makeState,
makeZoneEntry,
} from './__mocks__/fixtures';
import { ServerInfo_PlayerSchema } from 'generated/proto/serverinfo_player_pb';
// ── 2A: Initialisation & lifecycle ───────────────────────────────────────────
@ -67,7 +69,7 @@ describe('2B: Game state & player management', () => {
const counter = makeCounter({ id: 2 });
const arrow = makeArrow({ id: 3 });
const playerList: PlayerInfo[] = [
{
create(ServerInfo_PlayerSchema, {
properties: makePlayerProperties({ playerId: 7 }),
deckList: 'some deck',
zoneList: [
@ -83,7 +85,7 @@ describe('2B: Game state & player management', () => {
],
counterList: [counter],
arrowList: [arrow],
},
}),
];
const result = gamesReducer(state, {
@ -620,7 +622,7 @@ describe('2F: CARD_COUNTER_CHANGED', () => {
playerId: 1,
data: { zoneName: 'table', cardId: 4, counterId: 1, counterValue: 3 },
});
expect(result.games[1].players[1].zones['table'].cards[0].counterList).toEqual([{ id: 1, value: 3 }]);
expect(result.games[1].players[1].zones['table'].cards[0].counterList).toEqual([expect.objectContaining({ id: 1, value: 3 })]);
});
it('updates existing counter value when counterId matches', () => {
@ -631,7 +633,7 @@ describe('2F: CARD_COUNTER_CHANGED', () => {
playerId: 1,
data: { zoneName: 'table', cardId: 4, counterId: 1, counterValue: 7 },
});
expect(result.games[1].players[1].zones['table'].cards[0].counterList).toEqual([{ id: 1, value: 7 }]);
expect(result.games[1].players[1].zones['table'].cards[0].counterList).toEqual([expect.objectContaining({ id: 1, value: 7 })]);
});
it('removes counter from counterList when counterValue ≤ 0', () => {

View file

@ -7,6 +7,10 @@ import {
PlayerInfo,
PlayerProperties,
} from 'types';
import { create } from '@bufbuild/protobuf';
import { ServerInfo_CardSchema } from 'generated/proto/serverinfo_card_pb';
import { ServerInfo_CardCounterSchema } from 'generated/proto/serverinfo_cardcounter_pb';
import { GameAction } from './game.actions';
import { GameEntry, GameMessage, GamesState, PlayerEntry, ZoneEntry } from './game.interfaces';
import { Types } from './game.types';
@ -120,7 +124,7 @@ function buildEmptyCard(
faceDown: boolean,
providerId: string
): CardInfo {
return {
return create(ServerInfo_CardSchema, {
id,
name,
x,
@ -138,7 +142,7 @@ function buildEmptyCard(
attachZone: '',
attachCardId: -1,
providerId,
};
});
}
// ── Initial state ─────────────────────────────────────────────────────────────
@ -149,7 +153,7 @@ const initialState: GamesState = {
// ── Reducer ───────────────────────────────────────────────────────────────────
export const gamesReducer = (state: GamesState = initialState, action: any): GamesState => {
export const gamesReducer = (state: GamesState = initialState, action: GameAction): GamesState => {
switch (action.type) {
case Types.CLEAR_STORE: {
return initialState;
@ -422,7 +426,7 @@ export const gamesReducer = (state: GamesState = initialState, action: any): Gam
return state;
}
const newCard: CardInfo = {
const newCard: CardInfo = create(ServerInfo_CardSchema, {
id: cardId,
name: cardName,
x,
@ -440,7 +444,7 @@ export const gamesReducer = (state: GamesState = initialState, action: any): Gam
attachZone: '',
attachCardId: -1,
providerId: cardProviderId,
};
});
return updateZone(state, gameId, playerId, zoneName, {
cards: [...zone.cards, newCard],
cardCount: zone.cardCount + 1,
@ -514,7 +518,7 @@ export const gamesReducer = (state: GamesState = initialState, action: any): Gam
newCounterList =
existing >= 0
? card.counterList.map(c => (c.id === counterId ? { ...c, value: counterValue } : c))
: [...card.counterList, { id: counterId, value: counterValue }];
: [...card.counterList, create(ServerInfo_CardCounterSchema, { id: counterId, value: counterValue })];
}
const updatedCards = [...zone.cards];

View file

@ -1,6 +1,5 @@
import { Selectors } from './game.selectors';
import {
makeGameEntry, makePlayerEntry, makePlayerProperties, makeState,
import { makeGameEntry, makePlayerEntry, makeState,
makeZoneEntry, makeCard, makeCounter, makeArrow,
} from './__mocks__/fixtures';
import { GamesState } from './game.interfaces';

View file

@ -1,9 +1,14 @@
import { createSelector } from '@reduxjs/toolkit';
import { CardInfo } from 'types';
import { GamesState, GameEntry, PlayerEntry, ZoneEntry } from './game.interfaces';
interface State {
games: GamesState;
}
const EMPTY_ARRAY: CardInfo[] = [];
const EMPTY_OBJECT = {} as Record<string, never>;
export const Selectors = {
getGames: ({ games }: State): { [gameId: number]: GameEntry } => games.games,
@ -41,13 +46,13 @@ export const Selectors = {
): ZoneEntry | undefined => games.games[gameId]?.players[playerId]?.zones[zoneName],
getCards: ({ games }: State, gameId: number, playerId: number, zoneName: string) =>
games.games[gameId]?.players[playerId]?.zones[zoneName]?.cards ?? [],
games.games[gameId]?.players[playerId]?.zones[zoneName]?.cards ?? EMPTY_ARRAY,
getCounters: ({ games }: State, gameId: number, playerId: number) =>
games.games[gameId]?.players[playerId]?.counters ?? {},
games.games[gameId]?.players[playerId]?.counters ?? EMPTY_OBJECT,
getArrows: ({ games }: State, gameId: number, playerId: number) =>
games.games[gameId]?.players[playerId]?.arrows ?? {},
games.games[gameId]?.players[playerId]?.arrows ?? EMPTY_OBJECT,
getActivePlayerId: ({ games }: State, gameId: number): number | undefined =>
games.games[gameId]?.activePlayerId,
@ -65,8 +70,10 @@ export const Selectors = {
games.games[gameId]?.reversed ?? false,
getMessages: ({ games }: State, gameId: number) =>
games.games[gameId]?.messages ?? [],
games.games[gameId]?.messages ?? EMPTY_ARRAY,
getActiveGameIds: ({ games }: State): number[] =>
Object.keys(games.games).map(Number),
getActiveGameIds: createSelector(
[({ games }: State) => games.games],
(games) => Object.keys(games).map(Number)
),
};

View file

@ -31,4 +31,4 @@ export const Types = {
ZONE_DUMPED: '[Games] Zone Dumped',
ZONE_PROPERTIES_CHANGED: '[Games] Zone Properties Changed',
GAME_SAY: '[Games] Game Say',
};
} as const;