Skip to content

Commit

Permalink
🚀 Persist results in local storage
Browse files Browse the repository at this point in the history
  • Loading branch information
brathis committed Apr 24, 2024
1 parent f9217a6 commit 9d7fd55
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 54 deletions.
3 changes: 1 addition & 2 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ <h3>for Robinson R44</h3>
class="controls"
[totalCapacity]="totalCapacity"
[state]="fuelGagesState"
[totalQuantity]="totalQuantity"
[guess]="guess"
[time]="time"
[result]="result"
(restartButtonClicked$)="restartButtonClicked()"
(guessSubmitted$)="guessSubmitted($event)"
></app-controls>
Expand Down
31 changes: 0 additions & 31 deletions src/app/app.component.spec.ts

This file was deleted.

13 changes: 9 additions & 4 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component, Inject, OnInit } from '@angular/core';
import { FuelTank } from './fuel-gages/fuel-tank.model';
import { Subscription, combineLatest, interval, timer } from 'rxjs';
import { FuelGagesState } from './fuel-gages/fuel-gages.state';
import { Result } from './result.model';
import { RESULTS_SERVICE, ResultsService } from './results.service';

@Component({
selector: 'app-root',
Expand All @@ -15,12 +17,14 @@ export class AppComponent implements OnInit {
.reduce((a, b) => a + b, 0);

totalQuantity: number = 0;
guess: number = 0;
fuelGagesState = FuelGagesState.HIDDEN;
time = 0;
timeSubscription: Subscription | null = null;
result: Result | null = null;

constructor() {
constructor(
@Inject(RESULTS_SERVICE) private readonly resultsService: ResultsService,
) {
const quantityObservables = [];
for (const tank of this.tanks) {
quantityObservables.push(tank.getQuantity$());
Expand Down Expand Up @@ -62,7 +66,8 @@ export class AppComponent implements OnInit {
private reveal(guess: number): void {
this.timeSubscription?.unsubscribe();
this.fuelGagesState = FuelGagesState.VISIBLE;
this.guess = guess;
this.result = new Result(new Date(), guess, this.totalQuantity, this.time);
this.resultsService.add(this.result);
}

guessSubmitted(guess: number): void {
Expand Down
6 changes: 5 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { FuelGagesComponent } from './fuel-gages/fuel-gages.component';
import { FuelGageComponent } from './fuel-gages/fuel-gage/fuel-gage.component';
import { ControlsComponent } from './controls/controls.component';
import { FormsModule } from '@angular/forms';
import { LocalStorageResultsService } from './local-storage-results.service';
import { RESULTS_SERVICE } from './results.service';

@NgModule({
declarations: [
Expand All @@ -15,7 +17,9 @@ import { FormsModule } from '@angular/forms';
ControlsComponent,
],
imports: [BrowserModule, FormsModule],
providers: [],
providers: [
{ provide: RESULTS_SERVICE, useClass: LocalStorageResultsService },
],
bootstrap: [AppComponent],
})
export class AppModule {}
12 changes: 6 additions & 6 deletions src/app/controls/controls.component.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<div class="controls">
<div class="result" *ngIf="state === 'VISIBLE'">
<div class="result" *ngIf="state === 'VISIBLE' && result">
<div class="result-guess">
<span class="label">Your Guess</span>
<span class="value">{{ guess | number: "1.0-0" }} l</span>
<span class="value">{{ result.getGuess() | number: "1.0-0" }} l</span>
</div>
<div class="result-total">
<span class="label">Total</span>
<span class="value">{{ totalQuantity | number: "1.0-0" }} l</span>
<span class="value">{{ result.getActual() | number: "1.0-0" }} l</span>
</div>
<div class="result-error">
<span class="label">Error</span>
<span class="value">{{ error | number: "1.0-0" }} l</span>
<span class="value">{{ result.getError() | number: "1.0-0" }} l</span>
</div>
<div class="result-time">
<span class="label">Time Taken</span>
<span class="value">{{ time }} s</span>
<span class="value">{{ result.getTime() }} s</span>
</div>
</div>
<div class="button">
Expand All @@ -31,7 +31,7 @@
<input
type="number"
(keydown.enter)="guessSubmitted()"
[(ngModel)]="guessModel"
[(ngModel)]="guess"
[disabled]="state === 'RESETTING'"
#guessInput
/>
Expand Down
15 changes: 5 additions & 10 deletions src/app/controls/controls.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ViewChild,
} from '@angular/core';
import { FuelGagesState } from '../fuel-gages/fuel-gages.state';
import { Result } from '../result.model';

@Component({
selector: 'app-controls',
Expand All @@ -20,40 +21,34 @@ import { FuelGagesState } from '../fuel-gages/fuel-gages.state';
export class ControlsComponent implements OnChanges {
@Input() totalCapacity: number = 0;
@Input() state: FuelGagesState = FuelGagesState.RESETTING;
@Input() totalQuantity: number = 0;
@Input() guess: number = 0;
@Input() time: number = 0;
@Input() result: Result | null = null;
@Output() restartButtonClicked$: EventEmitter<void> = new EventEmitter();
@Output() guessSubmitted$: EventEmitter<number> = new EventEmitter();
@ViewChild('guessInput') guessInput: ElementRef<HTMLInputElement> =
{} as ElementRef;
guessModel = '';
error = 0;
guess = '';

ngOnChanges(changes: SimpleChanges): void {
if (changes['state']) {
switch (changes['state'].currentValue) {
case FuelGagesState.RESETTING:
this.guessModel = '';
this.guess = '';
break;
case FuelGagesState.HIDDEN:
// TODO: what is the correct lifecycle method to use to avoid setTimeout?
setTimeout(() => this.guessInput.nativeElement.focus(), 0);
break;
}
}

if (changes['totalQuantity'] || changes['guess']) {
this.error = this.guess - this.totalQuantity;
}
}

restartButtonClicked(): void {
this.restartButtonClicked$.emit();
}

guessSubmitted(): void {
const guess = Number.parseInt(this.guessModel);
const guess = Number.parseInt(this.guess);
if (!Number.isNaN(guess) && 0 <= guess && guess <= this.totalCapacity) {
this.guessSubmitted$.emit(guess);
}
Expand Down
40 changes: 40 additions & 0 deletions src/app/local-storage-results.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Injectable, OnInit } from '@angular/core';
import { ResultsService } from './results.service';
import { Result } from './result.model';

@Injectable({
providedIn: 'root',
})
export class LocalStorageResultsService implements ResultsService, OnInit {
private static readonly KEY = 'results';
private results: Result[] = [];

constructor() {}

ngOnInit(): void {
this.load();
}

public add(result: Result): void {
this.results.push(result);
this.persist();
}

public get(): Result[] {
return this.results;
}

private load(): void {
this.results =
JSON.parse(
localStorage.getItem(LocalStorageResultsService.KEY) ?? '[]',
) ?? [];
}

private persist(): void {
localStorage.setItem(
LocalStorageResultsService.KEY,
JSON.stringify(this.results),
);
}
}
28 changes: 28 additions & 0 deletions src/app/result.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export class Result {
constructor(
private readonly instant: Date,
private readonly guess: number,
private readonly actual: number,
private readonly time: number,
) {}

getInstant(): Date {
return this.instant;
}

getGuess(): number {
return this.guess;
}

getActual(): number {
return this.actual;
}

getError(): number {
return this.guess - this.actual;
}

getTime(): number {
return this.time;
}
}
11 changes: 11 additions & 0 deletions src/app/results.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { InjectionToken } from '@angular/core';
import { Result } from './result.model';

export const RESULTS_SERVICE = new InjectionToken<ResultsService>(
'services.results',
);

export interface ResultsService {
add(result: Result): void;
get(): Result[];
}

0 comments on commit 9d7fd55

Please sign in to comment.