Compare commits
1 Commits
8c0a561a64
...
feat/issue
| Author | SHA1 | Date | |
|---|---|---|---|
| 5957660802 |
@@ -244,4 +244,21 @@ describe('HostShellComponent gameplay wiring', () => {
|
|||||||
expect(component.nextRoundError).toContain('Session code is required');
|
expect(component.nextRoundError).toContain('Session code is required');
|
||||||
expect(component.finishError).toContain('Session code is required');
|
expect(component.finishError).toContain('Session code is required');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('bootstraps refresh from resolver route context on init', () => {
|
||||||
|
vi.stubGlobal('window', {
|
||||||
|
sessionStorage: {
|
||||||
|
getItem: vi.fn().mockReturnValue(null),
|
||||||
|
setItem: vi.fn(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const component = new HostShellComponent({ snapshot: { data: { routeContext: { sessionCode: 'ab12' } } } } as never);
|
||||||
|
const refreshSpy = vi.spyOn(component, 'refreshSession').mockResolvedValue();
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
expect(component.sessionCode).toBe('AB12');
|
||||||
|
expect(refreshSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, Optional } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { createApiClient } from '../../../../../src/api/client';
|
import { createApiClient } from '../../../../../src/api/client';
|
||||||
import type { FinishGameResponse, ScoreboardResponse } from '../../../../../src/api/types';
|
import type { FinishGameResponse, ScoreboardResponse } from '../../../../../src/api/types';
|
||||||
import { createVerticalSliceController } from '../../../../../src/spa/vertical-slice';
|
import { createVerticalSliceController } from '../../../../../src/spa/vertical-slice';
|
||||||
|
import type { RouteSessionContext } from '../../session-route-context';
|
||||||
|
|
||||||
interface SessionDetail {
|
interface SessionDetail {
|
||||||
session: { code: string; status: string; current_round: number };
|
session: { code: string; status: string; current_round: number };
|
||||||
@@ -80,14 +82,15 @@ export class HostShellComponent implements OnInit {
|
|||||||
private readonly api = createApiClient();
|
private readonly api = createApiClient();
|
||||||
private readonly controller = createVerticalSliceController(this.api);
|
private readonly controller = createVerticalSliceController(this.api);
|
||||||
|
|
||||||
|
constructor(@Optional() private readonly route: ActivatedRoute | null = null) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashRoute = window.location.hash.replace(/^#\/?/, '');
|
const routeContext = this.route?.snapshot.data['routeContext'] as RouteSessionContext | undefined;
|
||||||
const match = hashRoute.match(/^host(?:\/[^/]+)?(?:\/([^/?#]+))?/i);
|
const codeFromRoute = routeContext?.sessionCode ?? '';
|
||||||
const codeFromRoute = match?.[1] ?? '';
|
|
||||||
const storedCode = window.sessionStorage.getItem('wpp.host-session-code') ?? '';
|
const storedCode = window.sessionStorage.getItem('wpp.host-session-code') ?? '';
|
||||||
const candidate = codeFromRoute || storedCode;
|
const candidate = codeFromRoute || storedCode;
|
||||||
|
|
||||||
|
|||||||
@@ -254,6 +254,28 @@ describe('PlayerShellComponent gameplay wiring', () => {
|
|||||||
expect(component.loadingTransition).toBeNull();
|
expect(component.loadingTransition).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hydrates session context from resolver payload on init', () => {
|
||||||
|
const component = new PlayerShellComponent({
|
||||||
|
snapshot: {
|
||||||
|
data: {
|
||||||
|
routeContext: {
|
||||||
|
sessionCode: 'ab12',
|
||||||
|
playerId: 7,
|
||||||
|
token: 'tok-7',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as never);
|
||||||
|
const refreshSpy = vi.spyOn(component, 'refreshSession').mockResolvedValue();
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
expect(component.sessionCode).toBe('AB12');
|
||||||
|
expect(component.playerId).toBe(7);
|
||||||
|
expect(component.sessionToken).toBe('tok-7');
|
||||||
|
expect(refreshSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
it('returnToJoin clears persisted session context and transient state', () => {
|
it('returnToJoin clears persisted session context and transient state', () => {
|
||||||
const values = new Map<string, string>();
|
const values = new Map<string, string>();
|
||||||
const localStorage = {
|
const localStorage = {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit, Optional } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { createApiClient } from '../../../../../src/api/client';
|
import { createApiClient } from '../../../../../src/api/client';
|
||||||
import { createSessionContextStore } from '../../../../../src/spa/session-context-store';
|
import { createSessionContextStore } from '../../../../../src/spa/session-context-store';
|
||||||
import { createVerticalSliceController } from '../../../../../src/spa/vertical-slice';
|
import { createVerticalSliceController } from '../../../../../src/spa/vertical-slice';
|
||||||
|
import type { RouteSessionContext } from '../../session-route-context';
|
||||||
|
|
||||||
interface SessionDetail {
|
interface SessionDetail {
|
||||||
session: { code: string; status: string; current_round: number };
|
session: { code: string; status: string; current_round: number };
|
||||||
@@ -108,7 +110,7 @@ export class PlayerShellComponent implements OnInit, OnDestroy {
|
|||||||
private readonly controller = createVerticalSliceController(createApiClient(), this.sessionContextStore);
|
private readonly controller = createVerticalSliceController(createApiClient(), this.sessionContextStore);
|
||||||
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor(@Optional() private readonly route: ActivatedRoute | null = null) {
|
||||||
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||||
this.connectionState = 'offline';
|
this.connectionState = 'offline';
|
||||||
}
|
}
|
||||||
@@ -120,17 +122,13 @@ export class PlayerShellComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const hashRoute = window.location.hash.replace(/^#\/?/, '');
|
const routeContext = this.route?.snapshot.data['routeContext'] as RouteSessionContext | undefined;
|
||||||
const match = hashRoute.match(/^player(?:\/[^/]+)?(?:\/([^/?#]+))?/i);
|
|
||||||
const codeFromRoute = match?.[1] ?? '';
|
|
||||||
|
|
||||||
const persistedContext = this.sessionContextStore.get();
|
const persistedContext = this.sessionContextStore.get();
|
||||||
if (persistedContext) {
|
|
||||||
this.playerId = persistedContext.playerId;
|
|
||||||
this.sessionToken = persistedContext.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidate = codeFromRoute || persistedContext?.sessionCode || '';
|
this.playerId = routeContext?.playerId ?? persistedContext?.playerId ?? 0;
|
||||||
|
this.sessionToken = routeContext?.token ?? persistedContext?.token ?? '';
|
||||||
|
|
||||||
|
const candidate = routeContext?.sessionCode || persistedContext?.sessionCode || '';
|
||||||
if (!candidate) {
|
if (!candidate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user