diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-06-24 13:29:50 +0200 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-06-24 13:29:50 +0200 |
| commit | 894e67863dbb89a4819e825fcdf7117021082b2a (patch) | |
| tree | fb544ef7fa99ff86489717e793595f503783bb72 /src/frontend/app/data | |
| parent | 7dd9ea97a2f34a35e80c28d59d046f839eb6c60b (diff) | |
Replace leaflet for maplibre, use react-router in framework mode
Diffstat (limited to 'src/frontend/app/data')
| -rw-r--r-- | src/frontend/app/data/StopDataProvider.ts | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/frontend/app/data/StopDataProvider.ts b/src/frontend/app/data/StopDataProvider.ts new file mode 100644 index 0000000..0c1e46e --- /dev/null +++ b/src/frontend/app/data/StopDataProvider.ts @@ -0,0 +1,160 @@ +export interface CachedStopList { + timestamp: number; + data: Stop[]; +} + +export type StopName = { + original: string; + intersect?: string; +} + +export interface Stop { + stopId: number; + name: StopName; + latitude?: number; + longitude?: number; + lines: string[]; + favourite?: boolean; +} + +// In-memory cache and lookup map +let cachedStops: Stop[] | null = null; +let stopsMap: Record<number, Stop> = {}; +// Custom names loaded from localStorage +let customNames: Record<number, string> = {}; + +// Initialize cachedStops and customNames once +async function initStops() { + if (!cachedStops) { + const response = await fetch('/stops.json'); + const stops = await response.json() as Stop[]; + // build array and map + stopsMap = {}; + cachedStops = stops.map(stop => { + const entry = { ...stop, favourite: false } as Stop; + stopsMap[stop.stopId] = entry; + return entry; + }); + // load custom names + const rawCustom = localStorage.getItem('customStopNames'); + if (rawCustom) customNames = JSON.parse(rawCustom) as Record<number, string>; + } +} + +async function getStops(): Promise<Stop[]> { + await initStops(); + // update favourites + const rawFav = localStorage.getItem('favouriteStops'); + const favouriteStops = rawFav ? JSON.parse(rawFav) as number[] : []; + cachedStops!.forEach(stop => stop.favourite = favouriteStops.includes(stop.stopId)); + return cachedStops!; +} + +// New: get single stop by id +async function getStopById(stopId: number): Promise<Stop | undefined> { + await initStops(); + const stop = stopsMap[stopId]; + if (stop) { + const rawFav = localStorage.getItem('favouriteStops'); + const favouriteStops = rawFav ? JSON.parse(rawFav) as number[] : []; + stop.favourite = favouriteStops.includes(stopId); + } + return stop; +} + +// Updated display name to include custom names +function getDisplayName(stop: Stop): string { + if (customNames[stop.stopId]) return customNames[stop.stopId]; + const nameObj = stop.name; + return nameObj.intersect || nameObj.original; +} + +// New: set or remove custom names +function setCustomName(stopId: number, label: string) { + customNames[stopId] = label; + localStorage.setItem('customStopNames', JSON.stringify(customNames)); +} + +function removeCustomName(stopId: number) { + delete customNames[stopId]; + localStorage.setItem('customStopNames', JSON.stringify(customNames)); +} + +// New: get custom label for a stop +function getCustomName(stopId: number): string | undefined { + return customNames[stopId]; +} + +function addFavourite(stopId: number) { + const rawFavouriteStops = localStorage.getItem('favouriteStops'); + let favouriteStops: number[] = []; + if (rawFavouriteStops) { + favouriteStops = JSON.parse(rawFavouriteStops) as number[]; + } + + if (!favouriteStops.includes(stopId)) { + favouriteStops.push(stopId); + localStorage.setItem('favouriteStops', JSON.stringify(favouriteStops)); + } +} + +function removeFavourite(stopId: number) { + const rawFavouriteStops = localStorage.getItem('favouriteStops'); + let favouriteStops: number[] = []; + if (rawFavouriteStops) { + favouriteStops = JSON.parse(rawFavouriteStops) as number[]; + } + + const newFavouriteStops = favouriteStops.filter(id => id !== stopId); + localStorage.setItem('favouriteStops', JSON.stringify(newFavouriteStops)); +} + +function isFavourite(stopId: number): boolean { + const rawFavouriteStops = localStorage.getItem('favouriteStops'); + if (rawFavouriteStops) { + const favouriteStops = JSON.parse(rawFavouriteStops) as number[]; + return favouriteStops.includes(stopId); + } + return false; +} + +const RECENT_STOPS_LIMIT = 10; + +function pushRecent(stopId: number) { + const rawRecentStops = localStorage.getItem('recentStops'); + let recentStops: Set<number> = new Set(); + if (rawRecentStops) { + recentStops = new Set(JSON.parse(rawRecentStops) as number[]); + } + + recentStops.add(stopId); + if (recentStops.size > RECENT_STOPS_LIMIT) { + const iterator = recentStops.values(); + const val = iterator.next().value as number; + recentStops.delete(val); + } + + localStorage.setItem('recentStops', JSON.stringify(Array.from(recentStops))); +} + +function getRecent(): number[] { + const rawRecentStops = localStorage.getItem('recentStops'); + if (rawRecentStops) { + return JSON.parse(rawRecentStops) as number[]; + } + return []; +} + +export default { + getStops, + getStopById, + getCustomName, + getDisplayName, + setCustomName, + removeCustomName, + addFavourite, + removeFavourite, + isFavourite, + pushRecent, + getRecent +}; |
