Skip to content

Commit d06e2e4

Browse files
feat: Add Main menu and game over screen
1 parent 619011c commit d06e2e4

File tree

16 files changed

+400
-29
lines changed

16 files changed

+400
-29
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules/**/*
22
.expo/*
33
npm-debug.*
4+
.DS_Store
45
*.jks
56
*.p12
67
*.key

App.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { Provider } from 'react-redux';
33
import { createStore, applyMiddleware } from 'redux';
44
import ReduxThunk from 'redux-thunk';
55
import reducers from './src/reducers';
6-
import TriviaScreen from './src/components/screens/TriviaScreen';
6+
import Router from './src/Router';
77

88
export default class App extends React.Component {
99

1010
render() {
1111
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
1212
return (
1313
<Provider store={store}>
14-
<TriviaScreen />
14+
<Router />
1515
</Provider>
1616
);
1717
}

assets/fonts/BadaboomBB_Reg.ttf

34.3 KB
Binary file not shown.

assets/fonts/GrinchedRegular.otf

29 KB
Binary file not shown.

assets/fonts/SaucerBB.ttf

575 KB
Binary file not shown.
391 KB
Loading

src/Router.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { Scene, Router } from 'react-native-router-flux';
3+
import TriviaGame from './components/screens/TriviaGame';
4+
import GameOver from './components/screens/GameOver';
5+
import MainMenu from './components/screens/MainMenu';
6+
7+
const RouterComponent = () => {
8+
return (
9+
<Router>
10+
<Scene key="main" hideNavBar={true} initial>
11+
<Scene key="mainMenu" component={MainMenu} />
12+
<Scene key="triviaGame" component={TriviaGame} />
13+
<Scene key="gameOver" component={GameOver} />
14+
</Scene>
15+
</Router>
16+
);
17+
};
18+
19+
export default RouterComponent;

src/actions/TriviaActions.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { Actions } from 'react-native-router-flux';
12
import { AllHtmlEntities as entities } from 'html-entities';
23
import {
4+
TRIVIA_START_GAME,
35
TRIVIA_FETCH_SUCCESS,
46
TRIVIA_FETCH_ERROR,
57
TRIVIA_NEXT_QUESTION,
@@ -31,9 +33,10 @@ export const triviaFetch = () => {
3133
}
3234
}
3335
);
36+
// console.log(formatedQuestions);
3437
dispatch({ type: TRIVIA_FETCH_SUCCESS, payload: formatedQuestions });
35-
}).catch(function () {
36-
dispatch({ type: TRIVIA_FETCH_ERROR });
38+
}).catch(function (error) {
39+
dispatch({ type: TRIVIA_FETCH_ERROR, payload: error });
3740
});
3841
}
3942
};
@@ -56,13 +59,25 @@ export const nextQuestion = (currentAnswer, currentQuestionIndex, questions, tot
5659
totalScore += 1;
5760
}
5861

59-
console.log(`Total Score: ${totalScore}`);
60-
6162
if (nextIndex < totalQuestionsSize) {
6263
dispatch({ type: TRIVIA_NEXT_QUESTION, payload: { currentQuestionIndex: nextIndex, totalScore } });
6364
}
6465
else {
6566
dispatch({ type: TRIVIA_GAME_OVER, payload: totalScore });
67+
// Call game over screen and disable back action
68+
Actions.gameOver({ type: 'reset' });
6669
}
6770
}
6871
};
72+
73+
/**
74+
* @description Start Trivia Game.
75+
*/
76+
export const startGame = () => {
77+
78+
return (dispatch) => {
79+
dispatch({ type: TRIVIA_START_GAME });
80+
// Call start game and disable back action
81+
Actions.triviaGame({ type: 'reset' });
82+
}
83+
};

src/actions/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export const TRIVIA_START_GAME = 'trivia_start_game';
12
export const TRIVIA_FETCH_SUCCESS = 'trivia_fetch_success';
23
export const TRIVIA_FETCH_ERROR = 'trivia_fetch_error';
34
export const TRIVIA_NEXT_QUESTION = 'trivia_next_question';

src/components/Button.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { Text, TouchableOpacity } from 'react-native';
3+
4+
const Button = ({ onPress, children }) => {
5+
const { buttonStyle, textStyle } = styles;
6+
7+
return (
8+
<TouchableOpacity onPress={onPress} style={buttonStyle}>
9+
<Text style={textStyle}>
10+
{children}
11+
</Text>
12+
</TouchableOpacity>
13+
);
14+
};
15+
16+
const styles = {
17+
textStyle: {
18+
flex: 1,
19+
padding: 12,
20+
color: '#ffffff',
21+
fontSize: 24,
22+
fontWeight:'normal',
23+
textAlign: 'center',
24+
textShadowColor:'#000000',
25+
textShadowOffset:{width: 2, height: 2},
26+
textShadowRadius:0,
27+
},
28+
buttonStyle: {
29+
height: 60,
30+
alignSelf: 'stretch',
31+
minHeight: 32,
32+
margin: 10,
33+
backgroundColor: 'rgba(64, 64, 255, 0.8)',
34+
borderRadius: 8
35+
}
36+
};
37+
38+
export default Button;

src/components/Question.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const defaultProps = {
3232
*/
3333

3434
/**
35-
* @description Represents a individual Quiz Question.
35+
* @description Individual quiz question with options.
3636
* @constructor
3737
* @param {Object} props - The props that were defined by the caller of this component.
3838
* @param {string} props.category - The id of the book.
@@ -63,7 +63,7 @@ function Question(props) {
6363
)}
6464
keyExtractor={(item, index) => `${index}-${item}`}
6565
onPressItem={props.onItemSelected}
66-
scrollEnabled={false}
66+
scrollEnabled={true}
6767
/>
6868
</View>
6969
);

src/components/QuestionOptionItem.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const propTypes = {
2121
*/
2222

2323
/**
24-
* @description Represents a individual Quiz Option.
24+
* @description Individual quiz question option component.
2525
* @constructor
2626
* @param {Object} props - The props that were defined by the caller of this component.
2727
* @param {string} props.optionDescription - The text of the option.
@@ -30,8 +30,6 @@ const propTypes = {
3030
class QuestionOptionItem extends PureComponent {
3131

3232
_onPress = () => {
33-
console.log('QuestionOptionItem Pressed : ');
34-
console.log(this.props.optionDescription);
3533
this.props.onPressItem(this.props.optionDescription);
3634
};
3735

src/components/screens/GameOver.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import React from 'react';
2+
import {
3+
View,
4+
Text,
5+
StyleSheet,
6+
ImageBackground,
7+
TouchableHighlight
8+
} from 'react-native';
9+
import { connect } from 'react-redux';
10+
import { Font } from 'expo';
11+
import { startGame } from '../../actions';
12+
import Button from '../Button';
13+
14+
15+
// Game background image
16+
const BACKGROUND_IMAGE = require('../../../assets/images/game_background.png');
17+
const GAMEOVER_TITLE_FONT = require('../../../assets/fonts/GrinchedRegular.otf');
18+
const GAMEOVER_FONT = require('../../../assets/fonts/BadaboomBB_Reg.ttf');
19+
20+
/**
21+
* @description Game Over screen.
22+
* @constructor
23+
* @param {Object} props - The props that were defined by the caller of this component.
24+
* @param {function} props.startGame - Callback executed when user clicks play again button.
25+
*/
26+
class GameOver extends React.Component {
27+
constructor(props){
28+
super(props);
29+
/**
30+
* @typedef {Object} ComponentState
31+
* @property {Object[]} fontLoaded - Indicates whether custom fonts already loaded.
32+
*/
33+
34+
/** @type {ComponentState} */
35+
this.state = {
36+
fontLoaded: false
37+
};
38+
}
39+
40+
async componentDidMount() {
41+
await Font.loadAsync({
42+
'game-over': GAMEOVER_FONT,
43+
'game-over-title': GAMEOVER_TITLE_FONT,
44+
});
45+
this.setState({ fontLoaded: true });
46+
}
47+
48+
render() {
49+
50+
const { elapsedTime, totalQuestionsNumber, totalScore } = this.props;
51+
// Change score color based on value
52+
const scorePercent = totalScore / totalQuestionsNumber;
53+
const scoreColor = (scorePercent >= 0.8) ? '#14AB00': (scorePercent >= 0.5) ? '#D9E100' : '#FF2020';
54+
return (
55+
<View style={styles.container}>
56+
<ImageBackground
57+
style={styles.imageBackground}
58+
source={BACKGROUND_IMAGE}
59+
resizeMode="cover"
60+
>
61+
{(this.state.fontLoaded) &&
62+
<View style={styles.gameOverData}>
63+
<Text style={styles.gameOverTitle}>GAME OVER</Text>
64+
<Text style={[styles.gameScoreText, { color: scoreColor }]}
65+
>Total Score: {totalScore} of {totalQuestionsNumber}</Text>
66+
<Text style={styles.gameTimeText}>Elapsed Time: {elapsedTime} seconds</Text>
67+
<Button onPress={() => this.props.startGame()}>
68+
Play Again
69+
</Button>
70+
</View>
71+
}
72+
</ImageBackground>
73+
</View>
74+
)
75+
}
76+
}
77+
78+
/**
79+
* GameOver component StyleSheet.
80+
*/
81+
const styles = StyleSheet.create({
82+
container: {
83+
flex: 1,
84+
},
85+
button: {
86+
alignItems: 'center',
87+
backgroundColor: 'rgba(64, 64, 64,0.8)',
88+
borderRadius: 8,
89+
padding: 10,
90+
margin: 20
91+
},
92+
imageBackground: {
93+
flex: 1,
94+
height: '100%',
95+
width: '100%',
96+
justifyContent: 'center',
97+
alignItems: 'center'
98+
},
99+
gameOverData: {
100+
padding: 16,
101+
marginTop: 32,
102+
marginBottom: 32,
103+
alignSelf: 'stretch',
104+
alignItems: 'center',
105+
borderWidth: 2,
106+
borderRadius: 8,
107+
borderColor: '#ffffff',
108+
justifyContent: 'center',
109+
backgroundColor: 'rgba(255, 255, 255, 0.9)',
110+
},
111+
gameOverTitle: {
112+
fontFamily: "game-over-title",
113+
color: '#000000',
114+
textShadowOffset: {width: -1, height: 1},
115+
textShadowRadius: 10,
116+
fontSize: 78
117+
},
118+
gameScoreText: {
119+
fontFamily: 'game-over',
120+
textShadowColor: 'black',
121+
textShadowOffset: {width: -1, height: 1},
122+
textShadowRadius: 10,
123+
fontSize: 36,
124+
marginTop: 10,
125+
marginBottom: 10,
126+
},
127+
gameTimeText: {
128+
fontFamily: 'game-over',
129+
fontSize: 36,
130+
textShadowOffset: {width: -1, height: 1},
131+
textShadowRadius: 10,
132+
color: '#8f61f9',
133+
marginTop: 10,
134+
marginBottom: 10,
135+
},
136+
});
137+
138+
const mapStateToProps = ({ trivia }) => {
139+
const { totalScore, startTime, endTime, questions } = trivia;
140+
141+
// Elapsed time in seconds
142+
const elapsedTime = Math.round((endTime - startTime) / 1000);
143+
144+
const totalQuestionsNumber = questions.length;
145+
146+
return { elapsedTime, totalQuestionsNumber, totalScore };
147+
};
148+
149+
export default connect(mapStateToProps, { startGame })(GameOver);

0 commit comments

Comments
 (0)