Skip to content

Commit

Permalink
Stop the timer when the game is not visible
Browse files Browse the repository at this point in the history
Closes #477
  • Loading branch information
LeonardoBraga committed Aug 6, 2019
1 parent fd68190 commit 19b0306
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 60 deletions.
77 changes: 63 additions & 14 deletions src/main/services/preact-canvas/components/game/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ interface State {
playMode: PlayMode;
toReveal: number;
startTime: number;
endTime: number;
hiddenTime: number;
// This should always be set as we prevent the game from starting until the
// renderer is loaded.
renderer?: Renderer;
Expand All @@ -71,16 +71,17 @@ initFocusHandling();
export default class Game extends Component<Props, State> {
state: State;
private _tryAgainBtn?: HTMLButtonElement;
private _intervalId?: number;

constructor(props: Props) {
super(props);
this.state = {
playMode: PlayMode.Pending,
toReveal: props.toRevealTotal,
startTime: 0,
hiddenTime: 0,
completeTime: 0,
bestTime: 0,
endTime: 0
bestTime: 0
};

this._init();
Expand All @@ -105,6 +106,7 @@ export default class Game extends Component<Props, State> {
return (
<div class={gameClass}>
<TopBar
time={completeTime}
timerRunning={timerRunning}
toReveal={toReveal}
toRevealTotal={toRevealTotal}
Expand Down Expand Up @@ -175,11 +177,14 @@ export default class Game extends Component<Props, State> {
this.props.onDangerModeChange(true);
}
window.addEventListener("keyup", this.onKeyUp);
document.addEventListener("visibilitychange", this.onVisibilityChange);
}

componentWillUnmount() {
this.props.gameChangeUnsubscribe(this.onGameChange);
this.stopTimer();
window.removeEventListener("keyup", this.onKeyUp);
document.removeEventListener("visibilitychange", this.onVisibilityChange);
}

componentDidUpdate(_: Props, previousState: State) {
Expand All @@ -195,6 +200,26 @@ export default class Game extends Component<Props, State> {
}
}

@bind
private onVisibilityChange() {
if (this.state.playMode !== PlayMode.Playing) {
return;
}

const newState: Partial<State> = {};
if (document.visibilityState === "hidden") {
newState.hiddenTime = Date.now();
this.stopTimer();
} else {
const { startTime, hiddenTime } = this.state;
newState.startTime = startTime + Date.now() - hiddenTime;
newState.completeTime = Date.now() - newState.startTime;
this.startTimer();
}

this.setState(newState as State);
}

private async _init() {
let renderer: Renderer;
let animator: Animator;
Expand Down Expand Up @@ -259,17 +284,26 @@ export default class Game extends Component<Props, State> {
) {
newState.playMode = gameChange.playMode;

if (gameChange.playMode! === PlayMode.Playing) {
newState.startTime = Date.now();
} else if (gameChange.playMode! === PlayMode.Won) {
newState.completeTime = Date.now() - this.state.startTime;
newState.bestTime = await submitTime(
this.props.width,
this.props.height,
this.props.mines,
newState.completeTime
);
this.props.onDangerModeChange(false);
switch (gameChange.playMode!) {
case PlayMode.Playing:
newState.startTime = Date.now();
this.startTimer();
break;

case PlayMode.Won:
newState.completeTime = Date.now() - this.state.startTime;
newState.bestTime = await submitTime(
this.props.width,
this.props.height,
this.props.mines,
newState.completeTime
);
this.props.onDangerModeChange(false);

/* fall through */
case PlayMode.Lost:
this.stopTimer();
break;
}
}

Expand Down Expand Up @@ -314,4 +348,19 @@ export default class Game extends Component<Props, State> {
this.props.stateService.revealSurrounding(x, y);
}
}

private startTimer() {
this._intervalId = setInterval(() => this.updateCompleteTime(), 1000);
}

private stopTimer() {
clearInterval(this._intervalId);
}

private updateCompleteTime() {
const newState: Partial<State> = {
completeTime: Date.now() - this.state.startTime
};
this.setState(newState as State);
}
}
51 changes: 5 additions & 46 deletions src/main/services/preact-canvas/components/top-bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,56 +26,13 @@ import {
// tslint:disable:max-classes-per-file

interface TimeProps {
running: boolean;
time: number;
}

// Using a sub class to avoid Preact diffing every second.
class Time extends Component<TimeProps, {}> {
private _start?: number;
private _intervalId?: number;

componentDidMount() {
if (this.props.running) {
this._startTimer();
}
}

componentWillReceiveProps({ running }: TimeProps) {
if (running === this.props.running) {
return;
}

if (running) {
this._startTimer();
} else {
this._stopTimer();
}
}

shouldComponentUpdate() {
return false;
}

componentWillUnmount() {
this._stopTimer();
}

render() {
return <div role="timer">00:00</div>;
}

private _startTimer() {
this._start = Date.now();

this._intervalId = setInterval(() => {
requestAnimationFrame(() => {
this.base!.textContent = minSec(Date.now() - this._start!);
});
}, 1000);
}

private _stopTimer() {
clearInterval(this._intervalId);
return <div role="timer">{minSec(this.props.time)}</div>;
}
}

Expand All @@ -95,6 +52,7 @@ export interface Props {
toRevealTotal?: number;
toReveal?: number;
timerRunning?: boolean;
time: number;
playMode?: PlayMode;
useMotion?: boolean;
showBestTime?: boolean;
Expand All @@ -109,6 +67,7 @@ export default class TopBar extends Component<Props, State> {
toReveal,
toRevealTotal,
timerRunning,
time: currentTime,
playMode,
useMotion,
bestTime,
Expand All @@ -126,7 +85,7 @@ export default class TopBar extends Component<Props, State> {
{showBestTime && bestTime
? [<div>{minSec(bestTime)}</div>, <Star class={timeIcon} />]
: [
<Time running={timerRunning!} />,
<Time time={currentTime} />,
<Timer class={timeIcon} animate={timerRunning && useMotion} />
]}
</div>
Expand Down

0 comments on commit 19b0306

Please sign in to comment.