import type { Place } from "./types"; export type Screen = "places" | "collections" | "profile" | "place" | "collection"; export type Tab = "places" | "collections" | "profile"; export type StackFrame = { screen: Screen; placeId?: number; collectionId?: number; }; export type Modal = | { type: "add" } | { type: "editPlace"; placeId: number } | { type: "saveToCollection"; placeId: number } | { type: "createCollection" } | { type: "editCollection"; collectionId: number } | { type: "invite"; collectionId: number } | { type: "members"; collectionId: number } | { type: "confirmDeletePlace"; placeId: number } | { type: "confirmDeleteCollection"; collectionId: number } | null; export type AppState = { tab: Tab; stack: StackFrame[]; filter: string; search: string; places: Place[]; modal: Modal; toast: string | null; toastKey: number; offline: boolean; }; export type Action = | { type: "NAV"; screen: Screen; placeId?: number; collectionId?: number } | { type: "BACK" } | { type: "TAB"; tab: Tab } | { type: "SET_FILTER"; value: string } | { type: "SET_SEARCH"; value: string } | { type: "TOGGLE_VISITED"; placeId: number } | { type: "SET_RATING"; placeId: number; value: number } | { type: "SET_NOTES"; placeId: number; value: string } | { type: "ADD_PLACE"; place: Place } | { type: "PATCH_PLACE"; placeId: number; patch: Partial } | { type: "DELETE_PLACE"; placeId: number } | { type: "TOAST"; value: string } | { type: "CLEAR_TOAST"; key: number } | { type: "OPEN_ADD" } | { type: "OPEN_EDIT_PLACE"; placeId: number } | { type: "OPEN_SAVE_TO_COLLECTION"; placeId: number } | { type: "OPEN_CREATE_COLLECTION" } | { type: "OPEN_EDIT_COLLECTION"; collectionId: number } | { type: "OPEN_INVITE"; collectionId: number } | { type: "OPEN_MEMBERS"; collectionId: number } | { type: "CONFIRM_DELETE_PLACE"; placeId: number } | { type: "CONFIRM_DELETE_COLLECTION"; collectionId: number } | { type: "CLOSE_MODAL" } | { type: "SET_OFFLINE"; value: boolean }; export function makeInitialState(places: Place[]): AppState { return { tab: "places", stack: [{ screen: "places" }], filter: "all", search: "", places, modal: null, toast: null, toastKey: 0, offline: false, }; } export function reducer(state: AppState, action: Action): AppState { switch (action.type) { case "NAV": { const stack = [ ...state.stack, { screen: action.screen, placeId: action.placeId, collectionId: action.collectionId }, ]; return { ...state, stack }; } case "BACK": { if (state.stack.length <= 1) return state; return { ...state, stack: state.stack.slice(0, -1) }; } case "TAB": return { ...state, tab: action.tab, stack: [{ screen: action.tab }] }; case "SET_FILTER": return { ...state, filter: action.value }; case "SET_SEARCH": return { ...state, search: action.value }; case "TOGGLE_VISITED": { const places = state.places.map((p) => p.id === action.placeId ? { ...p, visited: !p.visited, visited_at: !p.visited ? new Date().toISOString().slice(0, 10) : undefined, } : p, ); return { ...state, places }; } case "SET_RATING": { const places = state.places.map((p) => p.id === action.placeId ? { ...p, my_rating: action.value || undefined } : p, ); return { ...state, places }; } case "SET_NOTES": { const places = state.places.map((p) => p.id === action.placeId ? { ...p, my_notes: action.value } : p, ); return { ...state, places }; } case "ADD_PLACE": return { ...state, places: [action.place, ...state.places] }; case "PATCH_PLACE": { const places = state.places.map((p) => p.id === action.placeId ? { ...p, ...action.patch } : p, ); return { ...state, places }; } case "DELETE_PLACE": return { ...state, places: state.places.filter((p) => p.id !== action.placeId), stack: state.stack.length > 1 ? state.stack.slice(0, -1) : state.stack, modal: null, }; case "TOAST": return { ...state, toast: action.value, toastKey: state.toastKey + 1 }; case "CLEAR_TOAST": return state.toastKey === action.key ? { ...state, toast: null } : state; case "OPEN_ADD": return { ...state, modal: { type: "add" } }; case "OPEN_EDIT_PLACE": return { ...state, modal: { type: "editPlace", placeId: action.placeId } }; case "OPEN_SAVE_TO_COLLECTION": return { ...state, modal: { type: "saveToCollection", placeId: action.placeId } }; case "OPEN_CREATE_COLLECTION": return { ...state, modal: { type: "createCollection" } }; case "OPEN_EDIT_COLLECTION": return { ...state, modal: { type: "editCollection", collectionId: action.collectionId } }; case "OPEN_INVITE": return { ...state, modal: { type: "invite", collectionId: action.collectionId } }; case "OPEN_MEMBERS": return { ...state, modal: { type: "members", collectionId: action.collectionId } }; case "CONFIRM_DELETE_PLACE": return { ...state, modal: { type: "confirmDeletePlace", placeId: action.placeId } }; case "CONFIRM_DELETE_COLLECTION": return { ...state, modal: { type: "confirmDeleteCollection", collectionId: action.collectionId } }; case "CLOSE_MODAL": return { ...state, modal: null }; case "SET_OFFLINE": return { ...state, offline: action.value }; default: return state; } } export type Dispatch = (action: Action) => void;