Skip to content

Commit

Permalink
Optimize client side projection, just store current the question
Browse files Browse the repository at this point in the history
  • Loading branch information
stoerti committed Aug 16, 2024
1 parent 6b2f0f0 commit 23217e0
Show file tree
Hide file tree
Showing 19 changed files with 227 additions and 274 deletions.
2 changes: 1 addition & 1 deletion e2e/test/multiplayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Scenario('multiplayer_moderated', ({I, loginPage, lobbyPage, gameRoomPage}) => {
I.waitForText("Banone", 5, "tr:has-text('"+username2+"')")
I.waitForText("Gürkin", 5, "tr:has-text('"+username3+"')")

I.wait(10000)
I.wait(3)

gameRoomPage.nextQuestion()

Expand Down
23 changes: 4 additions & 19 deletions frontend/src/domain/GameModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class Game {
readonly moderator: string | undefined;
readonly status: GameStatus;
readonly players: Player[];
readonly questions: GameQuestion[];
readonly currentQuestion: GameQuestion | undefined;

constructor(event: GameCreatedEvent) {
this.id = event.gameId
Expand All @@ -46,7 +46,6 @@ export class Game {
this.moderator = event.moderatorUsername
this.status = GameStatus.CREATED
this.players = []
this.questions = []
}

public copyWith(modifyObject: { [P in keyof Game]?: Game[P] }): Game {
Expand Down Expand Up @@ -119,21 +118,15 @@ export class Game {

public onQuestionAsked(event: QuestionAskedEvent): Game {
return this.copyWith({
questions: [
...this.questions,
currentQuestion:
new GameQuestion(event)
]
})
}

private updateQuestion(questionId: string, questionUpdater: (question: GameQuestion) => GameQuestion): Game {
const i = this.questions.findIndex(q => q.gameQuestionId == questionId)
if (i !== -1) {
const questionsCopy = [...this.questions]
questionsCopy[i] = questionUpdater(this.questions[i])

if (this.currentQuestion === undefined || this.currentQuestion.gameQuestionId === questionId) {
return this.copyWith({
questions: questionsCopy
currentQuestion: questionUpdater(this.currentQuestion!)
})
} else {
return this
Expand Down Expand Up @@ -187,14 +180,6 @@ export class Game {
public findPlayerPoints(gamePlayerId: string): number {
return this.players.find(player => player.id === gamePlayerId)?.points ?? 0
}

public findLastQuestion(): GameQuestion | undefined {
if (this.questions.length === 0)
return undefined

const latestQuestionNumber = Math.max(...this.questions.map((question) => question.gameQuestionNumber))
return this.questions.find((q) => q.gameQuestionNumber === latestQuestionNumber)
}
}

export class GameQuestion {
Expand Down
19 changes: 8 additions & 11 deletions frontend/src/domain/__tests__/GameModel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,9 @@ describe('testing game read model', () => {
game = game.onQuestionAsked(questionAsked("question2", 2, "Foo2", "Bar2"))
game = game.onQuestionAsked(questionAsked("question3", 3, "Foo3", "Bar3"))

expect(game.questions.length).toBe(3);

const question = game.questions.find(q => q.gameQuestionId == "question3")
expect(question).toBeDefined()
expect(question!.question.phrase).toBe("Foo3");
expect(game.currentQuestion).toBeDefined();
expect(game.currentQuestion?.gameQuestionId).toBe("question3");
expect(game.currentQuestion?.question.phrase).toBe("Foo3");
});

test('question can be answered', () => {
Expand All @@ -78,12 +76,11 @@ describe('testing game read model', () => {

game = game.onQuestionAnswered(questionAnswered("question1", "player2", "answer01_02", "bla"))

expect(game.questions.length).toBe(1);
expect(game.questions[0].answers.length).toBe(1);
expect(game.currentQuestion).toBeDefined();
expect(game.currentQuestion?.answers.length).toBe(1);

const question = game.questions.find(q => q.gameQuestionId == "question1")
expect(question!.answers).toHaveLength(1)
expect(question!.answers[0].answer).toBe("bla");
expect(game.currentQuestion!.answers).toHaveLength(1)
expect(game.currentQuestion!.answers[0].answer).toBe("bla");
});

test('question can be rated', () => {
Expand All @@ -100,7 +97,7 @@ describe('testing game read model', () => {
}
));

expect(game.questions[0].answers.length).toBe(2);
expect(game.currentQuestion?.answers.length).toBe(2);
expect(game.findPlayerPoints("player2")).toBe(10)
expect(game.findPlayerPoints("player3")).toBe(0)
});
Expand Down
15 changes: 2 additions & 13 deletions frontend/src/pages/GameCreationDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import {
Box,
Button,
Dialog,
DialogTitle,
FormControlLabel,
FormGroup,
MenuItem,
Select,
Switch,
TextField
} from "@mui/material";
import {Box, Button, Dialog, DialogTitle, FormControlLabel, FormGroup, MenuItem, Select, Switch, TextField} from "@mui/material";
import React, {useEffect} from "react";
import {NewGameCommand} from "../services/GameCommandService";
import {QuestionSetDto} from "../services/QuestionSetServiceTypes";
import {questionSetService, QuestionSetService} from "../services/QuestionSetService";
import {questionSetService} from "../services/QuestionSetService";

type GameCreationDialogProps = {
open: boolean
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/GameSelectionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const GameSelectionPage = (props: GameSelectionPageProps) => {
}
}
const onButtonClickJoinGameAsSpectator = async (gameId: string) => {
props.onGameSelected(gameId)
props.onGameSelected(gameId)
}

const onCloseNewGameDialog = () => {
Expand Down
90 changes: 45 additions & 45 deletions frontend/src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,56 @@ import React from "react";


type LoginPageProps = {
loginSuccessAction: (username: string) => void
loginSuccessAction: (username: string) => void
}
const LoginPage = ({loginSuccessAction}: LoginPageProps) => {
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);

const username: string = data.get('username')!.toString()
const username: string = data.get('username')!.toString()

console.log({
username: username,
});
loginSuccessAction(username)
};
console.log({
username: username,
});
loginSuccessAction(username)
};

return (
<Grid container component="main" sx={{height: '100vh', justifyContent: "center"}}>
<CssBaseline/>
<Paper elevation={8} sx={{
maxWidth: 400,
alignSelf: "center"
}}>
<Box component="form" noValidate onSubmit={handleSubmit} sx={{
my: 4,
mx: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<TextField margin="normal"
required
fullWidth
id="username"
label="Username"
name="username"
autoComplete="username"
autoFocus
sx={{margin: 2}}
/>
<Button
id="submitLogin"
type="submit"
fullWidth
variant="contained"
sx={{ml: 2, mr: 2, mb: 2}}
>Sign In</Button>
</Box>
</Paper>
</Grid>
)
return (
<Grid container component="main" sx={{height: '100vh', justifyContent: "center"}}>
<CssBaseline/>
<Paper elevation={8} sx={{
maxWidth: 400,
alignSelf: "center"
}}>
<Box component="form" noValidate onSubmit={handleSubmit} sx={{
my: 4,
mx: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<TextField margin="normal"
required
fullWidth
id="username"
label="Username"
name="username"
autoComplete="username"
autoFocus
sx={{margin: 2}}
/>
<Button
id="submitLogin"
type="submit"
fullWidth
variant="contained"
sx={{ml: 2, mr: 2, mb: 2}}
>Sign In</Button>
</Box>
</Paper>
</Grid>
)
}

export default LoginPage
98 changes: 49 additions & 49 deletions frontend/src/pages/QuizmaniaMainUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,67 @@ import GameSelectionPage from "./GameSelectionPage";
import GamePage from "./game/GamePage";

enum MainPageState {
LOGIN,
GAME_SELECTION,
IN_GAME
LOGIN,
GAME_SELECTION,
IN_GAME
}

const QuizmaniaMainUI = () => {
const [username, setUsername] = useState<string | undefined>(undefined);
const [gameId, setGameId] = useState<string | undefined>(undefined);
const [mainPageState, setMainPageState] = useState(MainPageState.LOGIN)
const [username, setUsername] = useState<string | undefined>(undefined);
const [gameId, setGameId] = useState<string | undefined>(undefined);
const [mainPageState, setMainPageState] = useState(MainPageState.LOGIN)

const snackbar = useSnackbar()
const snackbar = useSnackbar()

if (mainPageState === MainPageState.LOGIN
&& Cookies.get('username') && username === undefined) {
setUsername(Cookies.get('username'))
if (mainPageState === MainPageState.LOGIN
&& Cookies.get('username') && username === undefined) {
setUsername(Cookies.get('username'))

if (Cookies.get('gameId') && gameId === undefined) {
setGameId(Cookies.get('gameId'))
setMainPageState(MainPageState.IN_GAME)
} else {
setMainPageState(MainPageState.GAME_SELECTION)
}
if (Cookies.get('gameId') && gameId === undefined) {
setGameId(Cookies.get('gameId'))
setMainPageState(MainPageState.IN_GAME)
} else {
setMainPageState(MainPageState.GAME_SELECTION)
}
}

const onLoginSuccess = useCallback((username: string) => {
Cookies.set('username', username, {expires: 1});
setUsername(username)
setMainPageState(MainPageState.GAME_SELECTION)
snackbar.showMessage(
'Username: ' + username
)
}, [snackbar])
const onLoginSuccess = useCallback((username: string) => {
Cookies.set('username', username, {expires: 1});
setUsername(username)
setMainPageState(MainPageState.GAME_SELECTION)
snackbar.showMessage(
'Username: ' + username
)
}, [snackbar])

const onLogout = useCallback(() => {
Cookies.remove('username');
setUsername(undefined)
setMainPageState(MainPageState.LOGIN)
}, [])
const onLogout = useCallback(() => {
Cookies.remove('username');
setUsername(undefined)
setMainPageState(MainPageState.LOGIN)
}, [])

const onGameSelected = useCallback((gameId: string) => {
setGameId(gameId)
setMainPageState(MainPageState.IN_GAME)
Cookies.set('gameId', gameId, {expires: 1});
}, [])
const onGameSelected = useCallback((gameId: string) => {
setGameId(gameId)
setMainPageState(MainPageState.IN_GAME)
Cookies.set('gameId', gameId, {expires: 1});
}, [])

const onGameEnded = useCallback(() => {
setGameId(undefined)
setMainPageState(MainPageState.GAME_SELECTION)
Cookies.remove('gameId');
},[])
const onGameEnded = useCallback(() => {
setGameId(undefined)
setMainPageState(MainPageState.GAME_SELECTION)
Cookies.remove('gameId');
}, [])

if (mainPageState === MainPageState.LOGIN) {
return <LoginPage loginSuccessAction={onLoginSuccess}/>
}
if (mainPageState === MainPageState.GAME_SELECTION) {
return <GameSelectionPage onGameSelected={onGameSelected} onLogout={onLogout}/>
}
if (mainPageState === MainPageState.IN_GAME) {
return <GamePage gameId={gameId!} onGameEnded={onGameEnded}/>
}
return <div>Unknown state {mainPageState}</div>
if (mainPageState === MainPageState.LOGIN) {
return <LoginPage loginSuccessAction={onLoginSuccess}/>
}
if (mainPageState === MainPageState.GAME_SELECTION) {
return <GameSelectionPage onGameSelected={onGameSelected} onLogout={onLogout}/>
}
if (mainPageState === MainPageState.IN_GAME) {
return <GamePage gameId={gameId!} onGameEnded={onGameEnded}/>
}
return <div>Unknown state {mainPageState}</div>
}

export default QuizmaniaMainUI;
14 changes: 1 addition & 13 deletions frontend/src/pages/game/GameFinished.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import {Game} from "../../domain/GameModel";
import {GameCommandService} from "../../services/GameCommandService";
import React from "react";
import {
AppBar,
Box,
Button, Stack,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Toolbar,
Typography
} from "@mui/material";
import {AppBar, Box, Button, Stack, Table, TableBody, TableCell, TableHead, TableRow, Toolbar, Typography} from "@mui/material";
import Logout from "@mui/icons-material/Logout";
import EmojiEvents from "@mui/icons-material/EmojiEvents";
import {amber, brown, grey} from "@mui/material/colors";
Expand Down
Loading

0 comments on commit 23217e0

Please sign in to comment.