Compare commits

...

2 commits

Author SHA1 Message Date
4a7c54eab8
style: format code for consistency and readability
All checks were successful
CI / Get Changed Files (pull_request) Successful in 6s
CI / eslint (pull_request) Successful in 19s
CI / test-build (pull_request) Successful in 27s
CI / prettier (pull_request) Successful in 40s
CI / Checkstyle Main (pull_request) Successful in 1m28s
2025-04-02 10:26:11 +02:00
e983b21e07
feat(blackjack): refresh user balance on game state change 2025-04-02 10:25:43 +02:00
7 changed files with 59 additions and 22 deletions

View file

@ -82,8 +82,7 @@ export default class BlackjackComponent {
if (isGameOver) { if (isGameOver) {
console.log('Game is over, state:', game.state); console.log('Game is over, state:', game.state);
this.refreshUserBalance(); this.userService.refreshCurrentUser();
this.showGameResult.set(true); this.showGameResult.set(true);
console.log('Game result dialog should be shown now'); console.log('Game result dialog should be shown now');
} }
@ -96,7 +95,7 @@ export default class BlackjackComponent {
this.blackjackService.startGame(bet).subscribe({ this.blackjackService.startGame(bet).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
this.refreshUserBalance(); this.userService.refreshCurrentUser();
this.isActionInProgress.set(false); this.isActionInProgress.set(false);
}, },
error: (error) => { error: (error) => {
@ -115,6 +114,9 @@ export default class BlackjackComponent {
this.blackjackService.hit(this.currentGameId()!).subscribe({ this.blackjackService.hit(this.currentGameId()!).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
if (game.state !== 'IN_PROGRESS') {
this.userService.refreshCurrentUser();
}
this.isActionInProgress.set(false); this.isActionInProgress.set(false);
}, },
error: (error) => { error: (error) => {
@ -139,6 +141,7 @@ export default class BlackjackComponent {
this.blackjackService.stand(this.currentGameId()!).subscribe({ this.blackjackService.stand(this.currentGameId()!).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
this.userService.refreshCurrentUser();
this.isActionInProgress.set(false); this.isActionInProgress.set(false);
}, },
error: (error) => { error: (error) => {
@ -163,6 +166,7 @@ export default class BlackjackComponent {
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({ this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
next: (game) => { next: (game) => {
this.updateGameState(game); this.updateGameState(game);
this.userService.refreshCurrentUser();
this.isActionInProgress.set(false); this.isActionInProgress.set(false);
}, },
error: (error) => { error: (error) => {

View file

@ -18,10 +18,6 @@ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angula
<div class="card p-4"> <div class="card p-4">
<h3 class="section-heading text-xl mb-4">Spiel Informationen</h3> <h3 class="section-heading text-xl mb-4">Spiel Informationen</h3>
<div class="space-y-4"> <div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-text-secondary">Guthaben:</span>
<span class="text-emerald">{{ balance | currency: 'EUR' }}</span>
</div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-text-secondary">Aktuelle Wette:</span> <span class="text-text-secondary">Aktuelle Wette:</span>
<span [class]="currentBet > 0 ? 'text-accent-red' : 'text-text-secondary'"> <span [class]="currentBet > 0 ? 'text-accent-red' : 'text-text-secondary'">

View file

@ -86,7 +86,7 @@ export class GameResultComponent {
visible = false; visible = false;
get isWin(): boolean { get isWin(): boolean {
return this.gameState === GameState.PLAYER_WON; return this.gameState === GameState.PLAYER_WON || this.gameState === GameState.PLAYER_BLACKJACK;
} }
get isLoss(): boolean { get isLoss(): boolean {
@ -98,6 +98,7 @@ export class GameResultComponent {
} }
getResultTitle(): string { getResultTitle(): string {
if (this.gameState === GameState.PLAYER_BLACKJACK) return 'Blackjack!';
if (this.isWin) return 'Gewonnen!'; if (this.isWin) return 'Gewonnen!';
if (this.isLoss) return 'Verloren!'; if (this.isLoss) return 'Verloren!';
if (this.isDraw) return 'Unentschieden!'; if (this.isDraw) return 'Unentschieden!';
@ -105,6 +106,8 @@ export class GameResultComponent {
} }
getResultMessage(): string { getResultMessage(): string {
if (this.gameState === GameState.PLAYER_BLACKJACK)
return 'Glückwunsch! Du hast mit einem Blackjack gewonnen!';
if (this.isWin) return 'Glückwunsch! Du hast diese Runde gewonnen.'; if (this.isWin) return 'Glückwunsch! Du hast diese Runde gewonnen.';
if (this.isLoss) return 'Schade! Du hast diese Runde verloren.'; if (this.isLoss) return 'Schade! Du hast diese Runde verloren.';
if (this.isDraw) return 'Diese Runde endet unentschieden. Dein Einsatz wurde zurückgegeben.'; if (this.isDraw) return 'Diese Runde endet unentschieden. Dein Einsatz wurde zurückgegeben.';
@ -112,6 +115,7 @@ export class GameResultComponent {
} }
getResultClass(): string { getResultClass(): string {
if (this.gameState === GameState.PLAYER_BLACKJACK) return 'text-emerald font-bold';
if (this.isWin) return 'text-emerald'; if (this.isWin) return 'text-emerald';
if (this.isLoss) return 'text-accent-red'; if (this.isLoss) return 'text-accent-red';
if (this.isDraw) return 'text-yellow-400'; if (this.isDraw) return 'text-yellow-400';

View file

@ -3,4 +3,5 @@ export enum GameState {
IN_PROGRESS = 'IN_PROGRESS', IN_PROGRESS = 'IN_PROGRESS',
PLAYER_LOST = 'PLAYER_LOST', PLAYER_LOST = 'PLAYER_LOST',
DRAW = 'DRAW', DRAW = 'DRAW',
PLAYER_BLACKJACK = 'PLAYER_BLACKJACK',
} }

View file

@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { KeycloakProfile } from 'keycloak-js'; import { KeycloakProfile } from 'keycloak-js';
import { catchError, EMPTY, Observable } from 'rxjs'; import { BehaviorSubject, catchError, EMPTY, Observable, tap } from 'rxjs';
import { User } from '../model/User'; import { User } from '../model/User';
@Injectable({ @Injectable({
@ -9,20 +9,39 @@ import { User } from '../model/User';
}) })
export class UserService { export class UserService {
private http: HttpClient = inject(HttpClient); private http: HttpClient = inject(HttpClient);
private currentUserSubject = new BehaviorSubject<User | null>(null);
public currentUser$ = this.currentUserSubject.asObservable();
constructor() {
// Initialize with current user data
this.getCurrentUser().subscribe();
}
public getUser(id: string): Observable<User | null> { public getUser(id: string): Observable<User | null> {
return this.http.get<User | null>(`/backend/user/${id}`).pipe(catchError(() => EMPTY)); return this.http.get<User | null>(`/backend/user/${id}`).pipe(
catchError(() => EMPTY),
tap((user) => this.currentUserSubject.next(user))
);
} }
public getCurrentUser(): Observable<User | null> { public getCurrentUser(): Observable<User | null> {
return this.http.get<User | null>('/backend/user').pipe(catchError(() => EMPTY)); return this.http.get<User | null>('/backend/user').pipe(
catchError(() => EMPTY),
tap((user) => this.currentUserSubject.next(user))
);
}
public refreshCurrentUser(): void {
this.getCurrentUser().subscribe();
} }
public createUser(id: string, username: string): Observable<User> { public createUser(id: string, username: string): Observable<User> {
return this.http.post<User>('/backend/user', { return this.http
keycloakId: id, .post<User>('/backend/user', {
username: username, keycloakId: id,
}); username: username,
})
.pipe(tap((user) => this.currentUserSubject.next(user)));
} }
public async getOrCreateUser(userProfile: KeycloakProfile) { public async getOrCreateUser(userProfile: KeycloakProfile) {

View file

@ -18,7 +18,7 @@
<div <div
class="text-white font-bold bg-deep-blue-contrast rounded-full px-4 py-2 text-sm hover:bg-deep-blue-contrast/80 hover:cursor-pointer hover:scale-105 transition-all active:scale-95 select-none duration-300" class="text-white font-bold bg-deep-blue-contrast rounded-full px-4 py-2 text-sm hover:bg-deep-blue-contrast/80 hover:cursor-pointer hover:scale-105 transition-all active:scale-95 select-none duration-300"
> >
<span>Guthaben: {{ balance() | currency: 'EUR' : 'symbol' : '1.2-2' }}</span> <span>{{ balance() | currency: 'EUR' : 'symbol' : '1.2-2' }}</span>
</div> </div>
<button (click)="logout()" class="button-primary px-4 py-1.5">Abmelden</button> <button (click)="logout()" class="button-primary px-4 py-1.5">Abmelden</button>
} }

View file

@ -1,9 +1,17 @@
import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core'; import {
ChangeDetectionStrategy,
Component,
inject,
OnInit,
OnDestroy,
signal,
} from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { KeycloakService } from 'keycloak-angular'; import { KeycloakService } from 'keycloak-angular';
import { CurrencyPipe } from '@angular/common'; import { CurrencyPipe } from '@angular/common';
import { UserService } from '@service/user.service'; import { UserService } from '@service/user.service';
import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'app-navbar', selector: 'app-navbar',
templateUrl: './navbar.component.html', templateUrl: './navbar.component.html',
@ -11,22 +19,27 @@ import { UserService } from '@service/user.service';
imports: [RouterModule, CurrencyPipe], imports: [RouterModule, CurrencyPipe],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class NavbarComponent implements OnInit { export class NavbarComponent implements OnInit, OnDestroy {
isMenuOpen = false; isMenuOpen = false;
private keycloakService: KeycloakService = inject(KeycloakService); private keycloakService: KeycloakService = inject(KeycloakService);
isLoggedIn = this.keycloakService.isLoggedIn(); isLoggedIn = this.keycloakService.isLoggedIn();
private userService = inject(UserService); private userService = inject(UserService);
private user = this.userService.getCurrentUser(); private userSubscription: Subscription | undefined;
public balance = signal(0); public balance = signal(0);
ngOnInit() { ngOnInit() {
this.user.subscribe((user) => { this.userSubscription = this.userService.currentUser$.subscribe((user) => {
this.balance.set(user?.balance ?? 0); this.balance.set(user?.balance ?? 0);
}); });
} }
ngOnDestroy() {
if (this.userSubscription) {
this.userSubscription.unsubscribe();
}
}
login() { login() {
try { try {
const baseUrl = window.location.origin; const baseUrl = window.location.origin;