diff --git a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java index 540e4c5..5018914 100644 --- a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java +++ b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxController.java @@ -1,91 +1,91 @@ -package de.szut.casino.lootboxes; - -import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException; -import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException; -import de.szut.casino.user.UserEntity; -import de.szut.casino.user.UserService; -import jakarta.validation.Valid; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -@RestController -public class LootBoxController { - private final LootBoxRepository lootBoxRepository; - private final UserService userService; - private final LootBoxService lootBoxService; - - public LootBoxController(LootBoxRepository lootBoxRepository, UserService userService, LootBoxService lootBoxService) { - this.lootBoxRepository = lootBoxRepository; - this.userService = userService; - this.lootBoxService = lootBoxService; - } - - @GetMapping("/lootboxes") - public List getAllLootBoxes() { - return lootBoxRepository.findAll(); - } - - @PostMapping("/lootboxes/{id}") - public ResponseEntity purchaseLootBox(@PathVariable Long id) { - Optional optionalLootBox = lootBoxRepository.findById(id); - if (optionalLootBox.isEmpty()) { - return ResponseEntity.notFound().build(); - } - - LootBoxEntity lootBox = optionalLootBox.get(); - - Optional optionalUser = userService.getCurrentUser(); - if (optionalUser.isEmpty()) { - throw new UserNotFoundException(); - } - - UserEntity user = optionalUser.get(); - - if (lootBoxService.hasSufficientBalance(user, lootBox.getPrice())) { - throw new InsufficientFundsException(); - } - - RewardEntity reward = lootBoxService.determineReward(lootBox); - lootBoxService.handleBalance(user, lootBox, reward); - - return ResponseEntity.ok(reward); - } - - @PostMapping("/lootboxes") - public ResponseEntity createLootbox(@RequestBody @Valid CreateLootBoxDto createLootBoxDto) { - List rewardEntities = new ArrayList<>(); - - for (CreateRewardDto createRewardDto : createLootBoxDto.getRewards()) { - rewardEntities.add(new RewardEntity(createRewardDto.getValue(), createRewardDto.getProbability())); - } - - LootBoxEntity lootBoxEntity = new LootBoxEntity( - createLootBoxDto.getName(), - createLootBoxDto.getPrice(), - rewardEntities - ); - - this.lootBoxRepository.save(lootBoxEntity); - - return ResponseEntity.ok(lootBoxEntity); - } - - @DeleteMapping("/lootboxes/{id}") - public ResponseEntity deleteLootbox(@PathVariable Long id) { - Optional optionalLootBox = lootBoxRepository.findById(id); - if (optionalLootBox.isEmpty()) { - return ResponseEntity.notFound().build(); - } - - LootBoxEntity lootBox = optionalLootBox.get(); - lootBoxRepository.delete(lootBox); - - return ResponseEntity.ok(Collections.singletonMap("message", "successfully deleted lootbox")); - } - -} +package de.szut.casino.lootboxes; + +import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException; +import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException; +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserService; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +@RestController +public class LootBoxController { + private final LootBoxRepository lootBoxRepository; + private final UserService userService; + private final LootBoxService lootBoxService; + + public LootBoxController(LootBoxRepository lootBoxRepository, UserService userService, LootBoxService lootBoxService) { + this.lootBoxRepository = lootBoxRepository; + this.userService = userService; + this.lootBoxService = lootBoxService; + } + + @GetMapping("/lootboxes") + public List getAllLootBoxes() { + return lootBoxRepository.findAll(); + } + + @PostMapping("/lootboxes/{id}") + public ResponseEntity purchaseLootBox(@PathVariable Long id) { + Optional optionalLootBox = lootBoxRepository.findById(id); + if (optionalLootBox.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + LootBoxEntity lootBox = optionalLootBox.get(); + + Optional optionalUser = userService.getCurrentUser(); + if (optionalUser.isEmpty()) { + throw new UserNotFoundException(); + } + + UserEntity user = optionalUser.get(); + + if (lootBoxService.hasSufficientBalance(user, lootBox.getPrice())) { + throw new InsufficientFundsException(); + } + + RewardEntity reward = lootBoxService.determineReward(lootBox); + lootBoxService.handleBalance(user, lootBox, reward); + + return ResponseEntity.ok(reward); + } + + @PostMapping("/lootboxes") + public ResponseEntity createLootbox(@RequestBody @Valid CreateLootBoxDto createLootBoxDto) { + List rewardEntities = new ArrayList<>(); + + for (CreateRewardDto createRewardDto : createLootBoxDto.getRewards()) { + rewardEntities.add(new RewardEntity(createRewardDto.getValue(), createRewardDto.getProbability())); + } + + LootBoxEntity lootBoxEntity = new LootBoxEntity( + createLootBoxDto.getName(), + createLootBoxDto.getPrice(), + rewardEntities + ); + + this.lootBoxRepository.save(lootBoxEntity); + + return ResponseEntity.ok(lootBoxEntity); + } + + @DeleteMapping("/lootboxes/{id}") + public ResponseEntity deleteLootbox(@PathVariable Long id) { + Optional optionalLootBox = lootBoxRepository.findById(id); + if (optionalLootBox.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + LootBoxEntity lootBox = optionalLootBox.get(); + lootBoxRepository.delete(lootBox); + + return ResponseEntity.ok(Collections.singletonMap("message", "successfully deleted lootbox")); + } + +} diff --git a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java index d80370e..647b69b 100644 --- a/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java +++ b/backend/src/main/java/de/szut/casino/lootboxes/LootBoxService.java @@ -1,40 +1,40 @@ -package de.szut.casino.lootboxes; - -import de.szut.casino.user.UserEntity; -import de.szut.casino.user.UserRepository; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; - -@Service -public class LootBoxService { - private final UserRepository userRepository; - - public LootBoxService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - public boolean hasSufficientBalance(UserEntity user, BigDecimal price) { - return user.getBalance().compareTo(price) < 0; - } - - public RewardEntity determineReward(LootBoxEntity lootBox) { - double randomValue = Math.random(); - BigDecimal cumulativeProbability = BigDecimal.ZERO; - - for (RewardEntity reward : lootBox.getRewards()) { - cumulativeProbability = cumulativeProbability.add(reward.getProbability()); - if (randomValue <= cumulativeProbability.doubleValue()) { - return reward; - } - } - - return lootBox.getRewards().getLast(); - } - - public void handleBalance(UserEntity user, LootBoxEntity lootBox, RewardEntity reward) { - user.setBalance(user.getBalance().subtract(lootBox.getPrice())); - user.setBalance(user.getBalance().add(reward.getValue())); - userRepository.save(user); - } -} +package de.szut.casino.lootboxes; + +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class LootBoxService { + private final UserRepository userRepository; + + public LootBoxService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public boolean hasSufficientBalance(UserEntity user, BigDecimal price) { + return user.getBalance().compareTo(price) < 0; + } + + public RewardEntity determineReward(LootBoxEntity lootBox) { + double randomValue = Math.random(); + BigDecimal cumulativeProbability = BigDecimal.ZERO; + + for (RewardEntity reward : lootBox.getRewards()) { + cumulativeProbability = cumulativeProbability.add(reward.getProbability()); + if (randomValue <= cumulativeProbability.doubleValue()) { + return reward; + } + } + + return lootBox.getRewards().getLast(); + } + + public void handleBalance(UserEntity user, LootBoxEntity lootBox, RewardEntity reward) { + user.setBalance(user.getBalance().subtract(lootBox.getPrice())); + user.setBalance(user.getBalance().add(reward.getValue())); + userRepository.save(user); + } +} diff --git a/frontend/src/app/feature/game/blackjack/blackjack.component.ts b/frontend/src/app/feature/game/blackjack/blackjack.component.ts index 2763b55..56207b7 100644 --- a/frontend/src/app/feature/game/blackjack/blackjack.component.ts +++ b/frontend/src/app/feature/game/blackjack/blackjack.component.ts @@ -51,11 +51,19 @@ export default class BlackjackComponent implements OnInit { debtAmount = signal(0); ngOnInit(): void { + // Initial user load from server this.userService.getCurrentUser().subscribe((user) => { if (user) { this.balance.set(user.balance); } }); + + // Subscribe to user updates for real-time balance changes + this.userService.currentUser$.subscribe((user) => { + if (user) { + this.balance.set(user.balance); + } + }); } private updateGameState(game: BlackjackGame) { @@ -84,9 +92,19 @@ export default class BlackjackComponent implements OnInit { if (isGameOver) { console.log('Game is over, state:', game.state); this.userService.refreshCurrentUser(); - timer(1500).subscribe(() => { - this.showGameResult.set(true); - console.log('Game result dialog shown after delay'); + + // Get the latest balance before showing the result dialog + timer(1000).subscribe(() => { + this.userService.getCurrentUser().subscribe((user) => { + if (user) { + this.balance.set(user.balance); + // Show the result dialog after updating the balance + timer(500).subscribe(() => { + this.showGameResult.set(true); + console.log('Game result dialog shown after delay'); + }); + } + }); }); } } @@ -165,12 +183,20 @@ export default class BlackjackComponent implements OnInit { this.blackjackService.doubleDown(this.currentGameId()!).subscribe({ next: (game) => { this.updateGameState(game); - this.userService.getCurrentUser().subscribe((user) => { - if (user && user.balance < 0) { - this.debtAmount.set(Math.abs(user.balance)); - this.showDebtDialog.set(true); - } + + // Wait a bit to ensure the backend has finished processing + timer(1000).subscribe(() => { + this.userService.getCurrentUser().subscribe((user) => { + if (user) { + this.balance.set(user.balance); + if (user.balance < 0) { + this.debtAmount.set(Math.abs(user.balance)); + this.showDebtDialog.set(true); + } + } + }); }); + this.isActionInProgress.set(false); }, error: (error) => { @@ -184,7 +210,13 @@ export default class BlackjackComponent implements OnInit { onCloseGameResult(): void { console.log('Closing game result dialog'); this.showGameResult.set(false); - this.userService.refreshCurrentUser(); + + // Refresh the balance when dialog is closed and update local state + this.userService.getCurrentUser().subscribe((user) => { + if (user) { + this.balance.set(user.balance); + } + }); } onCloseDebtDialog(): void { diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts index d7199b3..88db765 100644 --- a/frontend/src/app/service/user.service.ts +++ b/frontend/src/app/service/user.service.ts @@ -13,6 +13,19 @@ export class UserService { private http: HttpClient = inject(HttpClient); private authService = inject(AuthService); + constructor() { + // Initialize with the current user from AuthService if available + const currentUser = this.authService.getUser(); + if (currentUser) { + this.currentUserSubject.next(currentUser); + } + + // Subscribe to auth service user updates + this.authService.userSubject.subscribe(user => { + this.currentUserSubject.next(user); + }); + } + public getCurrentUser(): Observable { return this.http.get('/backend/users/me').pipe( catchError(() => EMPTY), diff --git a/frontend/src/app/shared/components/navbar/navbar.component.ts b/frontend/src/app/shared/components/navbar/navbar.component.ts index 5d44f60..cd252f3 100644 --- a/frontend/src/app/shared/components/navbar/navbar.component.ts +++ b/frontend/src/app/shared/components/navbar/navbar.component.ts @@ -10,6 +10,7 @@ import { RouterModule } from '@angular/router'; import { AuthService } from '@service/auth.service'; import { Subscription } from 'rxjs'; import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component'; +import { UserService } from '@service/user.service'; @Component({ selector: 'app-navbar', @@ -21,9 +22,11 @@ import { AnimatedNumberComponent } from '@blackjack/components/animated-number/a export class NavbarComponent implements OnInit, OnDestroy { isMenuOpen = false; private authService: AuthService = inject(AuthService); + private userService: UserService = inject(UserService); isLoggedIn = this.authService.isLoggedIn(); private authSubscription!: Subscription; + private userSubscription!: Subscription; public balance = signal(0); ngOnInit() { @@ -32,10 +35,22 @@ export class NavbarComponent implements OnInit, OnDestroy { this.balance.set(user?.balance ?? 0); }, }); + + // Also subscribe to UserService for real-time balance updates + this.userSubscription = this.userService.currentUser$.subscribe({ + next: (user) => { + if (user) { + this.balance.set(user.balance); + } + }, + }); } ngOnDestroy() { this.authSubscription.unsubscribe(); + if (this.userSubscription) { + this.userSubscription.unsubscribe(); + } } logout() {