diff --git a/mobile/src/App.tsx b/mobile/src/App.tsx index 3e72a67..45e7d19 100644 --- a/mobile/src/App.tsx +++ b/mobile/src/App.tsx @@ -75,9 +75,12 @@ function App() { return ( - - - + + + ); diff --git a/mobile/src/components/board/BoardView.tsx b/mobile/src/components/board/BoardView.tsx index 5ae7a6e..735d413 100644 --- a/mobile/src/components/board/BoardView.tsx +++ b/mobile/src/components/board/BoardView.tsx @@ -7,7 +7,9 @@ import PitView from "./PitView"; import StoreView from "./StoreView"; import { Game } from "../../models/Game"; import { Pit } from "mancala.js"; -import { View } from "react-native"; +import { View, Dimensions } from "react-native"; +import StoneView from "./StoneView"; +import Util from "../../util/Util"; const BoardView: FunctionComponent<{ game: Game; @@ -17,11 +19,30 @@ const BoardView: FunctionComponent<{ revert: boolean; onPitSelect: (index: number, pit: Pit) => void; }> = ({ game, context, boardId, boardViewModel, revert, onPitSelect: onPitSelect }) => { + const windowWidth = Dimensions.get('window').width; + const windowHeight = Dimensions.get('window').height; + + const boardMargin = 0; + const boardPadding = 10; + const gap = 5; + const totalGap = gap * 7; + + const pitWidth = (windowWidth - boardMargin * 2 - boardPadding * 2 - totalGap) / 8; + const mancalaGame = game?.mancalaGame; const theme = context.themeManager.theme; const createPitView = (key: any, pit: Pit, pitViewModel: PitViewModel) => { - return onPitSelect(pit.index, pit)} />; + const stones = [...Util.range(pitViewModel.stoneCount)].map((i, index) => ( + + )); + return onPitSelect(pit.index, pit)} + style={{width: pitWidth, height: pitWidth}} > + {stones} + ; }; const createPitViewList = (pits: Pit[]) => pits.map((pit, index) => createPitView(index, pit, boardViewModel.pits[pit.index])); @@ -31,29 +52,59 @@ const BoardView: FunctionComponent<{ const player2BankIndex = mancalaGame?.board.player2BankIndex(); const player1BankViewModel = boardViewModel.pits[player1BankIndex]; const player2BankViewModel = boardViewModel.pits[player2BankIndex]; + + const primaryStoreViewModel = revert ? player1BankViewModel : player2BankViewModel; + const secondaryStoreViewModel = revert ? player2BankViewModel : player1BankViewModel; + const stonesPrimaryStore = [...Util.range(primaryStoreViewModel.stoneCount)].map((i, index) => ( + + )); + const stonesSecondaryStore = [...Util.range(secondaryStoreViewModel.stoneCount)].map((i, index) => ( + + )); + return ( + pitViewModel={primaryStoreViewModel} + style={{width: pitWidth, height: pitWidth * 2 + gap}} + fontSize={pitWidth / 5} + > + {stonesPrimaryStore} + + + + {revert ? player1Pits?.reverse() : player2Pits?.reverse()} + + + {revert ? player2Pits : player1Pits} + + - {revert ? player1Pits?.reverse() : player2Pits?.reverse()} - {revert ? player2Pits : player1Pits} + pitViewModel={secondaryStoreViewModel} + style={{width: pitWidth, height: pitWidth * 2 + gap}} + fontSize={pitWidth / 5} + > + {stonesSecondaryStore} + ); }; diff --git a/mobile/src/components/board/PitView.tsx b/mobile/src/components/board/PitView.tsx index 2a89d3c..a5a075d 100644 --- a/mobile/src/components/board/PitView.tsx +++ b/mobile/src/components/board/PitView.tsx @@ -3,33 +3,35 @@ import { FunctionComponent } from "react"; import Util from "../../util/Util"; import PitViewModel from "../../viewmodel/PitViewModel"; import StoneView from "./StoneView"; -import { Pressable, View } from "react-native"; +import { Pressable, View, ViewStyle } from "react-native"; const PitView: FunctionComponent<{ pitViewModel: PitViewModel; onClick: () => void; -}> = ({ pitViewModel, onClick }) => { - const stones = [...Util.range(pitViewModel.stoneCount)].map((i, index) => ( - - )); - + style: ViewStyle, + children: React.ReactNode +}> = ({ pitViewModel, onClick, style, children }) => { return ( - - + - {stones} - - + {children} + + + ); }; diff --git a/mobile/src/components/board/StoneView.tsx b/mobile/src/components/board/StoneView.tsx index 84a809e..e52f5a4 100644 --- a/mobile/src/components/board/StoneView.tsx +++ b/mobile/src/components/board/StoneView.tsx @@ -1,16 +1,16 @@ import * as React from "react"; import { FunctionComponent } from "react"; -import { View } from "react-native"; +import { View, ViewStyle } from "react-native"; -const StoneView: FunctionComponent<{ color: string }> = ({ color }) => { +const StoneView: FunctionComponent<{ color: string, style?: ViewStyle }> = ({ color, style }) => { return ( - + }, style]}> ); }; diff --git a/mobile/src/components/board/StoreView.tsx b/mobile/src/components/board/StoreView.tsx index b0a5162..b51a2f7 100644 --- a/mobile/src/components/board/StoreView.tsx +++ b/mobile/src/components/board/StoreView.tsx @@ -5,17 +5,16 @@ import { getColorByBrightness } from "../../util/ColorUtil"; import Util from "../../util/Util"; import PitViewModel from "../../viewmodel/PitViewModel"; import StoneView from "./StoneView"; -import { Text, View } from "react-native"; +import { Text, View, ViewStyle } from "react-native"; const StoreView: FunctionComponent<{ context: Context; pitViewModel: PitViewModel; - gridColumn: string; - gridRow: string; -}> = ({ context, pitViewModel, gridColumn, gridRow }) => { - const stones = [...Util.range(pitViewModel.stoneCount)].map((i, index) => ( - - )); + style: ViewStyle, + children: React.ReactNode + fontSize: number +}> = ({ context, pitViewModel, style, children, fontSize }) => { + const textColor = getColorByBrightness( pitViewModel.pitColor, context.themeManager.theme.textColor, @@ -23,11 +22,8 @@ const StoreView: FunctionComponent<{ ); return ( - {stones} + flexDirection: "row" + }]}> + {children} - {stones.length} + {pitViewModel.stoneCount} ); diff --git a/mobile/src/screens/GameScreen.tsx b/mobile/src/screens/GameScreen.tsx index d47805e..df9f69e 100644 --- a/mobile/src/screens/GameScreen.tsx +++ b/mobile/src/screens/GameScreen.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { View, Button, Text, useWindowDimensions } from 'react-native'; +import { View, Button, Text, useWindowDimensions, Alert } from 'react-native'; import { useTranslation } from 'react-i18next'; import { GameScreenProps } from '../types'; import { useState } from 'react'; @@ -76,7 +76,7 @@ export function GameScreen({ navigation, route }: GameScreenProps) { } const onGameCrashed = (message: any) => { const newCrashMessage = message as string; - Snackbar.show({text: t("InternalErrorOccurred")}); + Snackbar.show({ text: t("InternalErrorOccurred") }); console.error("on_game_crash"); console.error(newCrashMessage); } @@ -125,24 +125,29 @@ export function GameScreen({ navigation, route }: GameScreenProps) { const onLeaveGameClick = () => { if (Util.checkConnectionAndMaybeAlert(context, t("ConnectionLost"))) return; - - // TODO - //swal({ - // title: context.texts.AreYouSureToLeaveGame, - // icon: "warning", - // buttons: [context.texts.Yes, context.texts.Cancel], - // dangerMode: true, - //}) - // .then((cancel) => { - // if (!cancel) { - // context.rtmt.sendMessage(channel_leave_game, {}); - // } - // }); + Alert.alert(t('AreYouSureToLeaveGame'), "", [ + { + text: t('Cancel'), + style: 'cancel', + }, + { + text: t('Yes'), + onPress: () => { + context.rtmt.sendMessage(channel_leave_game, {}); + updateHeaderButton(); + }, + style: 'default', + }, + ], + { + cancelable: true, + onDismiss: () => { } + }); }; const onNewGameClick = () => { if (Util.checkConnectionAndMaybeAlert(context, t("ConnectionLost"))) return; - navigation.navigate("Loby", { context }) + navigation.replace("Loby", { context }) }; const onPitSelect = (index: number, pit: Pit) => { @@ -150,31 +155,31 @@ export function GameScreen({ navigation, route }: GameScreenProps) { return; } if (userKeyWhoLeave) { - Snackbar.show({text: t("GameEnded")}); + Snackbar.show({ text: t("GameEnded") }); return; } if (game.mancalaGame.state === "ended") { - Snackbar.show({text: t("GameEnded")}); + Snackbar.show({ text: t("GameEnded") }); return; } if (Util.checkConnectionAndMaybeAlert(context, t("ConnectionLost"))) return; if (game.mancalaGame.getPlayerIdByIndex(index) !== userKey) { - Snackbar.show({text: t("UCanOnlyPlayYourOwnPits")}); + Snackbar.show({ text: t("UCanOnlyPlayYourOwnPits") }); return; } const pitIndexForUser = index % (game.mancalaGame.board.totalPitCount() / 2); if (!game.mancalaGame.canPlayerMove(userKey, pitIndexForUser)) { - Snackbar.show({text: t("OpponentTurn")}); + Snackbar.show({ text: t("OpponentTurn") }); return; } if (checkHasAnOngoingAction()) { - Snackbar.show({text: t("UMustWaitUntilCurrentMoveComplete")}); + Snackbar.show({ text: t("UMustWaitUntilCurrentMoveComplete") }); return; } if (!boardViewModel) return; //TODO: this check should be in mancala.js if (pit.stoneCount === 0) { - Snackbar.show({text: t("UCanNotPlayEmptyPit")}); + Snackbar.show({ text: t("UCanNotPlayEmptyPit") }); return; } setHasOngoingAction(true); @@ -206,6 +211,25 @@ export function GameScreen({ navigation, route }: GameScreenProps) { }; }, []); + const updateHeaderButton = () => { + console.info(game?.mancalaGame.state) + navigation.setOptions({ + headerRight: () => ( + + + navigation.replace('Loby', { context })}> + {t('NewGame')}}/> + ); } diff --git a/mobile/src/screens/LobyScreen.tsx b/mobile/src/screens/LobyScreen.tsx index 254c78d..9141323 100644 --- a/mobile/src/screens/LobyScreen.tsx +++ b/mobile/src/screens/LobyScreen.tsx @@ -14,7 +14,7 @@ export default function LobyScreen({ navigation, route }: LobyScreenProps) { const onGameStart = async (message: Object) => { const newGame: CommonMancalaGame = message as CommonMancalaGame; const userKey = await context.userKeyStore.getUserKey(); - navigation.navigate("Game", { context, gameId: newGame.id, userKey }) + navigation.replace("Game", { context, gameId: newGame.id, userKey }) } useEffect(() => { @@ -26,7 +26,7 @@ export default function LobyScreen({ navigation, route }: LobyScreenProps) { }, []); return ( - + {t('SearchingOpponent')} );