Skip to content

Commit

Permalink
Merge pull request #20 from yassinedoghri/development
Browse files Browse the repository at this point in the history
Update countdown component to be generic and fix minor bugs for v0.5.1
  • Loading branch information
yassinedoghri committed May 26, 2018
2 parents f378f8f + e2ed1eb commit 2dc93fe
Show file tree
Hide file tree
Showing 21 changed files with 169 additions and 76 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -77,7 +77,7 @@ $ yarn start
│ │ ...
│ ├── components/ # UI styled-components
│ ├── config/ # config files
│ ├── constants/ # constants (eg. actionType names)
│ ├── constants/ # constants (eg. actionType names, routes)
│ ├── containers/ # game containers
│ │ ├── HomeScreen/
│ │ ├── PlayScreen/
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "marvel-jarvig",
"version": "0.5.0",
"description": "Marvel J.A.R.V.I.G. (Just A Rather Very Interesting Game) is a game that lets you find and discover Marvel Comics characters based on their name, image and description!",
"version": "0.5.1",
"description": "Marvel JARVIG (Just A Rather Very Interesting Game) is a game that lets you find and discover Marvel Comics characters based on their name, image and description!",
"homepage": "https://yassinedoghri.github.io/marvel-jarvig/",
"repository": {
"type": "git",
Expand Down Expand Up @@ -69,6 +69,7 @@
"lint-staged": "^7.0.5",
"prettier": "1.12.1",
"react-test-renderer": "^16.3.2",
"redux-devtools-extension": "^2.13.2",
"stylelint": "^9.2.0",
"stylelint-config-standard": "^18.2.0",
"stylelint-config-styled-components": "^0.1.1",
Expand Down
6 changes: 6 additions & 0 deletions src/actions/GameActions.js
Expand Up @@ -6,6 +6,7 @@ import {
INIT_NEW_GAME,
NEXT_QUESTION,
PASS_QUESTION,
SAVE_GAME_TIME,
SELECT_CHARACTER,
SET_DIFFICULTY,
UPDATE_SETTINGS
Expand Down Expand Up @@ -59,6 +60,11 @@ export const endGame = () => ({
type: END_GAME
});

export const saveGameTime = time => ({
type: SAVE_GAME_TIME,
payload: time
});

export const callRequest = () => ({
type: API_CALL_REQUEST
});
4 changes: 2 additions & 2 deletions src/components/Logo/__tests__/__snapshots__/index.js.snap
@@ -1,10 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Logo Block adds mobileIcon modifier 1`] = `
exports[`Logo Block adds mobileicon modifier 1`] = `
<a
className="sc-bxivhb cZFXvN"
href="/"
mobileIcon="true"
mobileicon="true"
onClick={[Function]}
>
<img
Expand Down
4 changes: 2 additions & 2 deletions src/components/Logo/__tests__/index.js
Expand Up @@ -17,10 +17,10 @@ describe("Logo Block", () => {
expect(tree).toMatchSnapshot();
});

it("adds mobileIcon modifier", () => {
it("adds mobileicon modifier", () => {
const tree = renderWithTheme(
<MemoryRouter>
<Logo to="/" mobileIcon="true">
<Logo to="/" mobileicon="true">
<Logo.MarvelLogo />
<Logo.JarvigText />
<Logo.Icon />
Expand Down
4 changes: 2 additions & 2 deletions src/components/Logo/index.js
Expand Up @@ -27,11 +27,11 @@ const Logo = styled(Link)`
}
& ${MarvelLogo}, & ${JarvigText} {
display: ${props => (props.mobileIcon ? "none" : "block")};
display: ${props => (props.mobileicon ? "none" : "block")};
}
& ${Icon} {
display: ${props => (props.mobileIcon ? "block" : "none")};
display: ${props => (props.mobileicon ? "block" : "none")};
}
${media.desktop`
Expand Down
1 change: 1 addition & 0 deletions src/constants/actionTypes.js
Expand Up @@ -15,6 +15,7 @@ export const NEXT_QUESTION = "NEXT_QUESTION";
export const SELECT_CHARACTER = "SELECT_CHARACTER";
export const CLEAR_GAME = "CLEAR_GAME";
export const END_GAME = "END_GAME";
export const SAVE_GAME_TIME = "SAVE_GAME_TIME";

export const API_CALL_REQUEST = "API_CALL_REQUEST";
export const API_CALL_SUCCESS = "API_CALL_SUCCESS";
Expand Down
7 changes: 7 additions & 0 deletions src/constants/routes.js
@@ -0,0 +1,7 @@
const Routes = {
Home: "/",
Play: "/play",
Results: "/results"
};

export default Routes;
3 changes: 2 additions & 1 deletion src/containers/App.js
@@ -1,4 +1,5 @@
import { FlexWrapper } from "components";
import Routes from "constants/routes";
import FooterContent from "containers/FooterContent";

import HeaderContent from "containers/HeaderContent";
Expand All @@ -21,7 +22,7 @@ class App extends Component {
<HeaderContent />
<MainContent />
<SidebarHelp />
{location.pathname === "/" && <SidebarSettings />}
{location.pathname === Routes.Home && <SidebarSettings />}
<FooterContent />
</FlexWrapper>
</ThemeProvider>
Expand Down
48 changes: 27 additions & 21 deletions src/containers/Countdown.js
@@ -1,4 +1,3 @@
import { GameUI } from "components";
import PropTypes from "prop-types";
import React, { Component } from "react";

Expand Down Expand Up @@ -28,24 +27,23 @@ class Countdown extends Component {
};
this.timer = 0;
this.startTimer = this.startTimer.bind(this);
this.stopTimer = this.stopTimer.bind(this);
this.countDown = this.countDown.bind(this);
}

componentDidMount() {
const { result, isGamePaused } = this.props;
if (
(result.length === 0 && this.timer === 0) ||
(result.length === 0 && isGamePaused)
) {
this.startTimer();
this.startTimer();
}

componentDidUpdate() {
const { isPaused, onCountdownPaused } = this.props;
if (isPaused) {
onCountdownPaused(this.state.time);
}
}

componentWillUnmount() {
if (this.timer) {
clearInterval(this.timer);
}
this.timer = 0;
this.stopTimer();
}

startTimer() {
Expand All @@ -54,11 +52,18 @@ class Countdown extends Component {
}
}

stopTimer() {
if (this.timer) {
clearInterval(this.timer);
}
this.timer = 0;
}

countDown() {
// Remove one second, set state so a re-render happens.
const { isGamePaused, onCountdownEnd } = this.props;
const { isPaused, onCountdownEnd } = this.props;

if (!isGamePaused) {
if (!isPaused) {
const seconds = this.state.seconds - 1;
this.setState({
time: Countdown.secondsToTime(seconds),
Expand All @@ -67,33 +72,34 @@ class Countdown extends Component {

// Check if we're at zero.
if (seconds === 0) {
clearInterval(this.timer);
onCountdownEnd();
this.stopTimer();
onCountdownEnd(this.state.time);
}
}
}

render() {
const { time } = this.state;
const { isGamePaused } = this.props;
const timer =
time.m === 0
? time.s
: `${time.m}:${time.s < 10 ? `0${time.s}` : time.s}`;

return <GameUI.Label blink={isGamePaused}>{timer}</GameUI.Label>;
return <React.Fragment>{timer}</React.Fragment>;
}
}

Countdown.propTypes = {
from: PropTypes.number.isRequired,
isGamePaused: PropTypes.bool,
result: PropTypes.arrayOf(PropTypes.string).isRequired,
onCountdownEnd: PropTypes.func.isRequired
isPaused: PropTypes.bool,
onCountdownPaused: PropTypes.func,
onCountdownEnd: PropTypes.func
};

Countdown.defaultProps = {
isGamePaused: false
isPaused: false,
onCountdownPaused: () => {},
onCountdownEnd: () => {}
};

export default Countdown;
3 changes: 2 additions & 1 deletion src/containers/FooterContent.js
@@ -1,4 +1,5 @@
import { toggleSidebar } from "actions/UIActions";
import Routes from "constants/routes";
import PropTypes from "prop-types";
import React, { Component } from "react";

Expand All @@ -17,7 +18,7 @@ class FooterContent extends Component {
return (
<Footer>
<Toolbar>
{location.pathname === "/" && (
{location.pathname === Routes.Home && (
<Toolbar.Item md onClick={() => toggleSidebar("settings")}>
<FaCog />
</Toolbar.Item>
Expand Down
55 changes: 39 additions & 16 deletions src/containers/HeaderContent.js
@@ -1,36 +1,49 @@
import { endGame } from "actions/GameActions";
import { endGame, saveGameTime } from "actions/GameActions";

import { GameUI, Header, Logo } from "components/index";
import Routes from "constants/routes";
import Countdown from "containers/Countdown";
import logo from "containers/MarvelLogo.svg";
import iconJarvig from "containers/icon-jarvig.svg";
import logo from "containers/MarvelLogo.svg";
import PropTypes from "prop-types";
import React, { Component } from "react";
import FaClock from "react-icons/lib/fa/clock-o";

import FaHeart from "react-icons/lib/fa/heart";

import TiChevronLeft from "react-icons/lib/ti/chevron-left";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { push } from "react-router-redux";
import { bindActionCreators, compose } from "redux";

import TiChevronLeft from "react-icons/lib/ti/chevron-left";
import { formatTimer } from "utils/helpers";

class HeaderContent extends Component {
handleCountdownEnd() {
const { endGame, push } = this.props;
handleCountdownPaused(time) {
const { game, saveGameTime } = this.props;

if (game.time !== time) {
saveGameTime(time);
}
}

handleCountdownEnd(time) {
const { endGame, push, saveGameTime } = this.props;

saveGameTime(time);
endGame();
push("/result");
push(Routes.Results);
}

render() {
const { location, time, game, isLoading, error } = this.props;

const isGamePaused = isLoading || game.checked || game.over || error;

return (
location.pathname !== "/" && (
location.pathname !== Routes.Home && (
<Header>
<Logo inline="true" mobileIcon="true" to="/" title="Go Home">
<Logo inline="true" mobileicon="true" to="/" title="Go Home">
<TiChevronLeft />
<Logo.MarvelLogo sm src={logo} alt="Marvel Logo" />
<Logo.JarvigText sm spaceLeft>
Expand All @@ -43,12 +56,20 @@ class HeaderContent extends Component {
<GameUI.Icon>
<FaClock />
</GameUI.Icon>
<Countdown
from={time * 60}
isGamePaused={isLoading || game.checked || game.over || error}
onCountdownEnd={() => this.handleCountdownEnd()}
result={game.result}
/>
<GameUI.Label blink={isGamePaused}>
{!game.over ? (
<Countdown
from={time * 60}
isPaused={isGamePaused}
onCountdownPaused={time => this.handleCountdownPaused(time)}
onCountdownEnd={time => this.handleCountdownEnd(time)}
/>
) : (
<React.Fragment>
{game.time ? formatTimer(game.time) : 0}
</React.Fragment>
)}
</GameUI.Label>
</GameUI.Item>
<GameUI.Item>
<GameUI.Icon animated>
Expand Down Expand Up @@ -78,6 +99,7 @@ HeaderContent.propTypes = {
message: PropTypes.string
}),
endGame: PropTypes.func.isRequired,
saveGameTime: PropTypes.func.isRequired,
push: PropTypes.func.isRequired
};

Expand All @@ -96,7 +118,8 @@ const mapDispatchToProps = dispatch =>
bindActionCreators(
{
push,
endGame
endGame,
saveGameTime
},
dispatch
);
Expand Down
17 changes: 12 additions & 5 deletions src/containers/MainContent.js
@@ -1,22 +1,29 @@
import { Main } from "components";
import Routes from "constants/routes";

import HomeScreen from "containers/HomeScreen";
import PlayScreen from "containers/PlayScreen";
import ResultScreen from "containers/ResultScreen";
import PropTypes from "prop-types";
import React, { Component } from "react";
import { Route, Switch, withRouter } from "react-router-dom";
import { Redirect, Route, Switch, withRouter } from "react-router-dom";
import withTracker from "utils/withTracker";

class MainContent extends Component {
render() {
const { location } = this.props;
return (
<Main isHome={location.pathname === "/"}>
<Main isHome={location.pathname === Routes.Home}>
<Switch>
<Route exact path="/" component={withTracker(HomeScreen)} />
<Route path="/play" component={withTracker(PlayScreen)} />
<Route path="/result" component={withTracker(ResultScreen)} />
<Route exact path={Routes.Home} component={withTracker(HomeScreen)} />
<Route exact path={Routes.Play} component={withTracker(PlayScreen)} />
<Route
exact
path={Routes.Results}
component={withTracker(ResultScreen)}
/>
{/* 404: redirects to homepage */}
<Redirect to={Routes.Home} />
</Switch>
</Main>
);
Expand Down

0 comments on commit 2dc93fe

Please sign in to comment.