Compare commits
35 Commits
0.2.3
..
fd8ac63dc3
| Author | SHA1 | Date | |
|---|---|---|---|
| fd8ac63dc3 | |||
| c4575fd219 | |||
| 8eb27ba8b9 | |||
| 7de4f3784b | |||
| 04df2250e9 | |||
| 99100a3086 | |||
| e3466bda30 | |||
| 550db1401b | |||
| 6a79be45bf | |||
| 82bf006f18 | |||
| 13edcb2f69 | |||
| 49d6aae1db | |||
| eadcd770ba | |||
| a3255602b3 | |||
| d471eef7af | |||
| 361ce1e817 | |||
| aa70083aed | |||
| 3b757d7ff7 | |||
| 19f3359106 | |||
| d676288315 | |||
| 671886781e | |||
| dc9a7b2e32 | |||
| 5951257c99 | |||
| a59e2a023b | |||
| 4f76bcc7c6 | |||
| 25b69fd7b6 | |||
| c18026bce6 | |||
| 91fa247696 | |||
| 97365371c8 | |||
| fdc0f1d73b | |||
| bc644c16e3 | |||
| 5497997455 | |||
| 53459648c6 | |||
| 8c97a726a7 | |||
| 2582f8e4d6 |
@@ -18,23 +18,3 @@
|
|||||||
* removed cache files ([7ee59c4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/7ee59c434bf137a08fd560bbc02ceefbcfd90f04))
|
* removed cache files ([7ee59c4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/7ee59c434bf137a08fd560bbc02ceefbcfd90f04))
|
||||||
* size of pieces and removed text view of the game state ([c60d00f](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/c60d00f9d25247504845654615065fbccd7fe448))
|
* size of pieces and removed text view of the game state ([c60d00f](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/c60d00f9d25247504845654615065fbccd7fe448))
|
||||||
* structure ([3e8c7c4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/3e8c7c4057e55aeec7cee8c24f6751ff24912c93))
|
* structure ([3e8c7c4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/3e8c7c4057e55aeec7cee8c24f6751ff24912c93))
|
||||||
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.1.0...0.0.0) (2026-05-12)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* NCS-69 Challenge request ([#3](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/issues/3)) ([bad7366](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/bad7366bdbb048c20218257b30ac22efc9ecb6db))
|
|
||||||
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.2.0...0.0.0) (2026-05-12)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* NCWF-1 401 ([#5](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/issues/5)) ([f8f93ef](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/f8f93efff48f1d7198023fed45b675c2e225df36))
|
|
||||||
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.2.1...0.0.0) (2026-05-12)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* NCWF-1 401 ([#6](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/issues/6)) ([6d1e06d](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/6d1e06dfd606b93d029e9c9b84eea6f8b3b6294e))
|
|
||||||
## [0.0.0](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/compare/0.2.2...0.0.0) (2026-05-14)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* added missing challenge routes ([61000f8](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/61000f8a22aff8b524664a756cc933834365f923))
|
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import { Routes } from '@angular/router';
|
|||||||
import { GameComponent } from './pages/game/game.component';
|
import { GameComponent } from './pages/game/game.component';
|
||||||
import { WelcomeComponent } from './pages/welcome/welcome.component';
|
import { WelcomeComponent } from './pages/welcome/welcome.component';
|
||||||
import { ProfileComponent } from './pages/profile/profile.component';
|
import { ProfileComponent } from './pages/profile/profile.component';
|
||||||
import { ChallengesComponent } from './pages/challenges/challenges.component';
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: WelcomeComponent },
|
{ path: '', component: WelcomeComponent },
|
||||||
{ path: 'profile', component: ProfileComponent },
|
{ path: 'profile', component: ProfileComponent },
|
||||||
{ path: 'challenges', component: ChallengesComponent },
|
|
||||||
{ path: 'game/:gameId', component: GameComponent },
|
{ path: 'game/:gameId', component: GameComponent },
|
||||||
{ path: '**', redirectTo: '' }
|
{ path: '**', redirectTo: '' }
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -263,14 +263,6 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (showChallengeDialog) {
|
|
||||||
<div class="dialog-overlay" (click)="closeChallengeDialog()">
|
|
||||||
<div class="dialog-card" (click)="$event.stopPropagation()">
|
|
||||||
<app-challenge-create-dialog (closeChallengeDialog)="closeChallengeDialog()"></app-challenge-create-dialog>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (errorMessage) {
|
@if (errorMessage) {
|
||||||
<p class="error-banner">{{ errorMessage }}</p>
|
<p class="error-banner">{{ errorMessage }}</p>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ export class WelcomeComponent implements OnInit, OnDestroy {
|
|||||||
showOptionsDialog = false;
|
showOptionsDialog = false;
|
||||||
showJoinDialog = false;
|
showJoinDialog = false;
|
||||||
showImportDialog = false;
|
showImportDialog = false;
|
||||||
showChallengeDialog = false;
|
|
||||||
|
|
||||||
gameIdInput = '';
|
gameIdInput = '';
|
||||||
importMode: ImportMode = 'fen';
|
importMode: ImportMode = 'fen';
|
||||||
@@ -224,21 +223,11 @@ export class WelcomeComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startOneVsOne(): void {
|
startOneVsOne(): void {
|
||||||
if (!this.requireAuth(() => this.openChallengeDialog())) {
|
if (!this.requireAuth(() => this.performStartOneVsOne())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openChallengeDialog();
|
this.performStartOneVsOne();
|
||||||
}
|
|
||||||
|
|
||||||
openChallengeDialog(): void {
|
|
||||||
this.closeAllDialogs();
|
|
||||||
this.showChallengeDialog = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
closeChallengeDialog(): void {
|
|
||||||
this.showChallengeDialog = false;
|
|
||||||
this.errorMessage = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startVsBot(difficulty: Difficulty): void {
|
startVsBot(difficulty: Difficulty): void {
|
||||||
@@ -363,6 +352,28 @@ export class WelcomeComponent implements OnInit, OnDestroy {
|
|||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private performStartOneVsOne(): void {
|
||||||
|
if (this.creating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.creating = true;
|
||||||
|
|
||||||
|
this.gameApi
|
||||||
|
.createGame()
|
||||||
|
.pipe(finalize(() => (this.creating = false)))
|
||||||
|
.subscribe({
|
||||||
|
next: (game) => {
|
||||||
|
void this.router.navigate(['/game', game.gameId], {
|
||||||
|
state: { theme: this.isSunsetMode ? 'light' : 'dark' }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.errorMessage = getErrorMessage(error, 'Unable to create a game.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private performStartVsBot(difficulty: Difficulty): void {
|
private performStartVsBot(difficulty: Difficulty): void {
|
||||||
if (this.creating) {
|
if (this.creating) {
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { HttpInterceptorFn } from '@angular/common/http';
|
|||||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
|
// Add token to protected endpoints only (not registration or login)
|
||||||
const isProtectedEndpoint =
|
const isProtectedEndpoint =
|
||||||
req.url.includes('/api/account/me') ||
|
req.url.includes('/api/account/me') ||
|
||||||
req.url.includes('/api/account/bots') ||
|
req.url.includes('/api/account/bots') ||
|
||||||
req.url.includes('/api/account/official-bots') ||
|
req.url.includes('/api/account/official-bots') ||
|
||||||
req.url.includes('/api/board/game') ||
|
|
||||||
req.url.includes('/api/challenge');
|
req.url.includes('/api/challenge');
|
||||||
|
|
||||||
if (token && isProtectedEndpoint) {
|
if (token && isProtectedEndpoint) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export class GameStreamService {
|
|||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private streamSubscription: Subscription | null = null;
|
private streamSubscription: Subscription | null = null;
|
||||||
private pollSubscription: Subscription | null = null;
|
private pollSubscription: Subscription | null = null;
|
||||||
private lastGameStateHash: string | null = null;
|
|
||||||
|
|
||||||
startStreaming(
|
startStreaming(
|
||||||
gameId: string,
|
gameId: string,
|
||||||
@@ -21,10 +20,7 @@ export class GameStreamService {
|
|||||||
.streamGame(gameId)
|
.streamGame(gameId)
|
||||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (event) => {
|
next: (event) => onEvent(event),
|
||||||
this.lastGameStateHash = JSON.stringify(event);
|
|
||||||
onEvent(event);
|
|
||||||
},
|
|
||||||
error: () => {
|
error: () => {
|
||||||
onStreamError();
|
onStreamError();
|
||||||
this.startPolling(gameId, onEvent);
|
this.startPolling(gameId, onEvent);
|
||||||
@@ -41,7 +37,7 @@ export class GameStreamService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pollSubscription = interval(5000)
|
this.pollSubscription = interval(1500)
|
||||||
.pipe(
|
.pipe(
|
||||||
startWith(0),
|
startWith(0),
|
||||||
switchMap(() => this.gameApi.getGame(gameId)),
|
switchMap(() => this.gameApi.getGame(gameId)),
|
||||||
@@ -49,16 +45,11 @@ export class GameStreamService {
|
|||||||
)
|
)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (game) => {
|
next: (game) => {
|
||||||
// Only emit if game state changed to avoid unnecessary updates
|
const event: GameStreamEvent = {
|
||||||
const stateHash = JSON.stringify(game.state);
|
type: 'gameFull',
|
||||||
if (this.lastGameStateHash !== stateHash) {
|
game
|
||||||
this.lastGameStateHash = stateHash;
|
};
|
||||||
const event: GameStreamEvent = {
|
onEvent(event);
|
||||||
type: 'gameFull',
|
|
||||||
game
|
|
||||||
};
|
|
||||||
onEvent(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -68,6 +59,5 @@ export class GameStreamService {
|
|||||||
this.pollSubscription?.unsubscribe();
|
this.pollSubscription?.unsubscribe();
|
||||||
this.streamSubscription = null;
|
this.streamSubscription = null;
|
||||||
this.pollSubscription = null;
|
this.pollSubscription = null;
|
||||||
this.lastGameStateHash = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,19 +84,8 @@ export class StreamHandlerService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set timeout to fallback if WebSocket doesn't connect quickly
|
|
||||||
const connectionTimeoutId = setTimeout(() => {
|
|
||||||
if (!connected && !fallbackActive) {
|
|
||||||
console.warn(`[StreamHandler] WebSocket timeout for ${gameId}, attempting NDJSON fallback`);
|
|
||||||
ws.close();
|
|
||||||
void startNdjsonFallback();
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
connected = true;
|
connected = true;
|
||||||
clearTimeout(connectionTimeoutId);
|
|
||||||
console.log(`[StreamHandler] WebSocket connected for ${gameId}`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = (message) => {
|
ws.onmessage = (message) => {
|
||||||
@@ -108,23 +97,19 @@ export class StreamHandlerService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
ws.onerror = (error) => {
|
||||||
console.warn(`[StreamHandler] WebSocket error for ${gameId}:`, error);
|
console.warn(`[StreamHandler] WebSocket error for ${gameId}, attempting NDJSON fallback:`, error);
|
||||||
clearTimeout(connectionTimeoutId);
|
if (!connected) {
|
||||||
if (!connected && !fallbackActive) {
|
|
||||||
void startNdjsonFallback();
|
void startNdjsonFallback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
clearTimeout(connectionTimeoutId);
|
|
||||||
console.warn(`[StreamHandler] WebSocket closed for ${gameId}, connected=${connected}`);
|
console.warn(`[StreamHandler] WebSocket closed for ${gameId}, connected=${connected}`);
|
||||||
if (connected) {
|
if (!connected) {
|
||||||
// Connection was established but closed, stream is complete
|
|
||||||
observer.complete();
|
|
||||||
} else if (!fallbackActive) {
|
|
||||||
// Connection never established, try fallback
|
|
||||||
console.log(`[StreamHandler] Starting NDJSON fallback for ${gameId}`);
|
console.log(`[StreamHandler] Starting NDJSON fallback for ${gameId}`);
|
||||||
void startNdjsonFallback();
|
void startNdjsonFallback();
|
||||||
|
} else {
|
||||||
|
observer.complete();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=0
|
MAJOR=0
|
||||||
MINOR=2
|
MINOR=1
|
||||||
PATCH=3
|
PATCH=0
|
||||||
|
|||||||
Reference in New Issue
Block a user