aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/data/StopDataProvider.ts
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-06-24 13:29:50 +0200
committerAriel Costas Guerrero <ariel@costas.dev>2025-06-24 13:29:50 +0200
commit894e67863dbb89a4819e825fcdf7117021082b2a (patch)
treefb544ef7fa99ff86489717e793595f503783bb72 /src/frontend/app/data/StopDataProvider.ts
parent7dd9ea97a2f34a35e80c28d59d046f839eb6c60b (diff)
Replace leaflet for maplibre, use react-router in framework mode
Diffstat (limited to 'src/frontend/app/data/StopDataProvider.ts')
-rw-r--r--src/frontend/app/data/StopDataProvider.ts160
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
+};