File size: 3,708 Bytes
5c876be | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | /**
* Active ride & match state store.
*
* Tracks the currently active ride and match (if any), plus
* the search/loading state for ride matching.
*/
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import type { Ride } from '../types/ride';
import type { Match } from '../types/match';
// βββ State Shape βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
interface RideState {
/** The ride the user is currently involved in (as rider or passenger) */
activeRide: Ride | null;
/** The current match for the active ride */
activeMatch: Match | null;
/** Whether the app is currently searching for ride matches */
isSearching: boolean;
/** Error message from the last failed operation */
error: string | null;
}
// βββ Actions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
interface RideActions {
/** Set the active ride */
setActiveRide: (ride: Ride | null) => void;
/** Set the active match */
setActiveMatch: (match: Match | null) => void;
/** Toggle the searching state */
setSearching: (value: boolean) => void;
/** Set an error message */
setError: (error: string | null) => void;
/** Clear the active ride, match, and search state */
clearActiveRide: () => void;
/** Update ride status (convenience) */
updateRideStatus: (status: Ride['status']) => void;
/** Update match status (convenience) */
updateMatchStatus: (status: Match['status']) => void;
}
// βββ Store βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
export const useRideStore = create<RideState & RideActions>()(
immer((set) => ({
// Initial state
activeRide: null,
activeMatch: null,
isSearching: false,
error: null,
// Actions
setActiveRide: (ride) =>
set((state) => {
state.activeRide = ride;
}),
setActiveMatch: (match) =>
set((state) => {
state.activeMatch = match;
}),
setSearching: (value) =>
set((state) => {
state.isSearching = value;
if (value) {
state.error = null;
}
}),
setError: (error) =>
set((state) => {
state.error = error;
}),
clearActiveRide: () =>
set((state) => {
state.activeRide = null;
state.activeMatch = null;
state.isSearching = false;
state.error = null;
}),
updateRideStatus: (status) =>
set((state) => {
if (state.activeRide) {
state.activeRide.status = status;
}
}),
updateMatchStatus: (status) =>
set((state) => {
if (state.activeMatch) {
state.activeMatch.status = status;
}
}),
})),
);
// βββ Selectors βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
export const selectHasActiveRide = (state: RideState) =>
state.activeRide !== null && state.activeMatch !== null;
export const selectRideId = (state: RideState) => state.activeRide?.id ?? null;
export const selectMatchId = (state: RideState) =>
state.activeMatch?.id ?? null;
export const selectRideType = (state: RideState) =>
state.activeRide?.type ?? null;
|