From 5cc27f852b02446659e0ab85305916c9f5e5a5f0 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Wed, 6 Aug 2025 00:12:19 +0200 Subject: 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. --- src/frontend/app/routes/stoplist.tsx | 148 ++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 62 deletions(-) (limited to 'src/frontend/app/routes/stoplist.tsx') 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) => { const stopName = event.target.value || ""; @@ -68,77 +86,83 @@ export default function StopList() { return

{t("common.loading")}

; return ( -
-

UrbanoVigo Web

- -
-
- - -
-
+
+ +

UrbanoVigo Web

+ +
+
+ + +
+
+ + {searchResults && searchResults.length > 0 && ( +
+

+ {t("stoplist.search_results", "Resultados de la búsqueda")} +

+
    + {searchResults.map((stop: Stop) => ( + + ))} +
+
+ )} - {searchResults && searchResults.length > 0 && (
-

- {t("stoplist.search_results", "Resultados de la búsqueda")} -

+

{t("stoplist.favourites")}

+ + {favouritedStops?.length === 0 && ( +

+ {t( + "stoplist.no_favourites", + "Accede a una parada y márcala como favorita para verla aquí.", + )} +

+ )} +
    - {searchResults.map((stop: Stop) => ( - - ))} + {favouritedStops + ?.sort((a, b) => a.stopId - b.stopId) + .map((stop: Stop) => )}
- )} - -
-

{t("stoplist.favourites")}

- - {favouritedStops?.length === 0 && ( -

- {t( - "stoplist.no_favourites", - "Accede a una parada y márcala como favorita para verla aquí.", - )} -

- )} -
    - {favouritedStops - ?.sort((a, b) => a.stopId - b.stopId) - .map((stop: Stop) => )} -
-
+ {recentStops && recentStops.length > 0 && ( +
+

{t("stoplist.recents")}

+ +
    + {recentStops.map((stop: Stop) => ( + + ))} +
+
+ )} - {recentStops && recentStops.length > 0 && (
-

{t("stoplist.recents")}

+

{t("stoplist.all_stops", "Paradas")}

    - {recentStops.map((stop: Stop) => ( - - ))} + {data + ?.sort((a, b) => a.stopId - b.stopId) + .map((stop: Stop) => )}
- )} - -
-

{t("stoplist.all_stops", "Paradas")}

- -
    - {data - ?.sort((a, b) => a.stopId - b.stopId) - .map((stop: Stop) => )} -
-
+
); } -- cgit v1.3