11import { useFocusEffect } from '@react-navigation/native' ;
22import { type Href , router } from 'expo-router' ;
3- import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
3+ import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
44import { useTranslation } from 'react-i18next' ;
55import { ScrollView , StyleSheet , useWindowDimensions , View } from 'react-native' ;
66
@@ -21,7 +21,7 @@ import { useDispatchConsoleStore } from '@/stores/dispatch/dispatch-console-stor
2121import { useHomeStore } from '@/stores/home/home-store' ;
2222import { useNotesStore } from '@/stores/notes/store' ;
2323import { usePersonnelStore } from '@/stores/personnel/store' ;
24- import { useSignalRStore } from '@/stores/signalr/signalr-store' ;
24+ import { type SignalREventType , useSignalRStore } from '@/stores/signalr/signalr-store' ;
2525import { useUnitsStore } from '@/stores/units/store' ;
2626
2727export default function DispatchConsoleWeb ( ) {
@@ -38,7 +38,15 @@ export default function DispatchConsoleWeb() {
3838 const { units, isLoading : unitsLoading , fetchUnits } = useUnitsStore ( ) ;
3939 const { personnel, isLoading : personnelLoading , fetchPersonnel } = usePersonnelStore ( ) ;
4040 const { notes, isLoading : notesLoading , fetchNotes } = useNotesStore ( ) ;
41- const { lastUpdateTimestamp } = useSignalRStore ( ) ;
41+
42+ // SignalR store - subscribe to specific event timestamps
43+ const {
44+ lastEventType,
45+ lastPersonnelUpdateTimestamp,
46+ lastUnitsUpdateTimestamp,
47+ lastCallsUpdateTimestamp,
48+ isUpdateHubConnected,
49+ } = useSignalRStore ( ) ;
4250
4351 // Dispatch console store
4452 const {
@@ -71,6 +79,11 @@ export default function DispatchConsoleWeb() {
7179 const [ currentTime , setCurrentTime ] = useState ( new Date ( ) . toLocaleTimeString ( 'en-US' , { hour12 : false } ) ) ;
7280 const [ isAddingNote , setIsAddingNote ] = useState ( false ) ;
7381
82+ // Track previous timestamps to detect changes
83+ const prevPersonnelTimestamp = useRef ( 0 ) ;
84+ const prevUnitsTimestamp = useRef ( 0 ) ;
85+ const prevCallsTimestamp = useRef ( 0 ) ;
86+
7487 // Update time every second
7588 useEffect ( ( ) => {
7689 const timer = setInterval ( ( ) => {
@@ -123,21 +136,107 @@ export default function DispatchConsoleWeb() {
123136 } , [ trackEvent , isLandscape , isTablet ] )
124137 ) ;
125138
126- // Listen for SignalR updates and refresh data
139+ // Helper function to get event description for activity log
140+ const getEventDescription = ( eventType : SignalREventType | null ) : string => {
141+ switch ( eventType ) {
142+ case 'personnelStatusUpdated' :
143+ return t ( 'dispatch.personnel_status_updated' ) ;
144+ case 'personnelStaffingUpdated' :
145+ return t ( 'dispatch.personnel_staffing_updated' ) ;
146+ case 'unitStatusUpdated' :
147+ return t ( 'dispatch.unit_status_updated' ) ;
148+ case 'callsUpdated' :
149+ return t ( 'dispatch.calls_updated' ) ;
150+ case 'callAdded' :
151+ return t ( 'dispatch.call_added' ) ;
152+ case 'callClosed' :
153+ return t ( 'dispatch.call_closed' ) ;
154+ default :
155+ return t ( 'dispatch.data_refreshed' ) ;
156+ }
157+ } ;
158+
159+ // Listen for SignalR personnel updates
127160 useEffect ( ( ) => {
128- if ( lastUpdateTimestamp > 0 ) {
161+ if ( lastPersonnelUpdateTimestamp > 0 && lastPersonnelUpdateTimestamp !== prevPersonnelTimestamp . current ) {
162+ prevPersonnelTimestamp . current = lastPersonnelUpdateTimestamp ;
163+
164+ logger . info ( {
165+ message : 'SignalR: Personnel update received, refreshing personnel data' ,
166+ context : { eventType : lastEventType , timestamp : lastPersonnelUpdateTimestamp } ,
167+ } ) ;
168+
129169 addActivityLogEntry ( {
130- type : 'system ' ,
131- action : t ( 'dispatch.system_update ' ) ,
132- description : t ( 'dispatch.data_refreshed' ) ,
170+ type : 'personnel ' ,
171+ action : t ( 'dispatch.signalr_update ' ) ,
172+ description : getEventDescription ( lastEventType ) ,
133173 } ) ;
134174
135- fetchCalls ( ) ;
136- fetchUnits ( ) ;
137175 fetchPersonnel ( ) ;
138176 }
139177 // eslint-disable-next-line react-hooks/exhaustive-deps
140- } , [ lastUpdateTimestamp ] ) ;
178+ } , [ lastPersonnelUpdateTimestamp ] ) ;
179+
180+ // Listen for SignalR units updates
181+ useEffect ( ( ) => {
182+ if ( lastUnitsUpdateTimestamp > 0 && lastUnitsUpdateTimestamp !== prevUnitsTimestamp . current ) {
183+ prevUnitsTimestamp . current = lastUnitsUpdateTimestamp ;
184+
185+ logger . info ( {
186+ message : 'SignalR: Units update received, refreshing units data' ,
187+ context : { eventType : lastEventType , timestamp : lastUnitsUpdateTimestamp } ,
188+ } ) ;
189+
190+ addActivityLogEntry ( {
191+ type : 'unit' ,
192+ action : t ( 'dispatch.signalr_update' ) ,
193+ description : getEventDescription ( lastEventType ) ,
194+ } ) ;
195+
196+ fetchUnits ( ) ;
197+ }
198+ // eslint-disable-next-line react-hooks/exhaustive-deps
199+ } , [ lastUnitsUpdateTimestamp ] ) ;
200+
201+ // Listen for SignalR calls updates
202+ useEffect ( ( ) => {
203+ if ( lastCallsUpdateTimestamp > 0 && lastCallsUpdateTimestamp !== prevCallsTimestamp . current ) {
204+ prevCallsTimestamp . current = lastCallsUpdateTimestamp ;
205+
206+ logger . info ( {
207+ message : 'SignalR: Calls update received, refreshing calls data' ,
208+ context : { eventType : lastEventType , timestamp : lastCallsUpdateTimestamp } ,
209+ } ) ;
210+
211+ addActivityLogEntry ( {
212+ type : 'call' ,
213+ action : t ( 'dispatch.signalr_update' ) ,
214+ description : getEventDescription ( lastEventType ) ,
215+ } ) ;
216+
217+ fetchCalls ( ) ;
218+ // Also refresh notes if we have a selected call (call data may have changed)
219+ if ( selectedCallId ) {
220+ fetchNotes ( ) ;
221+ }
222+ }
223+ // eslint-disable-next-line react-hooks/exhaustive-deps
224+ } , [ lastCallsUpdateTimestamp ] ) ;
225+
226+ // Log when SignalR hub connection status changes
227+ useEffect ( ( ) => {
228+ if ( isUpdateHubConnected ) {
229+ logger . info ( {
230+ message : 'SignalR Update Hub connected - dispatch console ready for real-time updates' ,
231+ } ) ;
232+ addActivityLogEntry ( {
233+ type : 'system' ,
234+ action : t ( 'dispatch.signalr_connected' ) ,
235+ description : t ( 'dispatch.realtime_updates_active' ) ,
236+ } ) ;
237+ }
238+ // eslint-disable-next-line react-hooks/exhaustive-deps
239+ } , [ isUpdateHubConnected ] ) ;
141240
142241 // Fetch call extra data and notes when filter is active
143242 useEffect ( ( ) => {
0 commit comments