AI Implementation feature(905): Challenges Page and Challenge Solve Modal 1.01 (#47)
This commit was merged in pull request #47.
This commit is contained in:
@@ -73,25 +73,38 @@ export class EventStatusStore {
|
||||
private zeroWatcher: ReturnType<typeof setInterval> | null = null;
|
||||
private reloadOnZero: (() => void) | null = null;
|
||||
private reloadOnZeroFired = false;
|
||||
private lastAppliedJson: string | null = null;
|
||||
|
||||
constructor() {
|
||||
this.destroyRef.onDestroy(() => this.stop());
|
||||
}
|
||||
|
||||
applyServerStatus(payload: EventStatePayload): void {
|
||||
const json = JSON.stringify(payload);
|
||||
if (this.lastAppliedJson === json) {
|
||||
// Same payload delivered again (e.g. SSE tick that duplicates the
|
||||
// initial frame). Ignore it so the reload latch is not silently
|
||||
// disarmed after the watcher has already fired.
|
||||
return;
|
||||
}
|
||||
this.lastAppliedJson = json;
|
||||
const isTransition =
|
||||
this.state() !== payload.state ||
|
||||
this.eventStartUtc() !== payload.eventStartUtc ||
|
||||
this.eventEndUtc() !== payload.eventEndUtc;
|
||||
this.state.set(payload.state);
|
||||
this.serverNowUtc.set(payload.serverNowUtc);
|
||||
this.eventStartUtc.set(payload.eventStartUtc);
|
||||
this.eventEndUtc.set(payload.eventEndUtc);
|
||||
this.clientAnchorMs.set(Date.now());
|
||||
// Reset the one-shot so a subsequent transition (e.g. countdown→running) can fire again.
|
||||
if (this.reloadOnZeroFired) {
|
||||
if (isTransition && this.reloadOnZeroFired) {
|
||||
this.reloadOnZeroFired = false;
|
||||
}
|
||||
}
|
||||
|
||||
start(createSource: () => EventSourceLike, onUnauthorized?: () => void): void {
|
||||
this.stop();
|
||||
this.closeTransport();
|
||||
const src = createSource();
|
||||
this.source = src;
|
||||
const handler = (ev: MessageEvent | Event) => {
|
||||
@@ -119,14 +132,48 @@ export class EventStatusStore {
|
||||
if (!this.intervalId) {
|
||||
this.intervalId = setInterval(() => this.tick.update((v) => v + 1), 1000);
|
||||
}
|
||||
if (!this.zeroWatcher) {
|
||||
this.zeroWatcher = setInterval(() => this.checkZero(), 1000);
|
||||
}
|
||||
this.ensureWatcher();
|
||||
}
|
||||
|
||||
reloadAtCountdownZero(handler: () => void): void {
|
||||
this.subscribeReloadAtCountdownZero(handler);
|
||||
}
|
||||
|
||||
subscribeReloadAtCountdownZero(handler: () => void): () => void {
|
||||
this.reloadOnZero = handler;
|
||||
this.reloadOnZeroFired = false;
|
||||
this.ensureWatcher();
|
||||
return () => {
|
||||
if (this.reloadOnZero === handler) {
|
||||
this.reloadOnZero = null;
|
||||
this.reloadOnZeroFired = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private closeTransport(): void {
|
||||
if (this.source) {
|
||||
try {
|
||||
this.source.close();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
this.source = null;
|
||||
}
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
}
|
||||
if (this.zeroWatcher) {
|
||||
clearInterval(this.zeroWatcher);
|
||||
this.zeroWatcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
private ensureWatcher(): void {
|
||||
if (!this.zeroWatcher) {
|
||||
this.zeroWatcher = setInterval(() => this.checkZero(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private checkZero(): void {
|
||||
@@ -137,12 +184,24 @@ export class EventStatusStore {
|
||||
if (s !== null && s <= 0) {
|
||||
this.reloadOnZeroFired = true;
|
||||
try { this.reloadOnZero(); } catch { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
const startMs = new Date(this.eventStartUtc() ?? '').getTime();
|
||||
if (Number.isFinite(startMs) && startMs <= Date.now()) {
|
||||
this.reloadOnZeroFired = true;
|
||||
try { this.reloadOnZero(); } catch { /* ignore */ }
|
||||
}
|
||||
} else if (state === 'running') {
|
||||
const s = this.secondsToEnd();
|
||||
if (s !== null && s <= 0) {
|
||||
this.reloadOnZeroFired = true;
|
||||
try { this.reloadOnZero(); } catch { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
const endMs = new Date(this.eventEndUtc() ?? '').getTime();
|
||||
if (Number.isFinite(endMs) && endMs <= Date.now()) {
|
||||
this.reloadOnZeroFired = true;
|
||||
try { this.reloadOnZero(); } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,5 +223,8 @@ export class EventStatusStore {
|
||||
clearInterval(this.zeroWatcher);
|
||||
this.zeroWatcher = null;
|
||||
}
|
||||
this.reloadOnZero = null;
|
||||
this.reloadOnZeroFired = false;
|
||||
this.lastAppliedJson = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user