Skip to content

Commit

Permalink
Merge pull request #20 from charlesribeiro/feat/create-timer-service
Browse files Browse the repository at this point in the history
create timer service, add @ngneat/until-destroy
  • Loading branch information
charlesribeiro authored Aug 12, 2023
2 parents ec29d63 + bd9bc1a commit 41768c4
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 3 deletions.
3 changes: 2 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"cli": {
"schematicCollections": [
"@angular-eslint/schematics"
]
],
"analytics": false
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@angular/platform-browser": "^16.1.0",
"@angular/platform-browser-dynamic": "^16.1.0",
"@angular/router": "^16.1.0",
"@ngneat/until-destroy": "^10.0.0",
"@ngrx/effects": "16.2.0",
"@ngrx/store": "^16.2.0",
"@ngrx/store-devtools": "16.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<div>{{ flagsLeft$ | async }}</div>


{{ timer$ | async }}
<table class="table-auto">
<tr *ngFor="let row of cells$ | async">
<td *ngFor="let item of row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { mockCell } from "../../../../utils/mock-cell";
import * as fromAppActions from "../../../../state/app.actions";
import { IApp } from "../../../../state/app.interface";
import { mockBoard } from "../../../../utils/mock-board";
import { TimerService } from "../../../../services/timer.service";

describe("MainGameComponent", () => {
let component: MainGameComponent;
Expand All @@ -17,7 +18,11 @@ describe("MainGameComponent", () => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [MainGameComponent],
providers: [provideMockStore({ initialState }), StorageService],
providers: [
provideMockStore({ initialState }),
StorageService,
TimerService,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
});
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StorageService } from "../../../../services/storage.service";
import { Observable, combineLatest, map } from "rxjs";
import { Level } from "../../../../models/level.model";
import { GameStatus } from "../../../../models/gameStatus.model";
import { TimerService } from "../../../../services/timer.service";

@Component({
selector: "app-main-game",
Expand All @@ -18,16 +19,19 @@ export class MainGameComponent implements OnInit {
cells$: Observable<Cell[][]>;
gameStatus$: Observable<GameStatus>;
flagsLeft$: Observable<number>;
timer$: Observable<number>;

readonly GAMEOVER = GameStatus.LOST;
readonly WON = GameStatus.WON;

constructor(
private store: Store<IApp>,
private storage: StorageService,
private timer: TimerService,
) {}

ngOnInit(): void {
this.timer.startTimer(0);
this.store.dispatch(fromAppActions.setGameLevel({ level: Level.Easy }));
this.store.dispatch(fromAppActions.setBoardSize({ width: 5, height: 5 }));
this.store.dispatch(fromAppActions.startGame());
Expand All @@ -40,6 +44,8 @@ export class MainGameComponent implements OnInit {
]).pipe(
map(([totalMines, flagsAlreadyUsed]) => totalMines - flagsAlreadyUsed),
);

this.timer$ = this.timer.currentTimer$;
}

rightClick(cell: Cell): void {
Expand Down
56 changes: 56 additions & 0 deletions src/app/services/timer.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { TestBed } from "@angular/core/testing";
import { take } from "rxjs/operators";
import { TimerService } from "./timer.service";

describe("TimerService", () => {
let service: TimerService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
TimerService,
],
});

service = TestBed.inject(TimerService);
});

it("should be created", () => {
expect(service).toBeTruthy();
});

it("should start with a paused timer", (done) => {
service.timerPaused$.pipe(take(1)).subscribe((isPaused) => {
expect(isPaused).toBeTruthy();
done();
});
});

it("should start timer and update the current time", (done) => {
const initialTime = 10;
service.startTimer(initialTime);

service.currentTimer$.pipe(take(1)).subscribe((time) => {
expect(time).toBe(initialTime + 1);
done();
});
});

it("should toggle the timer state", (done) => {
service.startTimer(0);
service.toggleTimer();

service.timerPaused$.pipe(take(1)).subscribe((isPaused) => {
expect(isPaused).toBeTruthy();
done();
});
});

it("should indicate if the timer is active", () => {
service.startTimer(0);
expect(service.isTimerActive).toBeTruthy();

service.toggleTimer();
expect(service.isTimerActive).toBeFalsy();
});
});
49 changes: 49 additions & 0 deletions src/app/services/timer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject, EMPTY, interval, Observable } from "rxjs";
import { map, switchMap } from "rxjs/operators";
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";

@UntilDestroy()
@Injectable()
export class TimerService {
private timer$: Observable<number>;

private timerState$ = new BehaviorSubject<boolean>(true);

private currentTime = 0;

private isTimerRunning = false;

constructor() {
this.timer$ = this.timerState$.pipe(
switchMap((isPaused) =>
isPaused ? EMPTY : interval(1000).pipe(map(() => this.currentTime++)),
),
);

this.timerState$
.pipe(untilDestroyed(this))
.subscribe((isPaused) => (this.isTimerRunning = !isPaused));
}

get isTimerActive(): boolean {
return this.isTimerRunning;
}

get currentTimer$(): Observable<number> {
return this.timer$;
}

get timerPaused$(): Observable<boolean> {
return this.timerState$;
}

startTimer(initialTime: number): void {
this.currentTime = (initialTime ?? 0) + 1;
this.timerState$.next(false);
}

toggleTimer(): void {
this.timerState$.next(this.isTimerRunning);
}
}
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1859,6 +1859,13 @@
resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz"
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==

"@ngneat/until-destroy@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@ngneat/until-destroy/-/until-destroy-10.0.0.tgz#105db43d858bc1cec2f36272a296af68bed7bf43"
integrity sha512-xXFAabQ4YVJ82LYxdgUlaKZyR3dSbxqG3woSyaclzxfCgWMEDweCcM/GGYbNiHJa0WwklI98RXHvca+UyCxpeg==
dependencies:
tslib "^2.3.0"

"@ngrx/effects@16.2.0":
version "16.2.0"
resolved "https://registry.npmjs.org/@ngrx/effects/-/effects-16.2.0.tgz"
Expand Down

0 comments on commit 41768c4

Please sign in to comment.