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;