Files
TREK/client/src/store/tourMapVisibilityStore.ts
T
Maurice 3d9095ccde Tours: provenance contract, source check, one assignment path and the map overview
- P2C-02: tours record their origin (planned, GPX with file name, wanderer)
  instead of reading it off their shape; the detail answers a provenance
  contract and a trip copy keeps a tour's import record
- MF-03: a wanderer snapshot's source can be checked on purpose, read only,
  with a confirmed reimport as a new snapshot
- PR1-18: every way of putting a tour on a day shares one set of rules
- PR1-07/08: each person can hide tours from the trip map; the list shows
  each tour's line colour and the picked tour stands out on both maps
2026-10-15 21:51:22 +02:00

68 lines
2.3 KiB
TypeScript

import { create } from 'zustand';
const STORAGE_PREFIX = 'trek:hidden-tours:';
/** The scope one person's choice belongs to: this user in this trip. */
export function tourVisibilityScope(userId: number, tripId: number): string {
return `${userId}:${tripId}`;
}
function readHidden(scope: string): number[] {
try {
const parsed: unknown = JSON.parse(window.localStorage.getItem(STORAGE_PREFIX + scope) ?? '[]');
return Array.isArray(parsed) ? parsed.filter((id): id is number => Number.isSafeInteger(id) && id > 0) : [];
} catch {
// Storage blocked or the entry unreadable: every tour shows.
return [];
}
}
function writeHidden(scope: string, hidden: readonly number[]): void {
try {
if (hidden.length) window.localStorage.setItem(STORAGE_PREFIX + scope, JSON.stringify(hidden));
else window.localStorage.removeItem(STORAGE_PREFIX + scope);
} catch {
// Storage blocked: the choice still holds while the page is up.
}
}
interface TourMapVisibilityState {
/** Whose choice is loaded, `user:trip`; null before a planner opened. */
scope: string | null;
/** The tours this person hid from the map of this trip. */
hidden: readonly number[];
load: (scope: string) => void;
toggle: (placeId: number) => void;
showAll: () => void;
}
/**
* PR1-07: which tours one person hides from the map of one trip. A view
* setting of that person on this device, like which bookings' routes the map
* draws: it never changes the tour, its days or its colour, and nobody else
* sees it. Kept per user and trip in the browser's storage, so a reload keeps
* it. Ids of tours that are gone are dropped where the list is read, so they
* cannot hide anything later.
*/
export const useTourMapVisibilityStore = create<TourMapVisibilityState>()((set, get) => ({
scope: null,
hidden: [],
load: (scope) => {
if (get().scope === scope) return;
set({ scope, hidden: readHidden(scope) });
},
toggle: (placeId) => {
const { scope, hidden } = get();
if (!scope) return;
const next = hidden.includes(placeId) ? hidden.filter((id) => id !== placeId) : [...hidden, placeId];
writeHidden(scope, next);
set({ hidden: next });
},
showAll: () => {
const { scope } = get();
if (!scope) return;
writeHidden(scope, []);
set({ hidden: [] });
},
}));