fix : Stop bet is fixed partially
This commit is contained in:
parent
89009d28c4
commit
d1b52dc64c
@ -1,16 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { NgZone } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class StopbetService {
|
||||
export class StopbetService implements OnDestroy {
|
||||
private stopbetStatuses: Map<number, string> = new Map(); // raceNum => 'Y'|'N'|'S'
|
||||
private stopbetStatusesSubject = new BehaviorSubject<Map<number, string>>(this.stopbetStatuses);
|
||||
private currentVenue: string = '';
|
||||
private currentDate: string = '';
|
||||
private eventSource?: EventSource;
|
||||
private readonly STORAGE_KEY = 'stopbetStatuses';
|
||||
private readonly STORAGE_KEY = 'Stopbet'; // single key that persists across refreshes
|
||||
|
||||
constructor(private http: HttpClient, private zone: NgZone) {}
|
||||
|
||||
@ -20,48 +20,55 @@ export class StopbetService {
|
||||
}
|
||||
|
||||
// Initialize stopbet for a venue and date
|
||||
// NOTE: we DO NOT clear localStorage here — storage is preserved across refreshes.
|
||||
initialize(venue: string, date: string) {
|
||||
const newVenue = venue ? venue.toUpperCase() : '';
|
||||
const newDate = date || this.getTodayDate();
|
||||
|
||||
// Only clear if venue and date are valid and different
|
||||
if (newVenue && newDate && (this.currentVenue !== newVenue || this.currentDate !== newDate)) {
|
||||
console.log(
|
||||
`[STOPBET] Venue or date changed (from ${this.currentVenue}:${this.currentDate} to ${newVenue}:${newDate}), clearing stopbetStatuses`
|
||||
`[STOPBET] Venue/date changed (from ${this.currentVenue}:${this.currentDate} to ${newVenue}:${newDate}). Keeping Stopbet storage intact until explicit logout.`
|
||||
);
|
||||
this.stopbetStatuses.clear();
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
} else {
|
||||
console.log(
|
||||
`[STOPBET] No clear needed: newVenue=${newVenue}, newDate=${newDate}, currentVenue=${this.currentVenue}, currentDate=${this.currentDate}`
|
||||
`[STOPBET] initialize called: newVenue=${newVenue}, newDate=${newDate}, currentVenue=${this.currentVenue}, currentDate=${this.currentDate}`
|
||||
);
|
||||
}
|
||||
|
||||
this.currentVenue = newVenue;
|
||||
this.currentDate = newDate;
|
||||
|
||||
// Load from localStorage first
|
||||
// Load from localStorage (Stopbet) if present
|
||||
this.loadFromLocalStorage();
|
||||
|
||||
// Fetch from server and setup SSE
|
||||
// Fetch from server and setup SSE (these will call setStatus which persists)
|
||||
this.fetchInitialStopbets();
|
||||
this.setupSSE();
|
||||
}
|
||||
|
||||
// Public method to set race status
|
||||
setStatus(raceNum: number, status: string) {
|
||||
if (!Number.isFinite(raceNum) || raceNum <= 0) {
|
||||
console.warn(`[STOPBET] Ignoring invalid raceNum: ${raceNum}`);
|
||||
return;
|
||||
}
|
||||
const normalizedStatus = String(status).toUpperCase();
|
||||
if (!['Y', 'N', 'S'].includes(normalizedStatus)) {
|
||||
console.warn(`[STOPBET] Ignoring invalid status for race ${raceNum}: ${status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const oldStatus = this.stopbetStatuses.get(raceNum);
|
||||
if (oldStatus === status) {
|
||||
console.log(`[STOPBET] No change for race ${raceNum}: status remains ${status}`);
|
||||
if (oldStatus === normalizedStatus) {
|
||||
console.log(`[STOPBET] No change for race ${raceNum}: status remains ${normalizedStatus}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the specified race status
|
||||
this.stopbetStatuses.set(raceNum, status);
|
||||
this.stopbetStatuses.set(raceNum, normalizedStatus);
|
||||
|
||||
// If stopping this race, auto-disable previous races
|
||||
if (status === 'Y' || status === 'S') {
|
||||
if (normalizedStatus === 'Y' || normalizedStatus === 'S') {
|
||||
for (let prevRace = 1; prevRace < raceNum; prevRace++) {
|
||||
const prevStatus = this.stopbetStatuses.get(prevRace);
|
||||
if (prevStatus === 'N' || prevStatus === undefined) {
|
||||
@ -76,65 +83,105 @@ export class StopbetService {
|
||||
this.saveToLocalStorage();
|
||||
}
|
||||
|
||||
// Load Stopbet entry from localStorage if it is parseable.
|
||||
// We will only apply it if the stored venue/date match currentVenue/currentDate.
|
||||
private loadFromLocalStorage() {
|
||||
const cached = localStorage.getItem(this.STORAGE_KEY);
|
||||
if (cached) {
|
||||
try {
|
||||
const parsed = JSON.parse(cached);
|
||||
if (parsed.venue === this.currentVenue && parsed.date === this.currentDate) {
|
||||
this.stopbetStatuses.clear();
|
||||
for (const [race, status] of Object.entries(parsed.statuses)) {
|
||||
const raceNum = parseInt(race, 10);
|
||||
if (!isNaN(raceNum) && ['Y', 'N', 'S'].includes(status as string)) {
|
||||
this.stopbetStatuses.set(raceNum, status as string);
|
||||
}
|
||||
}
|
||||
console.log('[STOPBET] Loaded statuses from localStorage:', this.stopbetStatuses);
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
} else {
|
||||
console.log(
|
||||
`[STOPBET] localStorage data invalid for venue=${this.currentVenue}, date=${this.currentDate}:`,
|
||||
parsed
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to parse localStorage:', err);
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
if (!cached) {
|
||||
console.log('[STOPBET] No Stopbet entry found in localStorage');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(cached);
|
||||
if (!parsed || typeof parsed !== 'object') {
|
||||
console.warn('[STOPBET] Stopbet entry has unexpected shape, ignoring.');
|
||||
return;
|
||||
}
|
||||
|
||||
// If stored venue/date match current ones, load statuses into memory
|
||||
if (parsed.venue === this.currentVenue && parsed.date === this.currentDate && parsed.statuses) {
|
||||
this.stopbetStatuses.clear();
|
||||
for (const [race, status] of Object.entries(parsed.statuses)) {
|
||||
const raceNum = parseInt(race, 10);
|
||||
const s = String(status).toUpperCase();
|
||||
if (!isNaN(raceNum) && ['Y', 'N', 'S'].includes(s)) {
|
||||
this.stopbetStatuses.set(raceNum, s);
|
||||
}
|
||||
}
|
||||
console.log('[STOPBET] Loaded Stopbet from localStorage for current venue/date:', {
|
||||
venue: parsed.venue,
|
||||
date: parsed.date,
|
||||
statuses: parsed.statuses,
|
||||
});
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
} else {
|
||||
// If the stored Stopbet is for a different venue/date, keep it in storage (do not remove).
|
||||
console.log(
|
||||
`[STOPBET] Stopbet in storage is for ${parsed.venue}:${parsed.date} — not loading into current ${this.currentVenue}:${this.currentDate}.`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to parse Stopbet from localStorage:', err);
|
||||
// If corrupted, remove it to allow fresh writes next time
|
||||
try {
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
console.warn('[STOPBET] Removed corrupted Stopbet entry from localStorage.');
|
||||
} catch (e) {
|
||||
console.error('[STOPBET] Failed to remove corrupted Stopbet entry:', e);
|
||||
}
|
||||
} else {
|
||||
console.log('[STOPBET] No stopbetStatuses found in localStorage');
|
||||
}
|
||||
}
|
||||
|
||||
// Save the current statuses under Stopbet key (includes venue/date and a timestamp)
|
||||
private saveToLocalStorage() {
|
||||
try {
|
||||
const data = {
|
||||
venue: this.currentVenue,
|
||||
date: this.currentDate,
|
||||
statuses: Object.fromEntries(this.stopbetStatuses),
|
||||
statuses: Object.fromEntries(this.stopbetStatuses), // { "1":"Y", ... }
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data));
|
||||
console.log('[STOPBET] Saved statuses to localStorage:', data);
|
||||
console.log('[STOPBET] Saved Stopbet to localStorage:', data);
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to save to localStorage:', err);
|
||||
console.error('[STOPBET] Failed to save Stopbet to localStorage:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Public helper to clear the Stopbet entry and reset in-memory statuses (call on logout)
|
||||
public clearStopbetStorage() {
|
||||
try {
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
this.stopbetStatuses.clear();
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
console.log('[STOPBET] Cleared Stopbet storage and in-memory statuses (logout reset)');
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to clear Stopbet storage:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch initial statuses from backend and apply them
|
||||
private fetchInitialStopbets() {
|
||||
if (!this.currentVenue || !this.currentDate) {
|
||||
console.log('[STOPBET] fetchInitialStopbets skipped: venue/date not set');
|
||||
return;
|
||||
}
|
||||
|
||||
this.http
|
||||
.get(`http://localhost:8080/stopbet/raw?venue=${this.currentVenue}&date=${this.currentDate}`)
|
||||
.subscribe({
|
||||
next: (res: any) => {
|
||||
if (res.ok && res.data) {
|
||||
if (res && res.ok && res.data) {
|
||||
const key = `${this.currentVenue}:${this.currentDate}`;
|
||||
const meeting = res.data[key] || {};
|
||||
this.stopbetStatuses.clear();
|
||||
// Use setStatus to ensure auto-stop logic and persistence run
|
||||
for (const race in meeting) {
|
||||
const raceNum = parseInt(race, 10);
|
||||
const status = meeting[race];
|
||||
this.setStatus(raceNum, status);
|
||||
}
|
||||
console.log('[STOPBET] Fetched initial statuses:', this.stopbetStatuses);
|
||||
console.log('[STOPBET] Fetched initial statuses and applied them.');
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
this.saveToLocalStorage();
|
||||
} else {
|
||||
@ -147,16 +194,28 @@ export class StopbetService {
|
||||
});
|
||||
}
|
||||
|
||||
// Setup Server-Sent Events stream for real-time updates
|
||||
private setupSSE() {
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
try {
|
||||
this.eventSource.close();
|
||||
} catch (e) {
|
||||
/* ignore close errors */
|
||||
}
|
||||
}
|
||||
this.eventSource = new EventSource('http://localhost:8080/stopbet/stream');
|
||||
|
||||
try {
|
||||
this.eventSource = new EventSource('http://localhost:8080/stopbet/stream');
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to create EventSource:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventSource.onmessage = (event) => {
|
||||
this.zone.run(() => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'stopbet' && data.venue === this.currentVenue && data.date === this.currentDate) {
|
||||
if (data && data.type === 'stopbet' && data.venue === this.currentVenue && data.date === this.currentDate) {
|
||||
const raceNum = parseInt(data.race, 10);
|
||||
this.setStatus(raceNum, data.status);
|
||||
console.log('[STOPBET] SSE updated race', raceNum, 'to', data.status);
|
||||
@ -166,17 +225,23 @@ export class StopbetService {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.eventSource.onerror = (err) => {
|
||||
console.error('[STOPBET] SSE error:', err);
|
||||
};
|
||||
}
|
||||
|
||||
// Returns true if betting is open for the given race
|
||||
isOpen(race: number): boolean {
|
||||
const status = this.stopbetStatuses.get(race);
|
||||
return status === 'N' || status === undefined;
|
||||
}
|
||||
|
||||
// Find open race starting from 'start' up to maxRaces, wrapping around
|
||||
getOpenRaceStartingFrom(start: number, maxRaces: number): number {
|
||||
if (!Number.isFinite(start) || !Number.isFinite(maxRaces) || maxRaces <= 0) {
|
||||
return start;
|
||||
}
|
||||
for (let r = start; r <= maxRaces; r++) {
|
||||
if (this.isOpen(r)) return r;
|
||||
}
|
||||
@ -196,7 +261,11 @@ export class StopbetService {
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
try {
|
||||
this.eventSource.close();
|
||||
} catch (e) {
|
||||
/* ignore close errors */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user