diff --git a/package.json b/package.json index 5fa52873..2d19a780 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@react-native-firebase/auth": "^18.3.0", "@react-native-firebase/crashlytics": "^18.3.0", "@react-native-menu/menu": "^0.8.0", + "@react-native-picker/picker": "2.4.10", "@react-navigation/drawer": "^6.6.2", "@react-navigation/native": "^6.1.4", "@react-navigation/native-stack": "^6.9.12", @@ -69,8 +70,7 @@ "react-redux": "^8.0.2", "redux-persist": "^6.0.0", "semver": "^7.5.4", - "typescript": "^5.1.6", - "@react-native-picker/picker": "2.4.10" + "typescript": "^5.1.6" }, "devDependencies": { "@babel/core": "^7.19.3", @@ -100,4 +100,4 @@ "private": true, "name": "scorepad", "version": "1.0.0" -} +} \ No newline at end of file diff --git a/redux/GamesSlice.test.ts b/redux/GamesSlice.test.ts index d5888e43..39d24bf1 100644 --- a/redux/GamesSlice.test.ts +++ b/redux/GamesSlice.test.ts @@ -39,7 +39,7 @@ describe('games reducer', () => { throw new Error('game1 not found'); } expect(state.entities.game1.roundCurrent).toBe(1); - expect(state.entities.game1.roundTotal).toBe(1); + expect(state.entities.game1.roundTotal).toBe(2); }); it('should handle roundPrevious', () => { diff --git a/redux/GamesSlice.ts b/redux/GamesSlice.ts index 59ec0371..b6a3d6de 100644 --- a/redux/GamesSlice.ts +++ b/redux/GamesSlice.ts @@ -36,7 +36,7 @@ const gamesSlice = createSlice({ id: action.payload, changes: { roundCurrent: game.roundCurrent + 1, - roundTotal: Math.max(game.roundTotal, game.roundCurrent + 1) + roundTotal: Math.max(game.roundTotal, game.roundCurrent + 2) } }); }, @@ -65,30 +65,32 @@ interface GamesSlice { export const asyncCreateGame = createAsyncThunk( 'games/create', - async (gameCount: number, { dispatch }) => { - const player1Id = Crypto.randomUUID(); - const player2Id = Crypto.randomUUID(); + async ( + { gameCount, playerCount }: { gameCount: number, playerCount: number }, + { dispatch } + ) => { const newGameId = Crypto.randomUUID(); - dispatch(playerAdd({ - id: player1Id, - playerName: "Player 1", - scores: [0], - })); + const playerIds: string[] = []; + for (let i = 0; i < playerCount; i++) { + playerIds.push(Crypto.randomUUID()); + } - dispatch(playerAdd({ - id: player2Id, - playerName: "Player 2", - scores: [0], - })); + playerIds.forEach((playerId) => { + dispatch(playerAdd({ + id: playerId, + playerName: `Player ${playerIds.indexOf(playerId) + 1}`, + scores: [0], + })); + }); dispatch(gameSave({ id: newGameId, - title: `Game ${gameCount}`, + title: `Game ${gameCount + 1}`, dateCreated: Date.now(), roundCurrent: 0, - roundTotal: 0, - playerIds: [player1Id, player2Id], + roundTotal: 1, + playerIds: playerIds, })); dispatch(setCurrentGameId(newGameId)); diff --git a/redux/PlayersSlice.ts b/redux/PlayersSlice.ts index 9ee38541..6bfff8d8 100644 --- a/redux/PlayersSlice.ts +++ b/redux/PlayersSlice.ts @@ -4,7 +4,7 @@ import crashlytics from '@react-native-firebase/crashlytics'; type RoundIndex = number; -interface ScoreState { +export interface ScoreState { id: string; playerName: string; scores: number[]; diff --git a/redux/SettingsSlice.ts b/redux/SettingsSlice.ts index 6eb5ee43..fa4b626c 100644 --- a/redux/SettingsSlice.ts +++ b/redux/SettingsSlice.ts @@ -19,7 +19,7 @@ const initialState: SettingsState = { addendTwo: 10, currentGameId: undefined, onboarded: undefined, - showPointParticles: false, + showPointParticles: true, }; const settingsSlice = createSlice({ diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 18fe3721..69dd2aad 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -24,7 +24,9 @@ export type OnboardingScreenParamList = { export type RootStackParamList = { List: undefined; Game: undefined; - Settings: undefined; + Settings: { + reason?: string; + }; AppInfo: undefined; Share: undefined; Onboarding: OnboardingScreenParamList; @@ -97,13 +99,13 @@ export const Navigation = () => { }} /> ({ orientation: 'all', title: "Settings", header: ({ navigation }) => { - return ; + return ; }, - }} + })} /> void; @@ -11,22 +12,24 @@ interface Props { const BigButton: React.FunctionComponent = ({ icon, text, color, onPress }) => { return ( - - - - - {text} - - - + + + + + + {text} + + + + ); }; diff --git a/src/components/Buttons/CheckButton.tsx b/src/components/Buttons/CheckButton.tsx index c05d8c92..d890c97d 100644 --- a/src/components/Buttons/CheckButton.tsx +++ b/src/components/Buttons/CheckButton.tsx @@ -1,26 +1,34 @@ import React from 'react'; import analytics from '@react-native-firebase/analytics'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { ParamListBase } from '@react-navigation/native'; +import { ParamListBase, RouteProp } from '@react-navigation/native'; import HeaderButton from './HeaderButton'; -import { Icon } from 'react-native-elements/dist/icons/Icon'; import { systemBlue } from '../../constants'; +import { Text } from 'react-native'; +type RouteParams = { + Settings: { + reason?: string; + }; +}; interface Props { navigation: NativeStackNavigationProp; + route?: RouteProp; } -const CheckButton: React.FunctionComponent = ({ navigation }) => { +const CheckButton: React.FunctionComponent = ({ navigation, route }) => { + return ( { await analytics().logEvent('save_game'); - navigation.goBack(); + if (route?.params?.reason === 'new_game') { + navigation.navigate("Game"); + } else { + navigation.goBack(); + } }}> - + Done ); }; diff --git a/src/components/Buttons/MenuButton.tsx b/src/components/Buttons/MenuButton.tsx index 5a71d9b2..a3a17be3 100644 --- a/src/components/Buttons/MenuButton.tsx +++ b/src/components/Buttons/MenuButton.tsx @@ -11,10 +11,10 @@ interface Props { navigation: NativeStackNavigationProp; } -const MenuButton: React.FunctionComponent = ({ navigation }) => { +const HomeButton: React.FunctionComponent = ({ navigation }) => { return ( { - navigation.goBack(); + navigation.navigate("List"); await analytics().logEvent('menu'); }}> = ({ navigation }) => { ); }; -export default MenuButton; +export default HomeButton; diff --git a/src/components/Buttons/NewGameButton.tsx b/src/components/Buttons/NewGameButton.tsx index 5a0f5f1b..64a16518 100644 --- a/src/components/Buttons/NewGameButton.tsx +++ b/src/components/Buttons/NewGameButton.tsx @@ -5,8 +5,8 @@ import { ParamListBase } from '@react-navigation/native'; import { useAppSelector, useAppDispatch } from '../../../redux/hooks'; import { systemBlue } from '../../constants'; -import HeaderButton from './HeaderButton'; import { asyncCreateGame, selectAllGames } from '../../../redux/GamesSlice'; +import { MenuAction, MenuView } from '@react-native-menu/menu'; interface Props { navigation: NativeStackNavigationProp; @@ -17,21 +17,45 @@ const NewGameButton: React.FunctionComponent = ({ navigation }) => { const gameList = useAppSelector(state => selectAllGames(state)); - const addGameHandler = async () => { - dispatch(asyncCreateGame(gameList.length + 1)).then(() => { + const playerNumberOptions = [...Array.from(Array(12).keys(), n => n + 1)]; + + const menuActions: MenuAction[] = playerNumberOptions.map((number) => { + return { + id: number.toString(), + title: number.toString() + " Players", + }; + }); + + const addGameHandler = async (playerCount: number) => { + dispatch( + asyncCreateGame({ + gameCount: gameList.length + 1, + playerCount: playerCount + }) + ).then(() => { setTimeout(() => { - navigation.navigate("Game"); + navigation.navigate("Settings", { reason: 'new_game' }); }, 500); }); }; return ( - + { + const playerNumber = parseInt(nativeEvent.event); + addGameHandler(playerNumber); + }} + actions={menuActions}> - + ); }; diff --git a/src/components/EditGame.tsx b/src/components/EditGame.tsx index d9683947..d2aa27c3 100644 --- a/src/components/EditGame.tsx +++ b/src/components/EditGame.tsx @@ -37,41 +37,48 @@ const EditGame = ({ }) => { }; return ( - - - - Created: {new Date(currentGame.dateCreated).toLocaleDateString()} -   {new Date(currentGame.dateCreated).toLocaleTimeString()} - - + <> + + + + + + Created: {new Date(currentGame.dateCreated).toLocaleDateString()} +   {new Date(currentGame.dateCreated).toLocaleTimeString()} + + + ); }; const styles = StyleSheet.create({ - container: { + inputContainer: { alignItems: 'flex-start', flexDirection: 'column', justifyContent: 'flex-start', - margin: 10, + backgroundColor: '#222', + borderRadius: 10, + padding: 2, + paddingHorizontal: 10, marginVertical: 5, + marginHorizontal: 10, }, input: { - color: '#eee', + color: '#EEE', }, creation: { - color: '#eee', - margin: 10, + color: '#999', } }); diff --git a/src/components/EditPlayer.tsx b/src/components/EditPlayer.tsx index 175b1d99..bd998c2d 100644 --- a/src/components/EditPlayer.tsx +++ b/src/components/EditPlayer.tsx @@ -1,13 +1,14 @@ import React from 'react'; -import { Text, View, StyleSheet, TouchableOpacity, NativeSyntheticEvent, TextInputEndEditingEventData } from 'react-native'; +import { Text, View, StyleSheet, TouchableOpacity, NativeSyntheticEvent, TextInputEndEditingEventData, Alert } from 'react-native'; import { Icon, Input } from 'react-native-elements'; -import { palette, systemBlue } from '../constants'; +import { palette } from '../constants'; import { selectGameById, updateGame } from '../../redux/GamesSlice'; import { selectPlayerById } from '../../redux/PlayersSlice'; import { removePlayer, updatePlayer } from '../../redux/PlayersSlice'; import analytics from '@react-native-firebase/analytics'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; +import Animated from 'react-native-reanimated'; interface Props { playerId: string; @@ -32,7 +33,6 @@ const EditPlayer: React.FunctionComponent = ({ playerId, index, setPlayer playerName: name, } })); - // dispatch(playerNameSet(index, name)); setPlayerWasAdded(false); }; @@ -73,21 +73,39 @@ const EditPlayer: React.FunctionComponent = ({ playerId, index, setPlayer } })(); + const deleteConfirmHandler = async () => { + Alert.alert( + "Delete Player", + "Are you sure you want to delete this player? This will delete all scores for this player.", + [ + { + text: "Cancel", + style: "cancel", + }, + { + text: "Delete", + onPress: () => { + deleteHandler(); + } + } + ] + ); + }; + const DeleteButton = ({ }) => { if (index == 0) { return <>; }; return - - - Delete + + ; }; return ( - + {index + 1} @@ -109,10 +127,11 @@ const EditPlayer: React.FunctionComponent = ({ playerId, index, setPlayer renderErrorMessage={false} selectTextOnFocus={true} style={styles.input} + inputContainerStyle={{ borderBottomWidth: 0 }} /> - + ); }; @@ -121,12 +140,16 @@ const styles = StyleSheet.create({ alignItems: 'center', flexDirection: 'row', justifyContent: 'flex-start', - margin: 10, + backgroundColor: '#222', + borderRadius: 10, + padding: 2, + paddingHorizontal: 10, marginVertical: 5, + marginHorizontal: 10, }, playerNumber: { - color: systemBlue, - fontSize: 35, + color: '#eee', + fontSize: 25, fontVariant: ['tabular-nums'], fontWeight: "bold", padding: 5, @@ -134,11 +157,10 @@ const styles = StyleSheet.create({ colorBadge: { borderColor: '#eee', borderRadius: 25, - borderWidth: 1, - height: 30, + height: 25, marginHorizontal: 5, padding: 5, - width: 30, + width: 25, }, input: { color: '#eee', diff --git a/src/components/GameListItem.tsx b/src/components/GameListItem.tsx index b24451c2..e55eb759 100644 --- a/src/components/GameListItem.tsx +++ b/src/components/GameListItem.tsx @@ -12,29 +12,29 @@ import { setCurrentGameId } from '../../redux/SettingsSlice'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { ParamListBase } from '@react-navigation/native'; import { useAppDispatch, useAppSelector } from '../../redux/hooks'; -import { GameState } from '../../redux/GamesSlice'; import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit'; -import { selectAllPlayers } from '../../redux/PlayersSlice'; +import GameListItemPlayerName from './GameListItemPlayerName'; export type Props = { navigation: NativeStackNavigationProp; - game: GameState; + gameId: string; index: number; }; -const GameListItem: React.FunctionComponent = ({ navigation, game, index }) => { +const GameListItem: React.FunctionComponent = ({ navigation, gameId, index }) => { const dispatch = useAppDispatch(); - const chosenGame = useAppSelector(state => selectGameById(state, game.id)); - const playerNames = useAppSelector(state => - selectAllPlayers(state).filter(player => game.playerIds.includes(player.id)) - ) - .sort((a, b) => game.playerIds.indexOf(a.id) - game.playerIds.indexOf(b.id)) - .map(player => player.playerName); - const rounds: number = chosenGame?.roundTotal || 1; + if (gameId == null) { return null; } + + const roundTotal = useAppSelector(state => selectGameById(state, gameId)?.roundTotal); + const playerIds = useAppSelector(state => selectGameById(state, gameId)?.playerIds); + const gameTitle = useAppSelector(state => selectGameById(state, gameId)?.title); + const locked = useAppSelector(state => selectGameById(state, gameId)?.locked); + const dateCreated = useAppSelector(state => selectGameById(state, gameId)?.dateCreated); + if (roundTotal == null || playerIds == null) { return null; } const asyncSetCurrentGame = (dispatch: ThunkDispatch) => new Promise((resolve) => { - dispatch(setCurrentGameId(game.id)); + dispatch(setCurrentGameId(gameId)); resolve(); }); @@ -47,9 +47,9 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index }); await analytics().logEvent('select_game', { index: index, - game_id: game.id, - player_count: playerNames.length, - round_count: rounds + 1, + game_id: gameId, + player_count: playerIds.length, + round_count: roundTotal, }); }; @@ -62,8 +62,8 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index }); await analytics().logEvent('menu_share', { - round_count: rounds + 1, - player_count: playerNames.length, + round_count: roundTotal, + player_count: playerIds.length, }); }; @@ -72,12 +72,12 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index */ const editGameHandler = async () => { asyncSetCurrentGame(dispatch).then(() => { - navigation.navigate("Settings"); + navigation.navigate("Settings", { reason: 'edit_game' }); }); await analytics().logEvent('menu_edit', { - round_count: rounds + 1, - player_count: playerNames.length, + round_count: roundTotal, + player_count: playerIds.length, }); }; @@ -87,7 +87,7 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index const deleteGameHandler = async () => { Alert.alert( 'Delete Game', - `Are you sure you want to delete ${game.title}?`, + `Are you sure you want to delete ${gameTitle}?`, [ { text: 'Cancel', @@ -97,7 +97,7 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index { text: 'OK', onPress: () => { - dispatch(gameDelete(game.id)); + dispatch(gameDelete(gameId)); } }, ], @@ -106,8 +106,8 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index await analytics().logEvent('delete_game', { index: index, - round_count: rounds + 1, - player_count: playerNames.length, + round_count: roundTotal, + player_count: playerIds.length, }); }; @@ -166,31 +166,33 @@ const GameListItem: React.FunctionComponent = ({ navigation, game, index }; return ( - - + - {game.title} - {game.locked && } + {gameTitle} + {locked && } - {game.dateCreated} + {dateCreated} - {playerNames.join(', ')} + {playerIds.map((playerId, index) => ( + + ))} - {playerNames.length} + {playerIds.length} - {rounds + 1} + {roundTotal} diff --git a/src/components/GameListItemPlayerName.tsx b/src/components/GameListItemPlayerName.tsx new file mode 100644 index 00000000..49d3dbb7 --- /dev/null +++ b/src/components/GameListItemPlayerName.tsx @@ -0,0 +1,19 @@ +import { Text } from "react-native"; +import { useAppSelector } from "../../redux/hooks"; +import { selectPlayerById } from "../../redux/PlayersSlice"; + +interface Props { + playerId: string; + last?: boolean; +} + +const GameListItemPlayerName: React.FunctionComponent = ({ playerId, last = false }) => { + + const playerName = useAppSelector(state => selectPlayerById(state, playerId)?.playerName); + + return ( + {playerName}{!last && ', '} + ); +}; + +export default GameListItemPlayerName; diff --git a/src/components/Headers/AppInfoHeader.tsx b/src/components/Headers/AppInfoHeader.tsx index 22077e1c..793bd016 100644 --- a/src/components/Headers/AppInfoHeader.tsx +++ b/src/components/Headers/AppInfoHeader.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Text, StyleSheet } from 'react-native'; -import MenuButton from '../Buttons/MenuButton'; +import HomeButton from '../Buttons/MenuButton'; import CustomHeader from './CustomHeader'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { ParamListBase } from '@react-navigation/native'; @@ -14,7 +14,7 @@ const AppInfoHeader: React.FunctionComponent = ({ navigation }: Props) => return ( } + headerLeft={} headerCenter={Settings} /> ); diff --git a/src/components/Headers/GameHeader.tsx b/src/components/Headers/GameHeader.tsx index 1251caee..93f7645b 100644 --- a/src/components/Headers/GameHeader.tsx +++ b/src/components/Headers/GameHeader.tsx @@ -11,7 +11,7 @@ import { roundNext, roundPrevious } from '../../../redux/GamesSlice'; import { selectGameById } from '../../../redux/GamesSlice'; import { systemBlue } from '../../constants'; import { Button } from 'react-native-elements'; -import MenuButton from '../Buttons/MenuButton'; +import HomeButton from '../Buttons/MenuButton'; import AddendButton from '../Buttons/AddendButton'; import CustomHeader from './CustomHeader'; @@ -71,7 +71,7 @@ const GameHeader: React.FunctionComponent = ({ navigation }) => { if (currentGame == null) { return } + headerLeft={} headerCenter={Error} />; } @@ -102,7 +102,7 @@ const GameHeader: React.FunctionComponent = ({ navigation }) => { return ( - + } headerCenter={<> diff --git a/src/components/Headers/SettingsHeader.tsx b/src/components/Headers/SettingsHeader.tsx index 0d0ade87..5fe06642 100644 --- a/src/components/Headers/SettingsHeader.tsx +++ b/src/components/Headers/SettingsHeader.tsx @@ -1,25 +1,28 @@ import React from 'react'; import { Text, StyleSheet } from 'react-native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { ParamListBase } from '@react-navigation/native'; +import { ParamListBase, RouteProp } from '@react-navigation/native'; -import { useAppSelector } from '../../../redux/hooks'; -import { selectGameById } from '../../../redux/GamesSlice'; import CheckButton from '../Buttons/CheckButton'; import CustomHeader from './CustomHeader'; +type RouteParams = { + Settings: { + reason?: string; + }; +}; + interface Props { navigation: NativeStackNavigationProp; + route: RouteProp; } -const SettingsHeader: React.FunctionComponent = ({ navigation }) => { - const currentGame = useAppSelector(state => selectGameById(state, state.settings.currentGameId)); - +const SettingsHeader: React.FunctionComponent = ({ navigation, route }) => { return ( } - headerCenter={{currentGame?.title}} - headerRight={} + headerCenter={Edit Game} + headerRight={} /> ); }; diff --git a/src/components/Headers/ShareHeader.tsx b/src/components/Headers/ShareHeader.tsx index a851b449..93033855 100644 --- a/src/components/Headers/ShareHeader.tsx +++ b/src/components/Headers/ShareHeader.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Text, StyleSheet } from 'react-native'; -import MenuButton from '../Buttons/MenuButton'; +import HomeButton from '../Buttons/MenuButton'; import CustomHeader from './CustomHeader'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { ParamListBase } from '@react-navigation/native'; @@ -14,7 +14,7 @@ const ShareHeader: React.FunctionComponent = ({ navigation }: Props) => { return ( } + headerLeft={} headerCenter={Share} /> ); diff --git a/src/components/Rounds.tsx b/src/components/Rounds.tsx index 55c1463b..536dce99 100644 --- a/src/components/Rounds.tsx +++ b/src/components/Rounds.tsx @@ -18,7 +18,7 @@ interface RoundScollOffset { [key: number]: number; } -const Rounds: React.FunctionComponent = ({ navigation }) => { +const Rounds: React.FunctionComponent = ({ }) => { const [roundScollOffset, setRoundScrollOffset] = useState({}); const currentGameId = useAppSelector(state => state.settings.currentGameId); @@ -26,7 +26,7 @@ const Rounds: React.FunctionComponent = ({ navigation }) => { if (typeof currentGameId == 'undefined') return null; const roundCurrent = useAppSelector(state => selectGameById(state, currentGameId)?.roundCurrent || 0); - const roundTotal = useAppSelector(state => selectGameById(state, currentGameId)?.roundTotal || 0); + const roundTotal = useAppSelector(state => selectGameById(state, currentGameId)?.roundTotal || 1); const roundsScrollViewEl = useRef(null); @@ -51,11 +51,11 @@ const Rounds: React.FunctionComponent = ({ navigation }) => { }); }, [roundCurrent, roundScollOffset]); - const roundsIterator = [...Array(roundTotal + 1).keys()]; + const roundsIterator = [...Array(roundTotal).keys()]; return ( - + ; - disabled?: boolean; -} - -const PlayerNameColumn: React.FunctionComponent = ({ navigation, disabled = false }) => { +const PlayerNameColumn: React.FunctionComponent = ({ }) => { const currentGameId = useAppSelector(state => state.settings.currentGameId); const currentGame = useAppSelector(state => selectGameById(state, currentGameId)); @@ -27,29 +18,22 @@ const PlayerNameColumn: React.FunctionComponent = ({ navigation, disabled ).sort((a, b) => currentGame.playerIds.indexOf(a.id) - currentGame.playerIds.indexOf(b.id)); return ( - { - if (disabled) return; - - await analytics().logEvent('edit_game', { - game_id: currentGameId - }); - navigation.navigate("Settings"); - }}> +   + color='white' /> {players.map((player, index) => ( - + {player.playerName} ))} - + ); }; diff --git a/src/components/Sheets/GameSheet.tsx b/src/components/Sheets/GameSheet.tsx index 13ea995b..69526649 100644 --- a/src/components/Sheets/GameSheet.tsx +++ b/src/components/Sheets/GameSheet.tsx @@ -1,18 +1,21 @@ import React, { useCallback, useMemo, useState } from 'react'; -import { View, StyleSheet, Text } from 'react-native'; +import { View, StyleSheet, Text, Alert } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { ParamListBase } from '@react-navigation/native'; import BottomSheet, { BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { useIsFocused } from '@react-navigation/native'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import Rounds from '../Rounds'; import { selectGameById, updateGame } from '../../../redux/GamesSlice'; import { systemBlue } from '../../constants'; -import Animated, { Extrapolate, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; -import { TouchableOpacity, TouchableWithoutFeedback } from 'react-native'; -import { Icon } from 'react-native-elements'; +import Animated, { Extrapolate, FadeIn, Layout, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; +import { TouchableWithoutFeedback } from 'react-native'; +import { Button } from 'react-native-elements'; import { useGameSheetContext } from './GameSheetContext'; +import BigButton from '../BigButtons/BigButton'; +import { selectAllPlayers, updatePlayer } from '../../../redux/PlayersSlice'; /** * Height of the bottom sheet @@ -25,6 +28,8 @@ interface Props { } const GameSheet: React.FunctionComponent = ({ navigation, containerHeight }) => { + const isFocused = useIsFocused(); + const currentGameId = useAppSelector(state => state.settings.currentGameId); if (typeof currentGameId == 'undefined') return null; @@ -33,6 +38,13 @@ const GameSheet: React.FunctionComponent = ({ navigation, containerHeight if (currentGame == undefined) return null; + const players = useAppSelector(state => selectAllPlayers(state) + .filter(player => currentGame?.playerIds.includes(player.id)) + ).sort((a, b) => { + if (currentGame?.playerIds == undefined) return 0; + return currentGame.playerIds.indexOf(a.id) - currentGame.playerIds.indexOf(b.id); + }); + // ref const gameSheetRef = useGameSheetContext(); @@ -53,6 +65,46 @@ const GameSheet: React.FunctionComponent = ({ navigation, containerHeight }) ); + /** + * Reset the game, but keep the players + */ + const resetGameHandler = () => { + Alert.alert( + "Reset Game", + "Are you sure you want to reset this game? This will reset all scores and rounds.", + [ + { + text: "Cancel", + style: "cancel" + }, + { + text: "Reset", + onPress: () => { + if (currentGame == undefined) return; + + players.forEach((player) => { + dispatch(updatePlayer({ + id: player.id, + changes: { + scores: [0], + } + } + )); + }); + dispatch(updateGame({ + id: currentGame.id, + changes: { + roundCurrent: 0, + roundTotal: 0, + } + })); + navigation.navigate("Game"); + } + } + ] + ); + }; + // State variable for the current snap point index const [, setSnapPointIndex] = useState(0); @@ -136,6 +188,7 @@ const GameSheet: React.FunctionComponent = ({ navigation, containerHeight + {currentGame.locked && { gameSheetRef?.current?.snapToIndex(snapPoints.length - 1); }} @@ -143,7 +196,7 @@ const GameSheet: React.FunctionComponent = ({ navigation, containerHeight Locked } - {!currentGame.locked && + {false && navigation.navigate('Settings')}> Edit @@ -152,33 +205,55 @@ const GameSheet: React.FunctionComponent = ({ navigation, containerHeight - + Tap on a column to set the current round. - - navigation.navigate('Share')}> - - - Share - - - - - - + + {!currentGame.locked && + +