aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/routes/stoplist.tsx
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-08-06 00:12:19 +0200
committerAriel Costas Guerrero <ariel@costas.dev>2025-08-06 00:12:19 +0200
commit5cc27f852b02446659e0ab85305916c9f5e5a5f0 (patch)
tree622636a2a7eade5442a3efb1726d822657d30295 /src/frontend/app/routes/stoplist.tsx
parentb04fd7d33d07f9eddea2eb53e1389d5ca5453413 (diff)
feat: Implement pull-to-refresh functionality across various components
- Added `PullToRefresh` component to enable pull-to-refresh behavior in `StopList` and `Estimates` pages. - Integrated `usePullToRefresh` hook to manage pull-to-refresh state and actions. - Created `UpdateNotification` component to inform users of available updates from the service worker. - Enhanced service worker management with `ServiceWorkerManager` class for better update handling and caching strategies. - Updated CSS styles for new components and improved layout for better user experience. - Refactored API caching logic in service worker to handle multiple endpoints and dynamic cache expiration. - Added auto-refresh functionality for estimates data to keep information up-to-date.
Diffstat (limited to 'src/frontend/app/routes/stoplist.tsx')
-rw-r--r--src/frontend/app/routes/stoplist.tsx142
1 files changed, 83 insertions, 59 deletions
diff --git a/src/frontend/app/routes/stoplist.tsx b/src/frontend/app/routes/stoplist.tsx
index 58cdab4..70b1525 100644
--- a/src/frontend/app/routes/stoplist.tsx
+++ b/src/frontend/app/routes/stoplist.tsx
@@ -1,9 +1,11 @@
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useEffect, useMemo, useRef, useState, useCallback } from "react";
import StopDataProvider, { type Stop } from "../data/StopDataProvider";
import StopItem from "../components/StopItem";
import Fuse from "fuse.js";
import "./stoplist.css";
import { useTranslation } from "react-i18next";
+import { usePullToRefresh } from "../hooks/usePullToRefresh";
+import { PullToRefreshIndicator } from "../components/PullToRefresh";
export default function StopList() {
const { t } = useTranslation();
@@ -20,10 +22,26 @@ export default function StopList() {
[data],
);
- useEffect(() => {
- StopDataProvider.getStops().then((stops: Stop[]) => setData(stops));
+ const loadStops = useCallback(async () => {
+ const stops = await StopDataProvider.getStops();
+ setData(stops);
}, []);
+ const {
+ containerRef,
+ isRefreshing,
+ pullDistance,
+ canRefresh,
+ } = usePullToRefresh({
+ onRefresh: loadStops,
+ threshold: 80,
+ enabled: true,
+ });
+
+ useEffect(() => {
+ loadStops();
+ }, [loadStops]);
+
const handleStopSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
const stopName = event.target.value || "";
@@ -68,77 +86,83 @@ export default function StopList() {
return <h1 className="page-title">{t("common.loading")}</h1>;
return (
- <div className="page-container">
- <h1 className="page-title">UrbanoVigo Web</h1>
+ <div ref={containerRef} className="page-container stoplist-page">
+ <PullToRefreshIndicator
+ pullDistance={pullDistance}
+ isRefreshing={isRefreshing}
+ canRefresh={canRefresh}
+ >
+ <h1 className="page-title">UrbanoVigo Web</h1>
- <form className="search-form">
- <div className="form-group">
- <label className="form-label" htmlFor="stopName">
- {t("stoplist.search_label", "Buscar paradas")}
- </label>
- <input
- className="form-input"
- type="text"
- placeholder={randomPlaceholder}
- id="stopName"
- onChange={handleStopSearch}
- />
- </div>
- </form>
+ <form className="search-form">
+ <div className="form-group">
+ <label className="form-label" htmlFor="stopName">
+ {t("stoplist.search_label", "Buscar paradas")}
+ </label>
+ <input
+ className="form-input"
+ type="text"
+ placeholder={randomPlaceholder}
+ id="stopName"
+ onChange={handleStopSearch}
+ />
+ </div>
+ </form>
+
+ {searchResults && searchResults.length > 0 && (
+ <div className="list-container">
+ <h2 className="page-subtitle">
+ {t("stoplist.search_results", "Resultados de la búsqueda")}
+ </h2>
+ <ul className="list">
+ {searchResults.map((stop: Stop) => (
+ <StopItem key={stop.stopId} stop={stop} />
+ ))}
+ </ul>
+ </div>
+ )}
- {searchResults && searchResults.length > 0 && (
<div className="list-container">
- <h2 className="page-subtitle">
- {t("stoplist.search_results", "Resultados de la búsqueda")}
- </h2>
+ <h2 className="page-subtitle">{t("stoplist.favourites")}</h2>
+
+ {favouritedStops?.length === 0 && (
+ <p className="message">
+ {t(
+ "stoplist.no_favourites",
+ "Accede a una parada y márcala como favorita para verla aquí.",
+ )}
+ </p>
+ )}
+
<ul className="list">
- {searchResults.map((stop: Stop) => (
- <StopItem key={stop.stopId} stop={stop} />
- ))}
+ {favouritedStops
+ ?.sort((a, b) => a.stopId - b.stopId)
+ .map((stop: Stop) => <StopItem key={stop.stopId} stop={stop} />)}
</ul>
</div>
- )}
- <div className="list-container">
- <h2 className="page-subtitle">{t("stoplist.favourites")}</h2>
+ {recentStops && recentStops.length > 0 && (
+ <div className="list-container">
+ <h2 className="page-subtitle">{t("stoplist.recents")}</h2>
- {favouritedStops?.length === 0 && (
- <p className="message">
- {t(
- "stoplist.no_favourites",
- "Accede a una parada y márcala como favorita para verla aquí.",
- )}
- </p>
+ <ul className="list">
+ {recentStops.map((stop: Stop) => (
+ <StopItem key={stop.stopId} stop={stop} />
+ ))}
+ </ul>
+ </div>
)}
- <ul className="list">
- {favouritedStops
- ?.sort((a, b) => a.stopId - b.stopId)
- .map((stop: Stop) => <StopItem key={stop.stopId} stop={stop} />)}
- </ul>
- </div>
-
- {recentStops && recentStops.length > 0 && (
<div className="list-container">
- <h2 className="page-subtitle">{t("stoplist.recents")}</h2>
+ <h2 className="page-subtitle">{t("stoplist.all_stops", "Paradas")}</h2>
<ul className="list">
- {recentStops.map((stop: Stop) => (
- <StopItem key={stop.stopId} stop={stop} />
- ))}
+ {data
+ ?.sort((a, b) => a.stopId - b.stopId)
+ .map((stop: Stop) => <StopItem key={stop.stopId} stop={stop} />)}
</ul>
</div>
- )}
-
- <div className="list-container">
- <h2 className="page-subtitle">{t("stoplist.all_stops", "Paradas")}</h2>
-
- <ul className="list">
- {data
- ?.sort((a, b) => a.stopId - b.stopId)
- .map((stop: Stop) => <StopItem key={stop.stopId} stop={stop} />)}
- </ul>
- </div>
+ </PullToRefreshIndicator>
</div>
);
}