Files
weirsoe-party-protocol/frontend/angular/src/app/session-route-context.ts
Asger Geel Weirsoee 4b2b21fe57
All checks were successful
CI / test-and-quality (push) Successful in 3m1s
CI / test-and-quality (pull_request) Successful in 3m1s
Fix route locale resolver to only apply explicit lang param
2026-03-02 01:05:00 +00:00

157 lines
4.4 KiB
TypeScript

import { inject } from '@angular/core';
import { type ActivatedRouteSnapshot, type CanActivateFn, type ResolveFn, Router, type UrlTree } from '@angular/router';
import { createSessionContextStore } from '../../../src/spa/session-context-store';
import { normalizeLocale, resolvePreferredLocale, setPreferredLocale } from './lobby-i18n';
export interface RouteSessionContext {
sessionCode: string | null;
playerId: number | null;
token: string | null;
locale: string;
}
const HOST_STORAGE_KEY = 'wpp.host-session-code';
function normalizeCode(value: string): string {
return value.trim().toUpperCase();
}
function isCodeLike(value: string | null | undefined): value is string {
return !!value && /^[A-Za-z0-9]{4,12}$/.test(value.trim());
}
function hasPlayerSessionContext(sessionCode: string): boolean {
const context = createSessionContextStore(window.localStorage).get();
if (!context) {
return false;
}
return (
isCodeLike(context.sessionCode) &&
normalizeCode(context.sessionCode) === normalizeCode(sessionCode) &&
Number.isInteger(context.playerId) &&
context.playerId > 0 &&
!!context.token.trim()
);
}
export function resolveSessionCode(route: ActivatedRouteSnapshot, mode: 'host' | 'player'): string | null {
const contextParam = route.paramMap.get('context');
const queryCode = route.queryParamMap.get('session');
if (isCodeLike(contextParam)) {
return normalizeCode(contextParam);
}
if (isCodeLike(queryCode)) {
return normalizeCode(queryCode);
}
if (mode === 'player') {
const persisted = createSessionContextStore(window.localStorage).get()?.sessionCode;
if (isCodeLike(persisted)) {
return normalizeCode(persisted);
}
return null;
}
const stored = window.sessionStorage.getItem(HOST_STORAGE_KEY);
if (isCodeLike(stored)) {
return normalizeCode(stored);
}
return null;
}
function resolveRouteLocale(route: ActivatedRouteSnapshot): string {
const langParam = route.queryParamMap.get('lang');
if (langParam !== null) {
const locale = normalizeLocale(langParam);
setPreferredLocale(locale);
return locale;
}
return resolvePreferredLocale();
}
async function sessionExists(code: string): Promise<boolean> {
const response = await fetch(`/lobby/sessions/${encodeURIComponent(code)}`, {
method: 'GET',
headers: { Accept: 'application/json' },
credentials: 'same-origin',
});
return response.ok;
}
async function requireSessionContext(route: ActivatedRouteSnapshot, mode: 'host' | 'player'): Promise<boolean> {
const phase = route.paramMap.get('phase');
const code = resolveSessionCode(route, mode);
if (!phase) {
if (mode === 'host' && code) {
window.sessionStorage.setItem(HOST_STORAGE_KEY, code);
}
return true;
}
if (!code) {
return false;
}
if (mode === 'player' && !hasPlayerSessionContext(code)) {
return false;
}
const ok = await sessionExists(code);
if (!ok) {
return false;
}
if (mode === 'host') {
window.sessionStorage.setItem(HOST_STORAGE_KEY, code);
}
return true;
}
async function guard(mode: 'host' | 'player', route: ActivatedRouteSnapshot): Promise<boolean | UrlTree> {
const router = inject(Router);
const allowed = await requireSessionContext(route, mode);
if (allowed) {
return true;
}
return router.createUrlTree([`/${mode}`]);
}
export const hostRouteGuard: CanActivateFn = (route) => guard('host', route);
export const playerRouteGuard: CanActivateFn = (route) => guard('player', route);
export const hostRouteContextResolver: ResolveFn<RouteSessionContext> = (route) => {
const code = resolveSessionCode(route, 'host');
const locale = resolveRouteLocale(route);
if (code) {
window.sessionStorage.setItem(HOST_STORAGE_KEY, code);
}
return { sessionCode: code, playerId: null, token: null, locale };
};
export const playerRouteContextResolver: ResolveFn<RouteSessionContext> = (route) => {
const code = resolveSessionCode(route, 'player');
const locale = resolveRouteLocale(route);
const context = createSessionContextStore(window.localStorage).get();
if (!code || !context || normalizeCode(context.sessionCode) !== code) {
return { sessionCode: code, playerId: null, token: null, locale };
}
return {
sessionCode: code,
playerId: Number.isInteger(context.playerId) && context.playerId > 0 ? context.playerId : null,
token: context.token.trim() || null,
locale,
};
};