fix theme and errors

This commit is contained in:
Halit Aksoy 2024-03-31 16:39:39 +03:00
parent 2cca40034d
commit 4f45da5ab5
8 changed files with 168 additions and 93 deletions

View File

@ -75,9 +75,12 @@ function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} initialParams={{context}} />
<Stack.Screen name="Loby" component={LobyScreen} />
<Stack.Screen name="Game" component={GameScreen} />
<Stack.Screen name="Home" component={HomeScreen} initialParams={{context}}
options={{title: t("Mancala"), headerStyle: { backgroundColor: context.themeManager.theme.appBarBgColor }}}/>
<Stack.Screen name="Loby" component={LobyScreen}
options={{title: t("Mancala"), headerStyle: { backgroundColor: context.themeManager.theme.appBarBgColor }}}/>
<Stack.Screen name="Game" component={GameScreen}
options={{title: t("Mancala"), headerStyle: { backgroundColor: context.themeManager.theme.appBarBgColor }}}/>
</Stack.Navigator>
</NavigationContainer>
);

View File

@ -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 <PitView key={key} pitViewModel={pitViewModel} onClick={() => onPitSelect(pit.index, pit)} />;
const stones = [...Util.range(pitViewModel.stoneCount)].map((i, index) => (
<StoneView key={index} color={pitViewModel.stoneColor} style={{width: pitWidth / 10, height: pitWidth / 10}}/>
));
return <PitView
key={key}
pitViewModel={pitViewModel}
onClick={() => onPitSelect(pit.index, pit)}
style={{width: pitWidth, height: pitWidth}} >
{stones}
</PitView>;
};
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) => (
<StoneView key={index} color={primaryStoreViewModel.stoneColor} style={{width: pitWidth / 10, height: pitWidth / 10}}/>
));
const stonesSecondaryStore = [...Util.range(secondaryStoreViewModel.stoneCount)].map((i, index) => (
<StoneView key={index} color={secondaryStoreViewModel.stoneColor} style={{width: pitWidth / 10, height: pitWidth / 10}} />
));
return (
<View style={{
backgroundColor: theme.boardColor,
padding: 20,
//display: 'grid',
//gridTemplateColumns: 'repeat(8, 11vw)',
//gridTemplateRows: 'repeat(2, 11vw)',
borderRadius: 30
padding: boardPadding,
borderRadius: 20,
flexDirection: "row",
gap: gap,
height: "auto",
}}>
<StoreView
context={context}
pitViewModel={revert ? player2BankViewModel : player1BankViewModel}
gridColumn="8 / 9"
gridRow="1 / 3"
/>
pitViewModel={primaryStoreViewModel}
style={{width: pitWidth, height: pitWidth * 2 + gap}}
fontSize={pitWidth / 5}
>
{stonesPrimaryStore}
</StoreView>
<View style={{flex: 6}}>
<View style={{
flexDirection: "row",
width: '100%',
flex: 1,
gap: gap
}}>
{revert ? player1Pits?.reverse() : player2Pits?.reverse()}
</View>
<View style={{
flexDirection: "row",
width: '100%',
flex: 1,
gap: gap
}}>
{revert ? player2Pits : player1Pits}
</View>
</View>
<StoreView
context={context}
pitViewModel={revert ? player1BankViewModel : player2BankViewModel}
gridColumn="1 / 2"
gridRow="1 / 3"
/>
{revert ? player1Pits?.reverse() : player2Pits?.reverse()}
{revert ? player2Pits : player1Pits}
pitViewModel={secondaryStoreViewModel}
style={{width: pitWidth, height: pitWidth * 2 + gap}}
fontSize={pitWidth / 5}
>
{stonesSecondaryStore}
</StoreView>
</View>
);
};

View File

@ -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) => (
<StoneView key={index} color={pitViewModel.stoneColor} />
));
style: ViewStyle,
children: React.ReactNode
}> = ({ pitViewModel, onClick, style, children }) => {
return (
<Pressable onPress={onClick}>
<View style={{
backgroundColor: pitViewModel.pitColor,
margin: 5,
<View style={[style, {
backgroundColor: pitViewModel.pitColor,
borderRadius: 50,
//justifyItems: 'center',
}]}>
<Pressable onPress={onClick} style={{
width: "100%",
height: "100%",
padding: 5,
borderRadius: 50,
display: 'flex',
alignItems: 'center',
alignContent: 'center',
justifyContent: 'center',
//justifyItems: 'center',
flexWrap: 'wrap',
flexDirection: "row",
flexWrap: "wrap"
}}>
{stones}
</View>
</Pressable>
{children}
</Pressable>
</View>
);
};

View File

@ -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 (
<View style={{
<View style={[{
backgroundColor: color,
margin: 1,
width: 10,
height: 10,
borderRadius: 10,
}}>
}, style]}>
</View>
);
};

View File

@ -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) => (
<StoneView key={index} color={pitViewModel.stoneColor} />
));
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 (
<View
style={{
style={[style, {
backgroundColor: pitViewModel.pitColor,
//gridColumn: gridColumn,
//gridRow: gridRow,
margin: 5,
borderRadius: 50,
display: 'flex',
alignItems: 'center',
@ -35,17 +31,18 @@ const StoreView: FunctionComponent<{
alignContent: 'center',
flexWrap: 'wrap',
position: 'relative',
}}>
{stones}
flexDirection: "row"
}]}>
{children}
<Text style={{
color: textColor,
position: 'absolute',
bottom: 20,
bottom: fontSize/2,
fontFamily: 'monospace',
fontWeight: 'bold',
fontSize: 20,
fontSize: fontSize
}}>
{stones.length}
{pitViewModel.stoneCount}
</Text>
</View>
);

View File

@ -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: () => (
<Button onPress={() => {
if (game?.mancalaGame.state !== "ended") {
onLeaveGameClick();
} else {
onNewGameClick();
}
}} title={game?.mancalaGame.state !== "ended" ? t('Leave') : t('NewGame')} />
),
});
}
React.useEffect(() => {
updateHeaderButton();
}, [navigation, game]);
const textColorOnAppBar = getColorByBrightness(
context.themeManager.theme.appBarBgColor,
context.themeManager.theme.textColor,
@ -279,21 +303,18 @@ export function GameScreen({ navigation, route }: GameScreenProps) {
function renderMobileBoardToolbar() {
return <BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
<View />
{buildInfoPanel({ visible: isMobile })}
<View />
</BoardToolbar>;
}
function buildBoardTopToolbar() {
return <BoardToolbar style={{ alignItems: "flex-end" }} visible={showBoardView || false}>
<UserStatus style={{
marginBottom: 15, marginLeft: "6%", maxWidth: isMobile ? 40 : 30,
width: isMobile ? 40 : 30
}} context={context} layoutMode="left" user={topLocatedUser} visible={showBoardView || false} />
<UserStatus style={{ marginLeft: 10, width: 100 }} context={context}
layoutMode="left" user={topLocatedUser} visible={showBoardView || false} />
{buildInfoPanel({ visible: !isMobile })}
<UserStatus style={{
marginBottom: 15, marginRight: "6%", maxWidth: isMobile ? 40: 30,
width: isMobile ? 40: 30
}} context={context} layoutMode="right" user={bottomLocatedUser} visible={showBoardView || false} />
<UserStatus style={{ marginLeft: 10, width: 100 }} context={context} layoutMode="right" user={bottomLocatedUser} visible={showBoardView || false} />
</BoardToolbar>;
}

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import { View, Button } from 'react-native';
import { View, Button, Pressable, Text } from 'react-native';
import { useTranslation } from 'react-i18next';
import { HomeScreenProps } from '../types';
import CircularPanel from '../components/CircularPanel';
export function HomeScreen({ navigation, route }: HomeScreenProps) {
@ -9,10 +10,10 @@ export function HomeScreen({ navigation, route }: HomeScreenProps) {
const { t } = useTranslation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title={t('NewGame')}
onPress={() => navigation.navigate('Loby', { context })}></Button>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: context.themeManager.theme?.background }}>
<Pressable onPress={() => navigation.replace('Loby', { context })}>
<CircularPanel color={context.themeManager.theme?.boardColor} children={<Text style={{color: context.themeManager.theme.textColor}}>{t('NewGame')}</Text>}/>
</Pressable>
</View>
);
}

View File

@ -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 (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: context.themeManager.theme?.background }}>
<Text>{t('SearchingOpponent')}</Text>
</View>
);