aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/hooks/usePlanner.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/app/hooks/usePlanner.ts')
-rw-r--r--src/frontend/app/hooks/usePlanner.ts178
1 files changed, 140 insertions, 38 deletions
diff --git a/src/frontend/app/hooks/usePlanner.ts b/src/frontend/app/hooks/usePlanner.ts
index a28167a..445a426 100644
--- a/src/frontend/app/hooks/usePlanner.ts
+++ b/src/frontend/app/hooks/usePlanner.ts
@@ -1,21 +1,23 @@
+import { useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useState } from "react";
import { type PlannerSearchResult, type RoutePlan } from "../data/PlannerApi";
import { usePlanQuery } from "./usePlanQuery";
-const STORAGE_KEY = "planner_last_route";
+const STORAGE_KEY = "planner_route_history";
const EXPIRY_MS = 2 * 60 * 60 * 1000; // 2 hours
interface StoredRoute {
timestamp: number;
origin: PlannerSearchResult;
destination: PlannerSearchResult;
- plan: RoutePlan;
+ plan?: RoutePlan;
searchTime?: Date;
arriveBy?: boolean;
selectedItineraryIndex?: number;
}
-export function usePlanner() {
+export function usePlanner(options: { autoLoad?: boolean } = {}) {
+ const { autoLoad = true } = options;
const [origin, setOrigin] = useState<PlannerSearchResult | null>(null);
const [destination, setDestination] = useState<PlannerSearchResult | null>(
null
@@ -28,6 +30,8 @@ export function usePlanner() {
const [selectedItineraryIndex, setSelectedItineraryIndex] = useState<
number | null
>(null);
+ const [history, setHistory] = useState<StoredRoute[]>([]);
+ const queryClient = useQueryClient();
const {
data: queryPlan,
@@ -41,13 +45,13 @@ export function usePlanner() {
destination?.lon,
searchTime ?? undefined,
arriveBy,
- !!(origin && destination)
+ !!(origin && destination && searchTime)
);
// Sync query result to local state and storage
useEffect(() => {
if (queryPlan) {
- setPlan(queryPlan as any); // Cast because of slight type differences if any, but they should match now
+ setPlan(queryPlan as any);
if (origin && destination) {
const toStore: StoredRoute = {
@@ -59,7 +63,21 @@ export function usePlanner() {
arriveBy,
selectedItineraryIndex: selectedItineraryIndex ?? undefined,
};
- localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore));
+
+ setHistory((prev) => {
+ const filtered = prev.filter(
+ (r) =>
+ !(
+ r.origin.lat === origin.lat &&
+ r.origin.lon === origin.lon &&
+ r.destination.lat === destination.lat &&
+ r.destination.lon === destination.lon
+ )
+ );
+ const updated = [toStore, ...filtered].slice(0, 3);
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
+ return updated;
+ });
}
}
}, [
@@ -76,22 +94,40 @@ export function usePlanner() {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
- const data: StoredRoute = JSON.parse(stored);
- if (Date.now() - data.timestamp < EXPIRY_MS) {
- setOrigin(data.origin);
- setDestination(data.destination);
- setPlan(data.plan);
- setSearchTime(data.searchTime ? new Date(data.searchTime) : null);
- setArriveBy(data.arriveBy ?? false);
- setSelectedItineraryIndex(data.selectedItineraryIndex ?? null);
- } else {
- localStorage.removeItem(STORAGE_KEY);
+ const data: StoredRoute[] = JSON.parse(stored);
+ const valid = data.filter((r) => Date.now() - r.timestamp < EXPIRY_MS);
+ setHistory(valid);
+
+ if (autoLoad && valid.length > 0) {
+ const last = valid[0];
+ if (last.plan) {
+ queryClient.setQueryData(
+ [
+ "plan",
+ last.origin.lat,
+ last.origin.lon,
+ last.destination.lat,
+ last.destination.lon,
+ last.searchTime
+ ? new Date(last.searchTime).toISOString()
+ : undefined,
+ last.arriveBy ?? false,
+ ],
+ last.plan
+ );
+ setPlan(last.plan);
+ }
+ setOrigin(last.origin);
+ setDestination(last.destination);
+ setSearchTime(last.searchTime ? new Date(last.searchTime) : null);
+ setArriveBy(last.arriveBy ?? false);
+ setSelectedItineraryIndex(last.selectedItineraryIndex ?? null);
}
} catch (e) {
localStorage.removeItem(STORAGE_KEY);
}
}
- }, []);
+ }, [autoLoad]);
const searchRoute = async (
from: PlannerSearchResult,
@@ -101,9 +137,78 @@ export function usePlanner() {
) => {
setOrigin(from);
setDestination(to);
- setSearchTime(time ?? new Date());
+ const finalTime = time ?? new Date();
+ setSearchTime(finalTime);
setArriveBy(arriveByParam);
setSelectedItineraryIndex(null);
+
+ // Save to history immediately so other pages can pick it up
+ const toStore: StoredRoute = {
+ timestamp: Date.now(),
+ origin: from,
+ destination: to,
+ searchTime: finalTime,
+ arriveBy: arriveByParam,
+ };
+
+ setHistory((prev) => {
+ const filtered = prev.filter(
+ (r) =>
+ !(
+ r.origin.lat === from.lat &&
+ r.origin.lon === from.lon &&
+ r.destination.lat === to.lat &&
+ r.destination.lon === to.lon
+ )
+ );
+ const updated = [toStore, ...filtered].slice(0, 3);
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
+ return updated;
+ });
+ };
+
+ const loadRoute = (route: StoredRoute) => {
+ if (route.plan) {
+ queryClient.setQueryData(
+ [
+ "plan",
+ route.origin.lat,
+ route.origin.lon,
+ route.destination.lat,
+ route.destination.lon,
+ route.searchTime
+ ? new Date(route.searchTime).toISOString()
+ : undefined,
+ route.arriveBy ?? false,
+ ],
+ route.plan
+ );
+ setPlan(route.plan);
+ }
+ setOrigin(route.origin);
+ setDestination(route.destination);
+ setSearchTime(route.searchTime ? new Date(route.searchTime) : null);
+ setArriveBy(route.arriveBy ?? false);
+ setSelectedItineraryIndex(route.selectedItineraryIndex ?? null);
+
+ // Move to top of history
+ setHistory((prev) => {
+ const filtered = prev.filter(
+ (r) =>
+ !(
+ r.origin.lat === route.origin.lat &&
+ r.origin.lon === route.origin.lon &&
+ r.destination.lat === route.destination.lat &&
+ r.destination.lon === route.destination.lon
+ )
+ );
+ const updated = [{ ...route, timestamp: Date.now() }, ...filtered].slice(
+ 0,
+ 3
+ );
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
+ return updated;
+ });
};
const clearRoute = () => {
@@ -113,6 +218,7 @@ export function usePlanner() {
setSearchTime(null);
setArriveBy(false);
setSelectedItineraryIndex(null);
+ setHistory([]);
localStorage.removeItem(STORAGE_KEY);
};
@@ -120,32 +226,26 @@ export function usePlanner() {
setSelectedItineraryIndex(index);
// Update storage
- const stored = localStorage.getItem(STORAGE_KEY);
- if (stored) {
- try {
- const data: StoredRoute = JSON.parse(stored);
- data.selectedItineraryIndex = index;
- localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
- } catch (e) {
- // Ignore
- }
- }
+ setHistory((prev) => {
+ if (prev.length === 0) return prev;
+ const updated = [...prev];
+ updated[0] = { ...updated[0], selectedItineraryIndex: index };
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
+ return updated;
+ });
}, []);
const deselectItinerary = useCallback(() => {
setSelectedItineraryIndex(null);
// Update storage
- const stored = localStorage.getItem(STORAGE_KEY);
- if (stored) {
- try {
- const data: StoredRoute = JSON.parse(stored);
- data.selectedItineraryIndex = undefined;
- localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
- } catch (e) {
- // Ignore
- }
- }
+ setHistory((prev) => {
+ if (prev.length === 0) return prev;
+ const updated = [...prev];
+ updated[0] = { ...updated[0], selectedItineraryIndex: undefined };
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
+ return updated;
+ });
}, []);
return {
@@ -159,7 +259,9 @@ export function usePlanner() {
searchTime,
arriveBy,
selectedItineraryIndex,
+ history,
searchRoute,
+ loadRoute,
clearRoute,
selectItinerary,
deselectItinerary,