Skip to content

Commit

Permalink
show confetti when the endpoint is reached
Browse files Browse the repository at this point in the history
  • Loading branch information
chrispyles committed Jul 3, 2024
1 parent b969cda commit 2afc8ef
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 4 deletions.
17 changes: 17 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@angular/platform-browser": "^18.0.0",
"@angular/platform-browser-dynamic": "^18.0.0",
"@angular/router": "^18.0.0",
"canvas-confetti": "^1.9.3",
"decorator-cache-getter": "^1.0.0",
"pure-rand": "^6.1.0",
"rxjs": "~7.8.0",
Expand All @@ -31,6 +32,7 @@
"@angular-devkit/build-angular": "^18.0.5",
"@angular/cli": "^18.0.5",
"@angular/compiler-cli": "^18.0.0",
"@types/canvas-confetti": "^1.6.4",
"@types/jasmine": "~5.1.0",
"angular-cli-ghpages": "^2.0.1",
"exhaustive": "^1.1.1",
Expand Down
20 changes: 16 additions & 4 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { signal, WritableSignal } from '@angular/core';
import { By } from '@angular/platform-browser';

import { AppComponent, WINDOW_TOKEN } from './app.component';
import { GameStateService } from './game-state.service';
import { Chooser, Dir, Maze } from '../lib';
import { signal } from '@angular/core';
import { By } from '@angular/platform-browser';
import { Chooser, Dir, Maze, Node } from '../lib';
import { LogoComponent } from './logo/logo.component';
import { MazeComponent } from './maze/maze.component';
import { ConfettiService } from './confetti.service';

describe('AppComponent', () => {
let positionSignal: WritableSignal<Node>;
let gameStateService: jasmine.SpyObj<GameStateService>;
let windowSpy: jasmine.SpyObj<Window>;
let clipboardWriteSpy: jasmine.Spy;
let confettiStartSpy: jasmine.Spy;
let createComponent = true;
let testBed: TestBed;
let fixture: ComponentFixture<AppComponent>;
Expand All @@ -33,13 +36,14 @@ describe('AppComponent', () => {

beforeEach(async () => {
const maze = new Maze(4, new Chooser(42));
positionSignal = signal(maze.getNode([0, 0]));
gameStateService = jasmine.createSpyObj<GameStateService>(
'GameStateService',
['getShareUrl', 'move', 'reset', 'solve'],
{
inAnimation: false,
maze,
position: signal(maze.getNode([0, 0])),
position: positionSignal,
path: signal([maze.getNode([0, 0])]),
},
);
Expand Down Expand Up @@ -75,6 +79,8 @@ describe('AppComponent', () => {
],
});

confettiStartSpy = spyOn(TestBed.inject(ConfettiService), 'start');

if (createComponent) {
await testBed.compileComponents();
fixture = TestBed.createComponent(AppComponent);
Expand Down Expand Up @@ -150,6 +156,12 @@ describe('AppComponent', () => {
.componentInstance.move.emit(Dir.U);
expect(gameStateService.move).not.toHaveBeenCalled();
});

it('should show confetti when the endpoint is reached', () => {
positionSignal.set(gameStateService.maze.end);
fixture.detectChanges();
expect(confettiStartSpy).toHaveBeenCalledTimes(1);
});
});

describe('solve maze button', () => {
Expand Down
12 changes: 12 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import {
OnInit,
signal,
ElementRef,
effect,
} from '@angular/core';

import { MazeComponent } from './maze/maze.component';
import { GameStateService } from './game-state.service';
import { Dir, type Maze } from '../lib';
import { LogoComponent } from './logo/logo.component';
import { GitHubLogoComponent } from './svgs/github-logo.component';
import { ConfettiService } from './confetti.service';

const DARK_MODE_CLASS = 'dark-mode';

Expand All @@ -31,6 +33,7 @@ export const WINDOW_TOKEN = new InjectionToken('window', {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit {
private readonly confettiService = inject(ConfettiService);
private readonly elementRef = inject(ElementRef);
private readonly gameStateService = inject(GameStateService);

Expand All @@ -45,6 +48,15 @@ export class AppComponent implements OnInit {
/** Whether the viewport is too small to use the app. */
viewportTooSmall = signal(false);

constructor() {
// When the user reaches the end of the maze, show a confetti animation.
effect(() => {
if (this.maze.end.equals(this.gameStateService.position())) {
this.confettiService.start();
}
});
}

ngOnInit(): void {
// Set dark mode if user's OS is using it.
if (
Expand Down
33 changes: 33 additions & 0 deletions src/app/confetti.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
import confetti from 'canvas-confetti';

@Injectable({
providedIn: 'root',
})
export class ConfettiService {
/** Show the confetti animation. */
start() {
confetti({
angle: 45,
disableForReducedMotion: true,
gravity: 0.75,
origin: { x: -0.05, y: 0.8 },
particleCount: 300,
scalar: 1.5,
spread: 80,
startVelocity: 80,
ticks: 1000,
})!;
confetti({
angle: 135,
disableForReducedMotion: true,
gravity: 0.75,
origin: { x: 1.05, y: 0.8 },
particleCount: 300,
scalar: 1.5,
spread: 100,
startVelocity: 80,
ticks: 1000,
})!;
}
}

0 comments on commit 2afc8ef

Please sign in to comment.