Skip to content

Commit

Permalink
refactor(webapp): wrap action bar with redux store to get ui state
Browse files Browse the repository at this point in the history
  • Loading branch information
ben196888 committed Jul 21, 2024
1 parent 29d06ec commit 3a0b5a7
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 46 deletions.
86 changes: 86 additions & 0 deletions packages/webapp/src/components/ActionBoard/ActionBar.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ActionMoveName } from '@/game/core/stage/action/move/type';
import { GameContext } from '../GameContextHelpers';
import { GameState } from '@/game/store/store';
import { RuleSelector } from '@/game/store/slice/rule';
import { ActionSlotSelector } from '@/game/store/slice/actionSlot';
import { createSelector } from '@reduxjs/toolkit';
import { getSelectedProjectSlots } from '@/lib/reducers/projectSlotSlice';
import { getSelectedJobSlots } from '@/lib/reducers/jobSlotSlice';
import { getSelectedHandProjectCards } from '@/lib/reducers/handProjectCardSlice';

interface StateProps {
selectedHandProjectCards: string[];
selectedJobSlots: string[];
selectedProjectSlots: string[];
}

export const mapStateToProps = createSelector(
getSelectedHandProjectCards,
getSelectedJobSlots,
getSelectedProjectSlots,
(handProjectCards, jobSlots, projectSlots) => {
const selectedHandProjectCards = Object.keys(handProjectCards).filter(cardId => handProjectCards[cardId]);
const selectedJobSlots = Object.keys(jobSlots).filter(slotId => jobSlots[slotId]);
const selectedProjectSlots = Object.keys(projectSlots).filter(slotId => projectSlots[slotId]);

return {
selectedHandProjectCards,
selectedJobSlots,
selectedProjectSlots,
};
}
);

export enum ActionMoveState {
Available = 'available',
Occupied = 'occupied',
Disabled = 'disabled'
}

export enum UserActionMoves {
CreateProject = 'createProject',
Recruit = 'recruit',
ContributeOwnedProjects = 'contributeOwnedProjects',
ContributeJoinedProjects = 'contributeJoinedProjects',
RemoveAndRefillJobs = 'removeAndRefillJobs',
Mirror = 'mirror',
EndActionTurn = 'endActionTurn'
}

const getActionMoveState = (state: GameState, actionMove: ActionMoveName): ActionMoveState => {
if (!RuleSelector.isActionSlotAvailable(state.rules, actionMove)) {
return ActionMoveState.Disabled;
}
if (ActionSlotSelector.isOccupied(state.table.actionSlots[actionMove])) {
return ActionMoveState.Occupied;
}
return ActionMoveState.Available;
};

export const mapGameContextToProps = (gameContext: GameContext, stateProps: StateProps) => {
const { G, events } = gameContext;

const actionsState: Record<UserActionMoves, ActionMoveState> = {
[UserActionMoves.CreateProject]: getActionMoveState(G, UserActionMoves.CreateProject),
[UserActionMoves.Recruit]: getActionMoveState(G, UserActionMoves.Recruit),
[UserActionMoves.ContributeOwnedProjects]: getActionMoveState(G, UserActionMoves.ContributeOwnedProjects),
[UserActionMoves.ContributeJoinedProjects]: getActionMoveState(G, UserActionMoves.ContributeJoinedProjects),
[UserActionMoves.RemoveAndRefillJobs]: getActionMoveState(G, UserActionMoves.RemoveAndRefillJobs),
[UserActionMoves.Mirror]: getActionMoveState(G, UserActionMoves.Mirror),
[UserActionMoves.EndActionTurn]: ActionMoveState.Available,
};

const onEndActionTurn = () => {
events.endTurn!();
};

const onActionClick = (action: UserActionMoves) => {
switch (action) {
case UserActionMoves.EndActionTurn:
onEndActionTurn();
break;
}
};

return { actionsState, onActionClick };
};
49 changes: 7 additions & 42 deletions packages/webapp/src/components/ActionBoard/ActionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import React from 'react';
import { Box, Button, Grid } from '@mui/material';
import { styled } from '@mui/material/styles';
import { ActionMoveName } from '@/game/core/stage/action/move/type';
import { GameContext } from '../GameContextHelpers';
import { GameState } from '@/game/store/store';
import { connectGameContext } from '../GameContextHelpers';
import { RuleSelector } from '@/game/store/slice/rule';
import { ActionSlotSelector } from '@/game/store/slice/actionSlot';

enum ActionMoveState {
Available = 'available',
Occupied = 'occupied',
Disabled = 'disabled'
}
import { connect } from 'react-redux';
import { UserActionMoves, ActionMoveState, mapStateToProps, mapGameContextToProps } from './ActionBar.selectors';

type ActionBarProps = {
actionsState: Record<ActionMoveName | 'endActionTurn', ActionMoveState>;
onActionClick: (action: string) => void;
actionsState: Record<UserActionMoves, ActionMoveState>;
onActionClick: (action: UserActionMoves) => void;
};

const StyledButton = styled(Button)(({ theme }) => ({
Expand Down Expand Up @@ -55,7 +46,7 @@ const ActionBar: React.FC<ActionBarProps> = ({ actionsState, onActionClick }) =>
<Box sx={{ display: 'flex', justifyContent: 'center', padding: '8px', backgroundColor: '#f0f0f0' }}>
<Grid container spacing={1} justifyContent="center">
{Object.entries(actionsState).map(([action, state]) => (
action === 'endActionTurn' ? (
action === UserActionMoves.EndActionTurn ? (
<EndActionButton
key={action}
onClick={() => onActionClick(action)}
Expand All @@ -66,7 +57,7 @@ const ActionBar: React.FC<ActionBarProps> = ({ actionsState, onActionClick }) =>
<StyledButton
key={action}
className={state}
onClick={() => state === ActionMoveState.Available && onActionClick(action)}
onClick={() => state === ActionMoveState.Available && onActionClick(action as UserActionMoves)}
disabled={state === ActionMoveState.Disabled}
>
{action.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
Expand All @@ -78,30 +69,4 @@ const ActionBar: React.FC<ActionBarProps> = ({ actionsState, onActionClick }) =>
);
};

const getActionMoveState = (state: GameState, actionMove: ActionMoveName): ActionMoveState => {
if (!RuleSelector.isActionSlotAvailable(state.rules, actionMove)) {
return ActionMoveState.Disabled;
}
if (ActionSlotSelector.isOccupied(state.table.actionSlots[actionMove])) {
return ActionMoveState.Occupied;
}
return ActionMoveState.Available;
};

const mapGameContextToProps = ({ G }: GameContext) => {
const actionsState: Record<ActionMoveName | 'endActionTurn', ActionMoveState> = {
createProject: getActionMoveState(G, 'createProject'),
recruit: getActionMoveState(G, 'recruit'),
contributeOwnedProjects: getActionMoveState(G, 'contributeOwnedProjects'),
contributeJoinedProjects: getActionMoveState(G, 'contributeJoinedProjects'),
removeAndRefillJobs: getActionMoveState(G, 'removeAndRefillJobs'),
mirror: getActionMoveState(G, 'mirror'),
endActionTurn: ActionMoveState.Available,
};

const onActionClick = (action: string) => {};

return { actionsState, onActionClick };
};

export default connectGameContext(mapGameContextToProps)(ActionBar);
export default connect(mapStateToProps)(connectGameContext(mapGameContextToProps)(ActionBar));
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { BoardProps } from 'boardgame.io/react';

export type GameContext = BoardProps<GameState>;
export type GameContextComponentProps = { gameContext: GameContext; };
export type MapGameContextToProps<T> = (context: GameContext) => T;
export type MapGameContextToProps<TProps, TOwnProps> = (context: GameContext, ownProps: TOwnProps) => TProps;

export const connectGameContext = <GameContextProps extends {} = {}, OwnProps extends {} = {}>(mapGameContextToProps: MapGameContextToProps<GameContextProps>) => (Component: React.FC<GameContextProps>) => {
const GameContextComponent: React.FC<GameContextComponentProps & OwnProps> = ({ gameContext, ...restProps }) => {
const props = mapGameContextToProps(gameContext);
export const connectGameContext = <GameContextProps extends {} = {}, TOwnProps extends {} = {}>(mapGameContextToProps: MapGameContextToProps<GameContextProps, TOwnProps>) => (Component: React.FC<GameContextProps>) => {
const GameContextComponent: React.FC<GameContextComponentProps & TOwnProps> = ({ gameContext, ...restProps }) => {
const ownProps = restProps as unknown as TOwnProps;
const props = mapGameContextToProps(gameContext, ownProps);
return <Component {...props} {...restProps} />;
};

Expand Down

0 comments on commit 3a0b5a7

Please sign in to comment.