Skip to content

Commit

Permalink
feat(app-load-balancing): update game handling for new connections logic
Browse files Browse the repository at this point in the history
  • Loading branch information
albertodigioacchino committed Jan 28, 2021
1 parent efc295e commit 56292ec
Show file tree
Hide file tree
Showing 19 changed files with 121 additions and 142 deletions.
4 changes: 3 additions & 1 deletion packages/game-app/src/_shared/models/Game.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Game as InnerGame } from '@pipeline/common';
import { Game as InnerGame, GameEntity as InnerGameEntity } from '@pipeline/common';
import { FirebaseFieldValue, FirebaseTimestamp } from './FirebaseTypes';

export type Game = InnerGame<FirebaseTimestamp, FirebaseFieldValue>;

export type GameEntity = InnerGameEntity<FirebaseTimestamp, FirebaseFieldValue>;
8 changes: 5 additions & 3 deletions packages/game-app/src/_shared/models/Status.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Status as InnerStatus } from '@pipeline/common';
import { FirebaseFieldValue, FirebaseTimestamp } from './FirebaseTypes';
import { FirebaseTimestamp } from './FirebaseTypes';

export type Status = InnerStatus<FirebaseTimestamp, FirebaseFieldValue>;
export type Status = {
state: 'online' | 'offline';
updatedAt: FirebaseTimestamp;
};
4 changes: 2 additions & 2 deletions packages/game-app/src/_shared/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SelectOption } from './SelectOption';
import { FirebaseFieldValue, FirebaseTimestamp } from './FirebaseTypes';
import { Game } from './Game';
import { Game, GameEntity } from './Game';
import { Status } from './Status';

export type { SelectOption, FirebaseTimestamp, FirebaseFieldValue, Game, Status };
export type { SelectOption, FirebaseTimestamp, FirebaseFieldValue, Game, Status, GameEntity };
5 changes: 3 additions & 2 deletions packages/game-app/src/createGame/apis/callCreateGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import 'firebase/firestore';
import { FirebaseCollection } from '@pipeline/common';
import { Game } from '@pipeline/models';

const DEFAULT_DECK_ID = '7p5qqvE8kCV9WWysVc2n';
export const DEFAULT_DECK_ID = '7p5qqvE8kCV9WWysVc2n';

export default function callCreateGame(data: GameCreationData, userId: string): Promise<string> {
export default async function callCreateGame(data: GameCreationData, userId: string): Promise<string> {
data.createdAt = firebase.firestore.Timestamp.now();
return firebase
.firestore()
.collection(FirebaseCollection.Games)
Expand Down
5 changes: 3 additions & 2 deletions packages/game-app/src/createGame/hook/useCreateGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { createRequestHook } from '@pipeline/requests-status';
import * as actions from '../actions';
import { useSelector } from 'react-redux';
import { selectors } from '../../gameView/slice';
import { GameEntity } from '@pipeline/models';

const _useCreateGame = createRequestHook('createGame', actions.createGame);

export default function useCreateGame() {
const request = _useCreateGame();

const newCreatedId = useSelector(selectors.getSelectedGameId);
const game: GameEntity | null = useSelector(selectors.getGame);

return {
...request,
newCreatedId,
newCreatedId: !game ? null : game.id,
};
}
20 changes: 18 additions & 2 deletions packages/game-app/src/createGame/sagas/createGame.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { call, put, select, takeEvery } from 'redux-saga/effects';
import * as actions from '../actions';
import { addRequestStatusManagement } from '@pipeline/requests-status';
import callCreateGame from '../apis/callCreateGame';
import callCreateGame, { DEFAULT_DECK_ID } from '../apis/callCreateGame';
import { actions as gameActions } from '../../gameView/slice';
import { AuthUser, selectors as authSelectors } from '@pipeline/auth';
import { DEFAULT_BOARD_DIMENSIONS } from '@pipeline/common';

function* executeCreateGame(action: ReturnType<typeof actions.createGame>) {
const user: AuthUser = yield select(authSelectors.getCurrentUser);
const newGameId: string = yield call(callCreateGame, action.payload, user.id);
yield put(gameActions.setSelectedGameId(newGameId));
yield put(
gameActions.saveGame({
id: newGameId,
scenarioTitle: action.payload.scenarioTitle,
scenarioContent: action.payload.scenarioContent,
scenarioCardId: action.payload.scenarioCardId || null,
deckId: DEFAULT_DECK_ID,
facilitator: {
id: user.id,
},
rtdbInstance: null,
cards: null,
boardDimensions: DEFAULT_BOARD_DIMENSIONS,
createdAt: action.payload.createdAt!,
}),
);
}

export default function* createGameSaga() {
Expand Down
3 changes: 3 additions & 0 deletions packages/game-app/src/createGame/types/gameCreationData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { FirebaseTimestamp } from '@pipeline/models';

export interface GameCreationData {
scenarioTitle: string;
scenarioContent: string;
createdAt?: FirebaseTimestamp;
scenarioCardId?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Box, IconButton, Typography, Dialog, Button, Input } from '@pipeline/co
import { useSelector } from 'react-redux';
import { selectors } from '../../slice';
import { ReactComponent as CopyIcon } from '@assets/icons/review.svg';
import { GameEntity } from '@pipeline/models';

type Props = {
isOpen: boolean;
Expand All @@ -23,9 +24,11 @@ function copy(text: string) {
const ShareGameDialog: React.FC<Props> = ({ isOpen, close }) => {
const t = useTranslate();

const gameId = useSelector(selectors.getSelectedGameId);
const game: GameEntity | null = useSelector(selectors.getGame);

const url = `${window.location.origin}/game/${gameId}`;
if (!game) return null;

const url = `${window.location.origin}/game/${game.id}`;

return (
<Dialog open={isOpen} title={t('game.share.title')}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React, { useCallback, useEffect, useRef } from 'react';
import { Pan } from '../../types/pan';
import { useZoomPanSetters } from '../ZoomPanContext';
import { containerStyle, contentStyle } from './ZoomPanContainer.style';
import { DEFAULT_BOARD_DIMENSIONS } from '@pipeline/common';

type Props = React.PropsWithChildren<{}>;

const scaleFactor = 0.9;
const boardSize = { width: 3840, height: 2160 };
const boardSize = { width: DEFAULT_BOARD_DIMENSIONS.x, height: DEFAULT_BOARD_DIMENSIONS.y };

const minScale = Math.max(window.innerWidth / boardSize.width, window.innerHeight / boardSize.height);

Expand Down
11 changes: 8 additions & 3 deletions packages/game-app/src/gameView/hooks/useGameState.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { useDispatch, useSelector } from 'react-redux';
import { selectors, actions } from '../slice';
import { useEffect } from 'react';
import { GameEntity } from '@pipeline/models';

export default function useGameState(currentGame: string) {
const placedCardsIds = useSelector(selectors.getPlacedCards);
const deckCardsIds = useSelector(selectors.getDeckCardsIds);
const selectedGameId = useSelector(selectors.getSelectedGameId);
const game: GameEntity | null = useSelector(selectors.getGame);

const dispatch = useDispatch();

useEffect(() => {
if ((!placedCardsIds && !deckCardsIds) || selectedGameId !== currentGame) {
if (
(!placedCardsIds && !deckCardsIds && !game) ||
(game && game.rtdbInstance == null) ||
(game && game.id !== currentGame)
) {
dispatch(actions.loadGame(currentGame));
}
}, [placedCardsIds, deckCardsIds, dispatch, currentGame, selectedGameId]);
}, [placedCardsIds, deckCardsIds, dispatch, currentGame, game]);

return {
placedCardsIds: placedCardsIds || [],
Expand Down
4 changes: 2 additions & 2 deletions packages/game-app/src/gameView/sagas/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { all } from 'redux-saga/effects';
import loadCards from './loadCards';
import loadGame from './loadGame';
import { loadGameSaga } from './loadGame';

export default function* gameSaga() {
yield all([loadCards(), loadGame()]);
yield all([loadCards(), loadGameSaga()]);
}
4 changes: 2 additions & 2 deletions packages/game-app/src/gameView/sagas/loadGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function* executeLoadGame(action: ReturnType<typeof actions.loadGame>) {
yield put(actions.saveGame({ ...game, id: action.payload }));
} else {
const bestRTDBInstance = yield call(selectBestRTDBInstance, action.payload);
yield put(actions.saveGame({ ...game, id: action.payload, rtdbInstance: bestRTDBInstance }));
yield put(actions.saveGame({ ...game, rtdbInstance: bestRTDBInstance, id: action.payload }));
}

const gameState: GameState = {
Expand All @@ -38,6 +38,6 @@ function* executeLoadGame(action: ReturnType<typeof actions.loadGame>) {
);
}

export default function* loadGameSaga() {
export function* loadGameSaga() {
yield takeEvery(actions.loadGame, addRequestStatusManagement(executeLoadGame, 'game.loadGame'));
}
16 changes: 4 additions & 12 deletions packages/game-app/src/gameView/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {
EntityState,
PayloadAction,
} from '@reduxjs/toolkit';
import { CardEntity, GameEntity } from '@pipeline/common';
import { CardEntity } from '@pipeline/common';
import { GameUIState } from './types/gameUIState';
import { FirebaseFieldValue, FirebaseTimestamp } from '@pipeline/models';
import { GameEntity } from '@pipeline/models';

export interface AdditionalCardData {
/**
Expand Down Expand Up @@ -54,9 +54,8 @@ export interface GameState {
}

export interface State {
game: GameEntity<FirebaseTimestamp, FirebaseFieldValue> | null;
game: GameEntity | null;
cards: EntityState<CardEntity>;
selectedGameId: string | null;
gameState: GameState | null;
scenario: {
title: string;
Expand All @@ -72,7 +71,6 @@ const adapter = createEntityAdapter<CardEntity>({
const initialState = {
game: null,
cards: adapter.getInitialState(),
selectedGameId: null,
gameState: null,
searchText: null,
} as State;
Expand All @@ -84,9 +82,6 @@ const slice = createSlice({
saveCards(state, action: PayloadAction<CardEntity[]>) {
state.cards = adapter.addMany(state.cards, action.payload);
},
setSelectedGameId(state, action: PayloadAction<string>) {
state.selectedGameId = action.payload;
},
setEstimation(state, action: PayloadAction<{ cardId: string; estimation: string }>) {
const gameState = state.gameState!;
gameState.cardsState[action.payload.cardId] = {
Expand All @@ -106,7 +101,6 @@ const slice = createSlice({
}>,
) {
state.gameState = action.payload.state;
state.selectedGameId = action.payload.gameId;
state.scenario = action.payload.scenario;
},
updateCardPosition(
Expand Down Expand Up @@ -139,7 +133,7 @@ const slice = createSlice({
};
}
},
saveGame(state, action: PayloadAction<GameEntity<FirebaseTimestamp, FirebaseFieldValue>>) {
saveGame(state, action: PayloadAction<GameEntity>) {
state.game = action.payload;
},
setSearchText(state, action: PayloadAction<string>) {
Expand All @@ -158,7 +152,6 @@ const getSlice = createSelector(
const getAllCards = createSelector(getSlice, state => cardsEntitiesSelectors.selectAll(state.cards));
const getAllCardsEntities = createSelector(getSlice, state => cardsEntitiesSelectors.selectEntities(state.cards));

const getSelectedGameId = createSelector(getSlice, state => state.selectedGameId);
const getGameState = createSelector(getSlice, state => state.gameState);
// TODO sort on write?
const getDeckCardsIds = createSelector(getGameState, getAllCardsEntities, (getGameState, cardsMap) => {
Expand Down Expand Up @@ -247,7 +240,6 @@ export const actions = {

export const selectors = {
getAllCards,
getSelectedGameId,
getDeckCardsIds,
getPlacedCards,
getCardStateForUI,
Expand Down
44 changes: 44 additions & 0 deletions packages/game-app/src/userGameStatus/apis/callUpdateConnections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import firebase from 'firebase';
import { RTDBPaths } from '@pipeline/common';
import CONFIG from '@pipeline/app-config';

export function initializeRTDB(rtdbInstance: string, gameId: string) {
const app = firebase.initializeApp(
{
databaseURL: `https://${rtdbInstance}.firebasedatabase.app`,
},
gameId,
);
CONFIG.REACT_APP_FIREBASE_USE_EMULATORS === 'true' && app.database().useEmulator('localhost', 9000);
}

export async function startListenToOnlineStatus(
uid: string,
gameId: string,
onConnect: () => void,
onDisconnect: () => void,
) {
const rtdb: firebase.database.Database = firebase.app(gameId).database();
const connectionsRef = rtdb.ref(`/${RTDBPaths.Connections}/${gameId}/${uid}`);
const lastOnlineRef = rtdb.ref(`/${RTDBPaths.Statuses}/${uid}/lastOnline`);

rtdb.ref('.info/connected').on('value', async snapshot => {
if (snapshot.val() === true) {
const con = connectionsRef.push();
await con.onDisconnect().remove(() => {
onDisconnect();
});
await con.set({ updatedAt: firebase.database.ServerValue.TIMESTAMP }, () => {
onConnect();
});

await lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
}
});
}

export async function stopListenToOnlineStatus(gameId: string) {
const rtdb: firebase.database.Database = firebase.app(gameId).database();
rtdb.ref('.info/connected').off();
await firebase.app(gameId).delete();
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import firebase from 'firebase';

export default async function selectBestRTDBInstance(gameId: string): Promise<string> {
export default async function selectBestRTDBInstance(gameId?: string): Promise<string> {
const functions = firebase.app().functions('europe-west1');
const selectBestRTDBInstance = functions.httpsCallable('selectBestRTDBInstance');
const res = await selectBestRTDBInstance({ gameId });
Expand Down
Loading

0 comments on commit 56292ec

Please sign in to comment.