Skip to content

Commit

Permalink
Merge pull request #7 from KartikWatts/feature-timer-countdown
Browse files Browse the repository at this point in the history
Enhancement: Game plays with Clock TimeOut
  • Loading branch information
KartikWatts authored Mar 15, 2022
2 parents 193cf5e + fd534cb commit a16893a
Show file tree
Hide file tree
Showing 17 changed files with 355 additions and 26 deletions.
13 changes: 5 additions & 8 deletions Native_Application/App.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import React from "react";
import tw from "twrnc";
import { View } from "react-native";
import { StatusBar } from "expo-status-bar";
import * as NavigationBar from "expo-navigation-bar";
import Game from "./containers/Game";
import { GameProvider } from "./contexts/GameContext";
import GameWrapper from "./components/GameWrapper";

NavigationBar.setBackgroundColorAsync("rgb(71, 85, 105)");
NavigationBar.setBehaviorAsync("overlay-swipe");
NavigationBar.setVisibilityAsync("hidden");

export default function App() {
return (
<View
style={tw`w-full h-full bg-slate-500 flex justify-center items-center flex-col`}
>
<Game />
<GameProvider>
<GameWrapper />
<StatusBar hidden={true} />
</View>
</GameProvider>
);
}
11 changes: 11 additions & 0 deletions Native_Application/assets/data/Interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,14 @@ export interface LayoutData {
x: number;
y: number;
}

export interface GameState {
isGameOn: boolean;
}

export enum GameActionType {
TOGGLE = "toggle",
}
export interface GameAction {
type: GameActionType;
}
5 changes: 5 additions & 0 deletions Native_Application/assets/data/Types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ let DEFAULT = 1,
ALREADY = 4,
WRONG = 5;

export type GameContextType = {
isGameOn: boolean;
toggleGameState: () => void;
};

export { DEFAULT, SELECTED, CORRECT, ALREADY, WRONG };
Binary file not shown.
84 changes: 84 additions & 0 deletions Native_Application/components/CountDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useContext, useEffect, useState } from "react";
import { Audio } from "expo-av";
import tw from "twrnc";
import { Text, View } from "react-native";
import { GameContext } from "../contexts/GameContext";

function CountDown() {
const [countDown, setCountDown] = useState("120");
const [countDownTime, setCountDownTime] = useState(0);
const [isFinalSeconds, setIsFinalSeconds] = useState(false);

const gameContext = useContext(GameContext);

const [countDownTimerSound, setCountDownTimerSound] =
useState<Audio.Sound>();

useEffect(() => {
(async () => {
const { sound } = await Audio.Sound.createAsync(
require("../assets/sounds/countdown_time.mp3")
);
setCountDownTimerSound(sound);
})();
}, []);

useEffect(() => {
// TODO:: TO BE REPLACED BY TIME OF THE GAME ON THE SERVER IF EXISTS
let cTime = new Date(+new Date() + 60000 * 2).getTime(); //Adding two minutes to current time
if (!countDownTime) {
setCountDownTime(cTime);
return;
}
let seconds = 120;
let countDownInterval = setInterval(() => {
let now = new Date(+new Date() - 1000 * 2).getTime();
let difference = countDownTime - now + 1;
if (difference < 0) difference = 0;
let minutes = Math.floor(
(difference % (1000 * 60 * 60)) / (1000 * 60)
);
seconds = Math.floor(
minutes * 60 + (difference % (1000 * 60)) / 1000
);
let secondsStr = seconds.toString();
setCountDown(secondsStr);
if (seconds <= 10) {
setIsFinalSeconds(true);
}
if (seconds == 0) {
if (countDownTimerSound) countDownTimerSound.stopAsync();
clearInterval(countDownInterval);
setTimeout(() => {
if (gameContext) gameContext.toggleGameState();
}, 10);
}
}, 1000);
}, [countDownTime]);

useEffect(() => {
if (countDownTimerSound) {
if (countDown == "10") {
countDownTimerSound.playAsync();
}
if (countDown == "0") {
countDownTimerSound.stopAsync();
}
}
}, [countDown]);

return (
<View style={tw`w-30 px-2`}>
<Text
style={[
isFinalSeconds ? tw`text-red-400` : tw`text-slate-300`,
tw`text-2xl`,
]}
>
{countDown}
</Text>
</View>
);
}

export default CountDown;
34 changes: 34 additions & 0 deletions Native_Application/components/GameWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useContext } from "react";
import tw from "twrnc";
import { Text, TouchableOpacity, View } from "react-native";
import Game from "../containers/Game";
import { GameContext } from "../contexts/GameContext";

export default function GameWrapper() {
const gameContext = useContext(GameContext);

return (
<View
style={tw`w-full h-full bg-slate-500 flex justify-center items-center flex-col`}
>
{gameContext && gameContext.isGameOn ? (
<Game />
) : (
<View>
<TouchableOpacity
onPress={() => {
if (gameContext) {
gameContext.toggleGameState();
}
}}
style={tw`bg-yellow-300 p-8 border-8 hover:bg-amber-300 border-blue-400 rounded-xl`}
>
<Text style={tw`text-xl font-bold text-blue-600`}>
Start Game
</Text>
</TouchableOpacity>
</View>
)}
</View>
);
}
16 changes: 11 additions & 5 deletions Native_Application/components/WordDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import { View, Text } from "react-native";
import tw from "twrnc";

import { WordProps } from "../assets/data/Types";
import CountDown from "./CountDown";

function WordDisplay(props: WordProps) {
return (
<View style={tw`h-8 w-full mb-8 flex flex-row items-center px-4`}>
<View style={tw`w-30 px-2`}>
<Text style={tw`text-slate-300 text-lg`}>
Score:{" "}
<Text style={tw`text-xl text-white`}>{props.score}</Text>
</Text>
<View>
<CountDown />
<View style={tw`w-30 px-2`}>
<Text style={tw`text-slate-300 text-lg`}>
Score:{" "}
<Text style={tw`text-xl text-white`}>
{props.score}
</Text>
</Text>
</View>
</View>
<View>
{props.word != "" && (
Expand Down
39 changes: 39 additions & 0 deletions Native_Application/contexts/GameContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { createContext, useReducer } from "react";
import {
GameAction,
GameActionType,
GameState,
} from "../assets/data/Interfaces";
import { GameContextType } from "../assets/data/Types";

const defaultState = { isGameOn: false };

const GameContext = createContext<GameContextType | null>(null);

const gameReducer = (state: GameState, action: GameAction) => {
const { type } = action;
switch (type) {
case GameActionType.TOGGLE: {
return { ...state, isGameOn: !state.isGameOn };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
};

const GameProvider: React.FC = (props) => {
const [state, dispatch] = useReducer(gameReducer, defaultState);
const toggleGameState = () => {
dispatch({ type: GameActionType.TOGGLE });
};
const value = { isGameOn: state.isGameOn, toggleGameState };

return (
<GameContext.Provider value={value}>
{props.children}
</GameContext.Provider>
);
};

export { GameContext, GameProvider };
22 changes: 20 additions & 2 deletions Web_Application/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import React from "react";
import React, { useContext } from "react";
import Game from "./containers/Game";
import { GameContext } from "./contexts/GameContext";

function App() {
const gameContext = useContext(GameContext);

return (
<div className="w-screen h-screen bg-slate-600 flex justify-center items-center flex-col">
<Game />
{gameContext && gameContext.isGameOn ? (
<Game />
) : (
<div>
<button
className="bg-yellow-300 p-8 border-4 hover:bg-amber-300 border-blue-400 rounded-lg text-blue-600 transition-all text-2xl drop-shadow-xl hover:drop-shadow-2xl font-bold hover:scale-105"
onClick={() => {
if (gameContext) {
gameContext.toggleGameState();
}
}}
>
Start Game
</button>
</div>
)}
</div>
);
}
Expand Down
11 changes: 11 additions & 0 deletions Web_Application/src/assets/data/Interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,14 @@ export interface Events {
id: number;
order: number;
}

export interface GameState {
isGameOn: boolean;
}

export enum GameActionType {
TOGGLE = "toggle",
}
export interface GameAction {
type: GameActionType;
}
5 changes: 5 additions & 0 deletions Web_Application/src/assets/data/Types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ export type WordProps = {
word: string;
score: number;
};

export type GameContextType = {
isGameOn: boolean;
toggleGameState: () => void;
};
Binary file not shown.
70 changes: 70 additions & 0 deletions Web_Application/src/components/CountDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { useContext, useEffect, useState } from "react";
import countdown_timer from "../assets/sounds/countdown_time.mp3";
import useSound from "use-sound";
import { GameContext } from "../contexts/GameContext";

function CountDown() {
const [countDown, setCountDown] = useState("120");
const [countDownTime, setCountDownTime] = useState(0);
const [isFinalSeconds, setIsFinalSeconds] = useState(false);

const gameContext = useContext(GameContext);

const [playCountDownTimer, { stop: stopCountDownTimer }] =
useSound(countdown_timer);

useEffect(() => {
// TODO:: TO BE REPLACED BY TIME OF THE GAME ON THE SERVER IF EXISTS
let cTime = new Date(+new Date() + 60000 * 2).getTime(); //Adding two minutes to current time
if (!countDownTime) {
setCountDownTime(cTime);
return;
}
let seconds = 120;
let countDownInterval = setInterval(() => {
let now = new Date(+new Date() - 1000 * 2).getTime();
let difference = countDownTime - now + 1;
if (difference < 0) difference = 0;
let minutes = Math.floor(
(difference % (1000 * 60 * 60)) / (1000 * 60)
);
seconds = Math.floor(
minutes * 60 + (difference % (1000 * 60)) / 1000
);
let secondsStr = seconds.toString();
setCountDown(secondsStr);
if (seconds <= 10) {
setIsFinalSeconds(true);
}
if (seconds == 0) {
clearInterval(countDownInterval);
stopCountDownTimer();
setTimeout(() => {
if (gameContext) gameContext.toggleGameState();
}, 10);
}
}, 1000);
}, [countDownTime]);

useEffect(() => {
if (countDown == "10") {
playCountDownTimer();
}
if (countDown == "0") {
stopCountDownTimer();
}
}, [countDown]);

return (
<div
id="countdown_timer"
className={`${
isFinalSeconds ? "text-red-400" : "text-slate-300"
} text-2xl`}
>
{countDown}
</div>
);
}

export default CountDown;
11 changes: 9 additions & 2 deletions Web_Application/src/components/WordDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import React from "react";
import { WordProps } from "../assets/data/Types";
import CountDown from "./CountDown";

function WordDisplay(props: WordProps) {
return (
<div className="h-2 w-full mb-4 flex items-center">
<div className="w-1/4 text-slate-300 text-lg">
Score: <span className="text-xl text-white">{props.score}</span>
<div className="w-1/2 p-4 flex flex-col">
<CountDown />
<div className="text-slate-300 text-lg flex content-center">
Score:{" "}
<span className="text-2xl text-white pl-1">
{props.score}
</span>
</div>
</div>
{props.word != "" && (
<div className="bg-white px-2">
Expand Down
Loading

0 comments on commit a16893a

Please sign in to comment.