Showing
23 changed files
with
607 additions
and
0 deletions
App.js
0 → 100644
| 1 | +import { useState } from "react"; | ||
| 2 | +import { StyleSheet, ImageBackground, SafeAreaView } from "react-native"; | ||
| 3 | +import { LinearGradient } from "expo-linear-gradient"; | ||
| 4 | +import { useFonts } from "expo-font"; | ||
| 5 | +import AppLoading from "expo-app-loading"; | ||
| 6 | + | ||
| 7 | +import StartGameScreen from "./screens/StartGameScreen"; | ||
| 8 | +import GameScreen from "./screens/GameScreen"; | ||
| 9 | +import GameOverScreen from "./screens/GameOverScreen"; | ||
| 10 | +import Colors from "./constants/colors"; | ||
| 11 | + | ||
| 12 | +export default function App() { | ||
| 13 | + const [userNumber, setUserNumber] = useState(); | ||
| 14 | + const [gameIsOver, setGameIsOver] = useState(true); | ||
| 15 | + const [guessRounds, setGuessRounds] = useState(0); | ||
| 16 | + | ||
| 17 | + const [fontsLoaded] = useFonts({ | ||
| 18 | + "open-sans": require("./assets/fonts/OpenSans-Regular.ttf"), | ||
| 19 | + "open-sans-bold": require("./assets/fonts/OpenSans-Bold.ttf"), | ||
| 20 | + }); | ||
| 21 | + | ||
| 22 | + if (!fontsLoaded) { | ||
| 23 | + return <AppLoading />; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + function pickedNumberHandler(pickedNumber) { | ||
| 27 | + setUserNumber(pickedNumber); | ||
| 28 | + setGameIsOver(false); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + function gameOverHandler(numberOfRounds) { | ||
| 32 | + setGameIsOver(true); | ||
| 33 | + setGuessRounds(numberOfRounds); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + function startNewGameHandler() { | ||
| 37 | + setUserNumber(null); | ||
| 38 | + setGuessRounds(0); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + let screen = <StartGameScreen onPickNumber={pickedNumberHandler} />; | ||
| 42 | + | ||
| 43 | + if (userNumber) { | ||
| 44 | + screen = ( | ||
| 45 | + <GameScreen userNumber={userNumber} onGameOver={gameOverHandler} /> | ||
| 46 | + ); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + if (gameIsOver && userNumber) { | ||
| 50 | + screen = ( | ||
| 51 | + <GameOverScreen | ||
| 52 | + userNumber={userNumber} | ||
| 53 | + roundsNumber={guessRounds} | ||
| 54 | + onStartNewGame={startNewGameHandler} | ||
| 55 | + /> | ||
| 56 | + ); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + return ( | ||
| 60 | + <LinearGradient | ||
| 61 | + colors={[Colors.primary700, Colors.accent500]} | ||
| 62 | + style={styles.rootScreen} | ||
| 63 | + > | ||
| 64 | + <ImageBackground | ||
| 65 | + source={require("./assets/images/background.png")} | ||
| 66 | + resizeMode="cover" | ||
| 67 | + style={styles.rootScreen} | ||
| 68 | + imageStyle={styles.backgroundImage} | ||
| 69 | + > | ||
| 70 | + <SafeAreaView style={styles.rootScreen}>{screen}</SafeAreaView> | ||
| 71 | + </ImageBackground> | ||
| 72 | + </LinearGradient> | ||
| 73 | + ); | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +const styles = StyleSheet.create({ | ||
| 77 | + rootScreen: { | ||
| 78 | + flex: 1, | ||
| 79 | + }, | ||
| 80 | + backgroundImage: { | ||
| 81 | + opacity: 0.15, | ||
| 82 | + }, | ||
| 83 | +}); |
app.json
0 → 100644
| 1 | +{ | ||
| 2 | + "expo": { | ||
| 3 | + "name": "numGame", | ||
| 4 | + "slug": "numGame", | ||
| 5 | + "version": "1.0.0", | ||
| 6 | + "orientation": "portrait", | ||
| 7 | + "icon": "./assets/icon.png", | ||
| 8 | + "userInterfaceStyle": "light", | ||
| 9 | + "splash": { | ||
| 10 | + "image": "./assets/splash.png", | ||
| 11 | + "resizeMode": "contain", | ||
| 12 | + "backgroundColor": "#ffffff" | ||
| 13 | + }, | ||
| 14 | + "updates": { | ||
| 15 | + "fallbackToCacheTimeout": 0 | ||
| 16 | + }, | ||
| 17 | + "assetBundlePatterns": [ | ||
| 18 | + "**/*" | ||
| 19 | + ], | ||
| 20 | + "ios": { | ||
| 21 | + "supportsTablet": true | ||
| 22 | + }, | ||
| 23 | + "android": { | ||
| 24 | + "adaptiveIcon": { | ||
| 25 | + "foregroundImage": "./assets/adaptive-icon.png", | ||
| 26 | + "backgroundColor": "#FFFFFF" | ||
| 27 | + } | ||
| 28 | + }, | ||
| 29 | + "web": { | ||
| 30 | + "favicon": "./assets/favicon.png" | ||
| 31 | + } | ||
| 32 | + } | ||
| 33 | +} |
assets/adaptive-icon.png
0 → 100644
17.1 KB
assets/favicon.png
0 → 100644
1.43 KB
assets/fonts/OpenSans-Bold.ttf
0 → 100644
No preview for this file type
assets/fonts/OpenSans-Regular.ttf
0 → 100644
No preview for this file type
assets/icon.png
0 → 100644
21.9 KB
assets/images/background.png
0 → 100644
2.19 MB
assets/images/success.png
0 → 100644
82.1 KB
assets/splash.png
0 → 100644
46.2 KB
babel.config.js
0 → 100644
components/game/GuessLogItem.js
0 → 100644
| 1 | +import { View, Text, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +import Colors from "../../constants/colors"; | ||
| 4 | + | ||
| 5 | +function GuessLogItem({ roundNumber, guess }) { | ||
| 6 | + return ( | ||
| 7 | + <View style={styles.listItem}> | ||
| 8 | + <Text style={styles.itemText}>#{roundNumber}</Text> | ||
| 9 | + <Text style={styles.itemText}>Opponent's Guess: {guess}</Text> | ||
| 10 | + </View> | ||
| 11 | + ); | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +export default GuessLogItem; | ||
| 15 | + | ||
| 16 | +const styles = StyleSheet.create({ | ||
| 17 | + listItem: { | ||
| 18 | + borderColor: Colors.primary800, | ||
| 19 | + borderWidth: 1, | ||
| 20 | + borderRadius: 40, | ||
| 21 | + padding: 12, | ||
| 22 | + marginVertical: 8, | ||
| 23 | + backgroundColor: Colors.accent500, | ||
| 24 | + flexDirection: "row", | ||
| 25 | + justifyContent: "space-between", | ||
| 26 | + width: "100%", | ||
| 27 | + elevation: 4, | ||
| 28 | + shadowColor: "black", | ||
| 29 | + shadowOffset: { width: 0, height: 0 }, | ||
| 30 | + shadowOpacity: 0.25, | ||
| 31 | + shadowRadius: 3, | ||
| 32 | + }, | ||
| 33 | + itemText: { | ||
| 34 | + fontFamily: "open-sans", | ||
| 35 | + }, | ||
| 36 | +}); |
components/game/NumberContainer.js
0 → 100644
| 1 | +import { View, Text, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +import Colors from "../../constants/colors"; | ||
| 4 | + | ||
| 5 | +function NumberContainer({ children }) { | ||
| 6 | + return ( | ||
| 7 | + <View style={styles.container}> | ||
| 8 | + <Text style={styles.numberText}>{children}</Text> | ||
| 9 | + </View> | ||
| 10 | + ); | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +export default NumberContainer; | ||
| 14 | + | ||
| 15 | +const styles = StyleSheet.create({ | ||
| 16 | + container: { | ||
| 17 | + borderWidth: 4, | ||
| 18 | + borderColor: Colors.accent500, | ||
| 19 | + padding: 24, | ||
| 20 | + margin: 24, | ||
| 21 | + borderRadius: 8, | ||
| 22 | + alignItems: "center", | ||
| 23 | + justifyContent: "center", | ||
| 24 | + }, | ||
| 25 | + numberText: { | ||
| 26 | + color: Colors.accent500, | ||
| 27 | + fontSize: 36, | ||
| 28 | + // fontWeight: 'bold', | ||
| 29 | + fontFamily: "open-sans-bold", | ||
| 30 | + }, | ||
| 31 | +}); |
components/ui/Card.js
0 → 100644
| 1 | +import { View, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +import Colors from "../../constants/colors"; | ||
| 4 | + | ||
| 5 | +function Card({ children }) { | ||
| 6 | + return <View style={styles.card}>{children}</View>; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export default Card; | ||
| 10 | + | ||
| 11 | +const styles = StyleSheet.create({ | ||
| 12 | + card: { | ||
| 13 | + justifyContent: "center", | ||
| 14 | + alignItems: "center", | ||
| 15 | + marginTop: 36, | ||
| 16 | + marginHorizontal: 24, | ||
| 17 | + padding: 16, | ||
| 18 | + backgroundColor: Colors.primary800, | ||
| 19 | + borderRadius: 8, | ||
| 20 | + elevation: 4, | ||
| 21 | + shadowColor: "black", | ||
| 22 | + shadowOffset: { width: 0, height: 2 }, | ||
| 23 | + shadowRadius: 6, | ||
| 24 | + shadowOpacity: 0.25, | ||
| 25 | + }, | ||
| 26 | +}); |
components/ui/InstructionText.js
0 → 100644
| 1 | +import { Text, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +import Colors from "../../constants/colors"; | ||
| 4 | + | ||
| 5 | +function InstructionText({ children, style }) { | ||
| 6 | + return <Text style={[styles.instructionText, style]}>{children}</Text>; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export default InstructionText; | ||
| 10 | + | ||
| 11 | +const styles = StyleSheet.create({ | ||
| 12 | + instructionText: { | ||
| 13 | + fontFamily: "open-sans", | ||
| 14 | + color: Colors.accent500, | ||
| 15 | + fontSize: 24, | ||
| 16 | + }, | ||
| 17 | +}); |
components/ui/PrimaryButton.js
0 → 100644
| 1 | +import { View, Text, Pressable, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +import Colors from "../../constants/colors"; | ||
| 4 | + | ||
| 5 | +function PrimaryButton({ children, onPress }) { | ||
| 6 | + return ( | ||
| 7 | + <View style={styles.buttonOuterContainer}> | ||
| 8 | + <Pressable | ||
| 9 | + style={({ pressed }) => | ||
| 10 | + pressed | ||
| 11 | + ? [styles.buttonInnerContainer, styles.pressed] | ||
| 12 | + : styles.buttonInnerContainer | ||
| 13 | + } | ||
| 14 | + onPress={onPress} | ||
| 15 | + android_ripple={{ color: Colors.primary600 }} | ||
| 16 | + > | ||
| 17 | + <Text style={styles.buttonText}>{children}</Text> | ||
| 18 | + </Pressable> | ||
| 19 | + </View> | ||
| 20 | + ); | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +export default PrimaryButton; | ||
| 24 | + | ||
| 25 | +const styles = StyleSheet.create({ | ||
| 26 | + buttonOuterContainer: { | ||
| 27 | + borderRadius: 28, | ||
| 28 | + margin: 4, | ||
| 29 | + overflow: "hidden", | ||
| 30 | + }, | ||
| 31 | + buttonInnerContainer: { | ||
| 32 | + backgroundColor: Colors.primary500, | ||
| 33 | + paddingVertical: 8, | ||
| 34 | + paddingHorizontal: 16, | ||
| 35 | + elevation: 2, | ||
| 36 | + }, | ||
| 37 | + buttonText: { | ||
| 38 | + color: "white", | ||
| 39 | + textAlign: "center", | ||
| 40 | + }, | ||
| 41 | + pressed: { | ||
| 42 | + opacity: 0.75, | ||
| 43 | + }, | ||
| 44 | +}); |
components/ui/Title.js
0 → 100644
| 1 | +import { Text, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +function Title({ children }) { | ||
| 4 | + return <Text style={styles.title}>{children}</Text>; | ||
| 5 | +} | ||
| 6 | + | ||
| 7 | +export default Title; | ||
| 8 | + | ||
| 9 | +const styles = StyleSheet.create({ | ||
| 10 | + title: { | ||
| 11 | + fontFamily: "open-sans-bold", | ||
| 12 | + fontSize: 24, | ||
| 13 | + // fontWeight: 'bold', | ||
| 14 | + color: "white", | ||
| 15 | + textAlign: "center", | ||
| 16 | + borderWidth: 2, | ||
| 17 | + borderColor: "white", | ||
| 18 | + padding: 12, | ||
| 19 | + }, | ||
| 20 | +}); |
constants/colors.js
0 → 100644
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "numgame", | ||
| 3 | + "version": "1.0.0", | ||
| 4 | + "main": "node_modules/expo/AppEntry.js", | ||
| 5 | + "scripts": { | ||
| 6 | + "start": "expo start", | ||
| 7 | + "android": "expo start --android", | ||
| 8 | + "ios": "expo start --ios", | ||
| 9 | + "web": "expo start --web", | ||
| 10 | + "eject": "expo eject" | ||
| 11 | + }, | ||
| 12 | + "dependencies": { | ||
| 13 | + "expo": "~45.0.0", | ||
| 14 | + "expo-app-loading": "~2.0.0", | ||
| 15 | + "expo-font": "~10.1.0", | ||
| 16 | + "expo-linear-gradient": "~11.3.0", | ||
| 17 | + "expo-status-bar": "~1.3.0", | ||
| 18 | + "react": "17.0.2", | ||
| 19 | + "react-dom": "17.0.2", | ||
| 20 | + "react-native": "0.68.2", | ||
| 21 | + "react-native-web": "0.17.7" | ||
| 22 | + }, | ||
| 23 | + "devDependencies": { | ||
| 24 | + "@babel/core": "^7.12.9" | ||
| 25 | + }, | ||
| 26 | + "private": true | ||
| 27 | +} |
screens/GameOverScreen.js
0 → 100644
| 1 | +import { View, Image, Text, StyleSheet } from "react-native"; | ||
| 2 | + | ||
| 3 | +import Title from "../components/ui/Title"; | ||
| 4 | +import PrimaryButton from "../components/ui/PrimaryButton"; | ||
| 5 | +import Colors from "../constants/colors"; | ||
| 6 | + | ||
| 7 | +function GameOverScreen({ roundsNumber, userNumber, onStartNewGame }) { | ||
| 8 | + return ( | ||
| 9 | + <View style={styles.rootContainer}> | ||
| 10 | + <Title>GAME OVER!</Title> | ||
| 11 | + <View style={styles.imageContainer}> | ||
| 12 | + <Image | ||
| 13 | + style={styles.image} | ||
| 14 | + source={require("../assets/images/success.png")} | ||
| 15 | + /> | ||
| 16 | + </View> | ||
| 17 | + <Text style={styles.summaryText}> | ||
| 18 | + Your phone needed <Text style={styles.highlight}>{roundsNumber}</Text>{" "} | ||
| 19 | + rounds to guess the number{" "} | ||
| 20 | + <Text style={styles.highlight}>{userNumber}</Text>. | ||
| 21 | + </Text> | ||
| 22 | + <PrimaryButton onPress={onStartNewGame}>Start New Game</PrimaryButton> | ||
| 23 | + </View> | ||
| 24 | + ); | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +export default GameOverScreen; | ||
| 28 | + | ||
| 29 | +const styles = StyleSheet.create({ | ||
| 30 | + rootContainer: { | ||
| 31 | + flex: 1, | ||
| 32 | + padding: 24, | ||
| 33 | + justifyContent: "center", | ||
| 34 | + alignItems: "center", | ||
| 35 | + }, | ||
| 36 | + imageContainer: { | ||
| 37 | + width: 300, | ||
| 38 | + height: 300, | ||
| 39 | + borderRadius: 150, | ||
| 40 | + borderWidth: 3, | ||
| 41 | + borderColor: Colors.primary800, | ||
| 42 | + overflow: "hidden", | ||
| 43 | + margin: 36, | ||
| 44 | + }, | ||
| 45 | + image: { | ||
| 46 | + width: "100%", | ||
| 47 | + height: "100%", | ||
| 48 | + }, | ||
| 49 | + summaryText: { | ||
| 50 | + fontFamily: "open-sans", | ||
| 51 | + fontSize: 24, | ||
| 52 | + textAlign: "center", | ||
| 53 | + marginBottom: 24, | ||
| 54 | + }, | ||
| 55 | + highlight: { | ||
| 56 | + fontFamily: "open-sans-bold", | ||
| 57 | + color: Colors.primary500, | ||
| 58 | + }, | ||
| 59 | +}); |
screens/GameScreen.js
0 → 100644
| 1 | +import { useState, useEffect } from "react"; | ||
| 2 | +import { View, StyleSheet, Alert, Text, FlatList } from "react-native"; | ||
| 3 | +import { Ionicons } from "@expo/vector-icons"; | ||
| 4 | + | ||
| 5 | +import NumberContainer from "../components/game/NumberContainer"; | ||
| 6 | +import Card from "../components/ui/Card"; | ||
| 7 | +import InstructionText from "../components/ui/InstructionText"; | ||
| 8 | +import PrimaryButton from "../components/ui/PrimaryButton"; | ||
| 9 | +import Title from "../components/ui/Title"; | ||
| 10 | +import GuessLogItem from "../components/game/GuessLogItem"; | ||
| 11 | + | ||
| 12 | +function generateRandomBetween(min, max, exclude) { | ||
| 13 | + const rndNum = Math.floor(Math.random() * (max - min)) + min; | ||
| 14 | + | ||
| 15 | + if (rndNum === exclude) { | ||
| 16 | + return generateRandomBetween(min, max, exclude); | ||
| 17 | + } else { | ||
| 18 | + return rndNum; | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +let minBoundary = 1; | ||
| 23 | +let maxBoundary = 100; | ||
| 24 | + | ||
| 25 | +function GameScreen({ userNumber, onGameOver }) { | ||
| 26 | + const initialGuess = generateRandomBetween(1, 100, userNumber); | ||
| 27 | + const [currentGuess, setCurrentGuess] = useState(initialGuess); | ||
| 28 | + const [guessRounds, setGuessRounds] = useState([initialGuess]); | ||
| 29 | + | ||
| 30 | + useEffect(() => { | ||
| 31 | + if (currentGuess === userNumber) { | ||
| 32 | + onGameOver(guessRounds.length); | ||
| 33 | + } | ||
| 34 | + }, [currentGuess, userNumber, onGameOver]); | ||
| 35 | + | ||
| 36 | + useEffect(() => { | ||
| 37 | + minBoundary = 1; | ||
| 38 | + maxBoundary = 100; | ||
| 39 | + }, []); | ||
| 40 | + | ||
| 41 | + function nextGuessHandler(direction) { | ||
| 42 | + // direction => 'lower', 'greater' | ||
| 43 | + if ( | ||
| 44 | + (direction === "lower" && currentGuess < userNumber) || | ||
| 45 | + (direction === "greater" && currentGuess > userNumber) | ||
| 46 | + ) { | ||
| 47 | + Alert.alert("Don't lie!", "You know that this is wrong...", [ | ||
| 48 | + { text: "Sorry!", style: "cancel" }, | ||
| 49 | + ]); | ||
| 50 | + return; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + if (direction === "lower") { | ||
| 54 | + maxBoundary = currentGuess; | ||
| 55 | + } else { | ||
| 56 | + minBoundary = currentGuess + 1; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + const newRndNumber = generateRandomBetween( | ||
| 60 | + minBoundary, | ||
| 61 | + maxBoundary, | ||
| 62 | + currentGuess | ||
| 63 | + ); | ||
| 64 | + setCurrentGuess(newRndNumber); | ||
| 65 | + setGuessRounds((prevGuessRounds) => [newRndNumber, ...prevGuessRounds]); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + const guessRoundsListLength = guessRounds.length; | ||
| 69 | + | ||
| 70 | + return ( | ||
| 71 | + <View style={styles.screen}> | ||
| 72 | + <Title>Opponent's Guess</Title> | ||
| 73 | + <NumberContainer>{currentGuess}</NumberContainer> | ||
| 74 | + <Card> | ||
| 75 | + <InstructionText style={styles.instructionText}> | ||
| 76 | + Higher or lower? | ||
| 77 | + </InstructionText> | ||
| 78 | + <View style={styles.buttonsContainer}> | ||
| 79 | + <View style={styles.buttonContainer}> | ||
| 80 | + <PrimaryButton onPress={nextGuessHandler.bind(this, "lower")}> | ||
| 81 | + <Ionicons name="md-remove" size={24} color="white" /> | ||
| 82 | + </PrimaryButton> | ||
| 83 | + </View> | ||
| 84 | + <View style={styles.buttonContainer}> | ||
| 85 | + <PrimaryButton onPress={nextGuessHandler.bind(this, "greater")}> | ||
| 86 | + <Ionicons name="md-add" size={24} color="white" /> | ||
| 87 | + </PrimaryButton> | ||
| 88 | + </View> | ||
| 89 | + </View> | ||
| 90 | + </Card> | ||
| 91 | + <View style={styles.listContainer}> | ||
| 92 | + {/* {guessRounds.map(guessRound => <Text key={guessRound}>{guessRound}</Text>)} */} | ||
| 93 | + <FlatList | ||
| 94 | + data={guessRounds} | ||
| 95 | + renderItem={(itemData) => ( | ||
| 96 | + <GuessLogItem | ||
| 97 | + roundNumber={guessRoundsListLength - itemData.index} | ||
| 98 | + guess={itemData.item} | ||
| 99 | + /> | ||
| 100 | + )} | ||
| 101 | + keyExtractor={(item) => item} | ||
| 102 | + /> | ||
| 103 | + </View> | ||
| 104 | + </View> | ||
| 105 | + ); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +export default GameScreen; | ||
| 109 | + | ||
| 110 | +const styles = StyleSheet.create({ | ||
| 111 | + screen: { | ||
| 112 | + flex: 1, | ||
| 113 | + padding: 24, | ||
| 114 | + }, | ||
| 115 | + instructionText: { | ||
| 116 | + marginBottom: 12, | ||
| 117 | + }, | ||
| 118 | + buttonsContainer: { | ||
| 119 | + flexDirection: "row", | ||
| 120 | + }, | ||
| 121 | + buttonContainer: { | ||
| 122 | + flex: 1, | ||
| 123 | + }, | ||
| 124 | + listContainer: { | ||
| 125 | + flex: 1, | ||
| 126 | + padding: 16, | ||
| 127 | + }, | ||
| 128 | +}); |
screens/StartGameScreen.js
0 → 100644
| 1 | +import { useState } from "react"; | ||
| 2 | +import { TextInput, View, StyleSheet, Alert } from "react-native"; | ||
| 3 | + | ||
| 4 | +import PrimaryButton from "../components/ui/PrimaryButton"; | ||
| 5 | +import Title from "../components/ui/Title"; | ||
| 6 | +import Colors from "../constants/colors"; | ||
| 7 | +import Card from "../components/ui/Card"; | ||
| 8 | +import InstructionText from "../components/ui/InstructionText"; | ||
| 9 | + | ||
| 10 | +function StartGameScreen({ onPickNumber }) { | ||
| 11 | + const [enteredNumber, setEnteredNumber] = useState(""); | ||
| 12 | + | ||
| 13 | + function numberInputHandler(enteredText) { | ||
| 14 | + setEnteredNumber(enteredText); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + function resetInputHandler() { | ||
| 18 | + setEnteredNumber(""); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + function confirmInputHandler() { | ||
| 22 | + const chosenNumber = parseInt(enteredNumber); | ||
| 23 | + | ||
| 24 | + if (isNaN(chosenNumber) || chosenNumber <= 0 || chosenNumber > 99) { | ||
| 25 | + Alert.alert( | ||
| 26 | + "Invalid number!", | ||
| 27 | + "Number has to be a number between 1 and 99.", | ||
| 28 | + [{ text: "Okay", style: "destructive", onPress: resetInputHandler }] | ||
| 29 | + ); | ||
| 30 | + return; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + onPickNumber(chosenNumber); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + return ( | ||
| 37 | + <View style={styles.rootContainer}> | ||
| 38 | + <Title>Guess My Number</Title> | ||
| 39 | + <Card> | ||
| 40 | + <InstructionText>Enter a Number</InstructionText> | ||
| 41 | + <TextInput | ||
| 42 | + style={styles.numberInput} | ||
| 43 | + maxLength={2} | ||
| 44 | + keyboardType="number-pad" | ||
| 45 | + autoCapitalize="none" | ||
| 46 | + autoCorrect={false} | ||
| 47 | + onChangeText={numberInputHandler} | ||
| 48 | + value={enteredNumber} | ||
| 49 | + /> | ||
| 50 | + <View style={styles.buttonsContainer}> | ||
| 51 | + <View style={styles.buttonContainer}> | ||
| 52 | + <PrimaryButton onPress={resetInputHandler}>Reset</PrimaryButton> | ||
| 53 | + </View> | ||
| 54 | + <View style={styles.buttonContainer}> | ||
| 55 | + <PrimaryButton onPress={confirmInputHandler}>Confirm</PrimaryButton> | ||
| 56 | + </View> | ||
| 57 | + </View> | ||
| 58 | + </Card> | ||
| 59 | + </View> | ||
| 60 | + ); | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +export default StartGameScreen; | ||
| 64 | + | ||
| 65 | +const styles = StyleSheet.create({ | ||
| 66 | + rootContainer: { | ||
| 67 | + flex: 1, | ||
| 68 | + marginTop: 100, | ||
| 69 | + alignItems: "center", | ||
| 70 | + }, | ||
| 71 | + numberInput: { | ||
| 72 | + height: 50, | ||
| 73 | + width: 50, | ||
| 74 | + fontSize: 32, | ||
| 75 | + borderBottomColor: Colors.accent500, | ||
| 76 | + borderBottomWidth: 2, | ||
| 77 | + color: Colors.accent500, | ||
| 78 | + marginVertical: 8, | ||
| 79 | + fontWeight: "bold", | ||
| 80 | + textAlign: "center", | ||
| 81 | + }, | ||
| 82 | + buttonsContainer: { | ||
| 83 | + flexDirection: "row", | ||
| 84 | + }, | ||
| 85 | + buttonContainer: { | ||
| 86 | + flex: 1, | ||
| 87 | + }, | ||
| 88 | +}); |
yarn.lock
0 → 100644
This diff could not be displayed because it is too large.
-
Please register or login to post a comment