()
| 427 | * - plays a bell on transition from queued → active |
| 428 | */ |
| 429 | export function useFreebuffSession(): UseFreebuffSessionResult { |
| 430 | const session = useFreebuffSessionStore((s) => s.session) |
| 431 | const error = useFreebuffSessionStore((s) => s.error) |
| 432 | |
| 433 | useEffect(() => { |
| 434 | const { setSession, setError } = useFreebuffSessionStore.getState() |
| 435 | |
| 436 | if (!IS_FREEBUFF) { |
| 437 | setSession({ status: 'disabled' }) |
| 438 | return |
| 439 | } |
| 440 | |
| 441 | const { token } = getAuthTokenDetails() |
| 442 | if (!token) { |
| 443 | logger.warn( |
| 444 | {}, |
| 445 | '[freebuff-session] No auth token; skipping waiting-room admission', |
| 446 | ) |
| 447 | setError('Not authenticated') |
| 448 | return |
| 449 | } |
| 450 | |
| 451 | let cancelled = false |
| 452 | let abortController = new AbortController() |
| 453 | let timer: ReturnType<typeof setTimeout> | null = null |
| 454 | let previousStatus: FreebuffSessionResponse['status'] | null = null |
| 455 | let restartGeneration = 0 |
| 456 | // Method for the NEXT tick. GET is read-only; POST claims/rotates a seat. |
| 457 | // Startup is GET (probe before committing). After any POST completes we |
| 458 | // flip back to GET. refresh() sets it to 'POST' for explicit join/rejoin; |
| 459 | // the startup takeover branch does the same when the probe finds a seat. |
| 460 | let nextMethod: 'GET' | 'POST' = 'GET' |
| 461 | |
| 462 | const apply = (next: FreebuffSessionResponse) => { |
| 463 | if (next.status === 'queued' || next.status === 'active') { |
| 464 | useFreebuffModelStore.getState().setSelectedModel(next.model) |
| 465 | recordFreebuffInstanceOwner(next.instanceId) |
| 466 | } else if (next.status === 'none' && next.accessTier === 'limited') { |
| 467 | useFreebuffModelStore |
| 468 | .getState() |
| 469 | .setSelectedModel(LIMITED_FREEBUFF_MODEL_ID) |
| 470 | } |
| 471 | setSession(next) |
| 472 | setError(null) |
| 473 | previousStatus = next.status |
| 474 | } |
| 475 | |
| 476 | const clearTimer = () => { |
| 477 | if (timer) { |
| 478 | clearTimeout(timer) |
| 479 | timer = null |
| 480 | } |
| 481 | } |
| 482 | |
| 483 | const schedule = (ms: number) => { |
| 484 | if (cancelled) return |
| 485 | clearTimer() |
| 486 | timer = setTimeout(tick, ms) |
no test coverage detected