From 340865ad234f974ec4c0afcbfb6ff06f50a11b2f Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Sun, 20 Apr 2025 19:07:29 +0200 Subject: Load stops from local data files --- src/AppContext.tsx | 2 +- src/components/StopItem.css | 54 +++++++++++++++ src/components/StopItem.tsx | 6 +- src/data/StopDataProvider.ts | 154 ++++++++++++++++++++++++------------------- src/pages/Estimates.tsx | 12 ++-- src/pages/Map.tsx | 16 +++-- src/pages/StopList.tsx | 8 +-- 7 files changed, 166 insertions(+), 86 deletions(-) create mode 100644 src/components/StopItem.css (limited to 'src') diff --git a/src/AppContext.tsx b/src/AppContext.tsx index 4ff391b..a9af208 100644 --- a/src/AppContext.tsx +++ b/src/AppContext.tsx @@ -155,7 +155,7 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { ); } } - }, []); + }, [mapState.hasLocationPermission, mapState.userLocation]); return ( = ({ stop }) => { + return (
  • - ({stop.stopId}) {stop.name} + {stop.favourite && } ({stop.stopId}) {StopDataProvider.getDisplayName(stop)}
    {stop.lines?.map(line => )}
    +
  • ); diff --git a/src/data/StopDataProvider.ts b/src/data/StopDataProvider.ts index 11674fd..55d0e78 100644 --- a/src/data/StopDataProvider.ts +++ b/src/data/StopDataProvider.ts @@ -3,91 +3,113 @@ export interface CachedStopList { data: Stop[]; } +export type StopName = { + original: string; + intersect?: string; +} + export interface Stop { - stopId: number - name: string; + stopId: number; + name: StopName; latitude?: number; longitude?: number; lines: string[]; favourite?: boolean; } -export class StopDataProvider { - async getStops(): Promise { - const rawFavouriteStops = localStorage.getItem('favouriteStops'); - let favouriteStops: number[] = []; - if (rawFavouriteStops) { - favouriteStops = JSON.parse(rawFavouriteStops) as number[]; - } - - const response = await fetch('/api/GetStopList'); - const stops = await response.json() as Stop[]; - - return stops.map((stop: Stop) => { - return { - ...stop, - favourite: favouriteStops.includes(stop.stopId) - }; - }); +export default { + getStops, + getDisplayName, + addFavourite, + removeFavourite, + isFavourite, + pushRecent, + getRecent +}; + +async function getStops(): Promise { + const rawFavouriteStops = localStorage.getItem('favouriteStops'); + let favouriteStops: number[] = []; + if (rawFavouriteStops) { + favouriteStops = JSON.parse(rawFavouriteStops) as number[]; } - 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)); - } + const response = await fetch('/stops.json'); + const stops = await response.json() as Stop[]; + + return stops.map((stop: Stop) => { + return { + ...stop, + favourite: favouriteStops.includes(stop.stopId) + }; + }); +} + +// Get display name based on preferences or context +function getDisplayName(stop: Stop): string { + if (typeof stop.name === 'string') { + return stop.name; } - removeFavourite(stopId: number) { - const rawFavouriteStops = localStorage.getItem('favouriteStops'); - let favouriteStops: number[] = []; - if (rawFavouriteStops) { - favouriteStops = JSON.parse(rawFavouriteStops) as number[]; - } + return stop.name.intersect || stop.name.original; +} + +function addFavourite(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)); + if (!favouriteStops.includes(stopId)) { + favouriteStops.push(stopId); + localStorage.setItem('favouriteStops', JSON.stringify(favouriteStops)); } +} - isFavourite(stopId: number): boolean { - const rawFavouriteStops = localStorage.getItem('favouriteStops'); - if (rawFavouriteStops) { - const favouriteStops = JSON.parse(rawFavouriteStops) as number[]; - return favouriteStops.includes(stopId); - } - return false; +function removeFavourite(stopId: number) { + const rawFavouriteStops = localStorage.getItem('favouriteStops'); + let favouriteStops: number[] = []; + if (rawFavouriteStops) { + favouriteStops = JSON.parse(rawFavouriteStops) as number[]; } - RECENT_STOPS_LIMIT = 10; + const newFavouriteStops = favouriteStops.filter(id => id !== stopId); + localStorage.setItem('favouriteStops', JSON.stringify(newFavouriteStops)); +} - pushRecent(stopId: number) { - const rawRecentStops = localStorage.getItem('recentStops'); - let recentStops: Set = new Set(); - if (rawRecentStops) { - recentStops = new Set(JSON.parse(rawRecentStops) as number[]); - } +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; +} - recentStops.add(stopId); - if (recentStops.size > this.RECENT_STOPS_LIMIT) { - const iterator = recentStops.values(); - const val = iterator.next().value as number; - recentStops.delete(val); - } +const RECENT_STOPS_LIMIT = 10; - localStorage.setItem('recentStops', JSON.stringify(Array.from(recentStops))); +function pushRecent(stopId: number) { + const rawRecentStops = localStorage.getItem('recentStops'); + let recentStops: Set = new Set(); + if (rawRecentStops) { + recentStops = new Set(JSON.parse(rawRecentStops) as number[]); } - getRecent(): number[] { - const rawRecentStops = localStorage.getItem('recentStops'); - if (rawRecentStops) { - return JSON.parse(rawRecentStops) as number[]; - } - return []; + recentStops.add(stopId); + if (recentStops.size > RECENT_STOPS_LIMIT) { + const iterator = recentStops.values(); + const val = iterator.next().value as number; + recentStops.delete(val); } -} \ No newline at end of file + + 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 []; +} diff --git a/src/pages/Estimates.tsx b/src/pages/Estimates.tsx index 900ffc5..90745da 100644 --- a/src/pages/Estimates.tsx +++ b/src/pages/Estimates.tsx @@ -1,6 +1,6 @@ import { JSX, useEffect, useState } from "react"; import { useParams } from "react-router"; -import { StopDataProvider } from "../data/StopDataProvider"; +import StopDataProvider from "../data/StopDataProvider"; import { Star } from 'lucide-react'; import "../styles/Estimates.css"; import { RegularTable } from "../components/RegularTable"; @@ -22,8 +22,6 @@ export interface StopDetails { }[] } -const sdp = new StopDataProvider(); - const loadData = async (stopId: string) => { const resp = await fetch(`/api/GetStopEstimates?id=${stopId}`); return await resp.json(); @@ -44,20 +42,20 @@ export function Estimates(): JSX.Element { }) - sdp.pushRecent(parseInt(params.stopId ?? "")); + StopDataProvider.pushRecent(parseInt(params.stopId ?? "")); setFavourited( - sdp.isFavourite(parseInt(params.stopId ?? "")) + StopDataProvider.isFavourite(parseInt(params.stopId ?? "")) ); }, [params.stopId]); const toggleFavourite = () => { if (favourited) { - sdp.removeFavourite(parseInt(params.stopId ?? "")); + StopDataProvider.removeFavourite(parseInt(params.stopId ?? "")); setFavourited(false); } else { - sdp.addFavourite(parseInt(params.stopId ?? "")); + StopDataProvider.addFavourite(parseInt(params.stopId ?? "")); setFavourited(true); } } diff --git a/src/pages/Map.tsx b/src/pages/Map.tsx index 9abb7a3..af95bf9 100644 --- a/src/pages/Map.tsx +++ b/src/pages/Map.tsx @@ -1,4 +1,4 @@ -import { StopDataProvider, Stop } from "../data/StopDataProvider"; +import StopDataProvider, { Stop } from "../data/StopDataProvider"; import 'leaflet/dist/leaflet.css' import 'react-leaflet-markercluster/styles' @@ -20,8 +20,6 @@ const icon = new Icon({ shadowSize: [41, 41] }); -const sdp = new StopDataProvider(); - // Componente auxiliar para detectar cambios en el mapa const MapEventHandler = () => { const { updateMapState } = useApp(); @@ -43,9 +41,17 @@ export function StopMap() { const { mapState } = useApp(); useEffect(() => { - sdp.getStops().then((stops) => { setStops(stops); }); + StopDataProvider.getStops().then((stops) => { setStops(stops); }); }, []); + const getDisplayName = (stop: Stop): string => { + if (typeof stop.name === 'string') { + return stop.name; + } + + return stop.name.intersect || stop.name.original; + } + return ( ( - {stop.name} + {getDisplayName(stop)}
    {stop.lines.map((line) => ( diff --git a/src/pages/StopList.tsx b/src/pages/StopList.tsx index 72cccda..449ae84 100644 --- a/src/pages/StopList.tsx +++ b/src/pages/StopList.tsx @@ -1,10 +1,8 @@ import { useEffect, useMemo, useState } from "react"; -import { Stop, StopDataProvider } from "../data/StopDataProvider"; +import StopDataProvider, { Stop } from "../data/StopDataProvider"; import StopItem from "../components/StopItem"; import Fuse from "fuse.js"; -const sdp = new StopDataProvider(); - const placeholders = [ "Urzaiz", "Gran Vía", @@ -22,7 +20,7 @@ export function StopList() { const [searchResults, setSearchResults] = useState(null); useEffect(() => { - sdp.getStops().then((stops: Stop[]) => setData(stops)) + StopDataProvider.getStops().then((stops: Stop[]) => setData(stops)) }, []); const handleStopSearch = (event: React.ChangeEvent) => { @@ -39,7 +37,7 @@ export function StopList() { }, [data]) const recentStops = useMemo(() => { - const recent = sdp.getRecent(); + const recent = StopDataProvider.getRecent(); if (recent.length === 0) return null; -- cgit v1.3