Skip to content

Commit 15f8bc5

Browse files
committed
RU-T46 PR#198 fixes
1 parent b646d91 commit 15f8bc5

7 files changed

Lines changed: 130 additions & 12 deletions

File tree

src/api/calls/calls.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { cacheManager } from '@/lib/cache/cache-manager';
12
import { type ActiveCallsResult } from '@/models/v4/calls/activeCallsResult';
23
import { type CallExtraDataResult } from '@/models/v4/calls/callExtraDataResult';
34
import { type CallResult } from '@/models/v4/calls/callResult';
@@ -126,6 +127,15 @@ export const createCall = async (callData: CreateCallRequest) => {
126127
};
127128

128129
const response = await createCallApi.post<SaveCallResult>(data);
130+
131+
// Invalidate cache after successful mutation
132+
try {
133+
cacheManager.remove('/Calls/GetActiveCalls');
134+
} catch (error) {
135+
// Silently handle cache removal errors
136+
console.warn('Failed to invalidate calls cache:', error);
137+
}
138+
129139
return response.data;
130140
};
131141

@@ -174,6 +184,15 @@ export const updateCall = async (callData: UpdateCallRequest) => {
174184
};
175185

176186
const response = await updateCallApi.post<SaveCallResult>(data);
187+
188+
// Invalidate cache after successful mutation
189+
try {
190+
cacheManager.remove('/Calls/GetActiveCalls');
191+
} catch (error) {
192+
// Silently handle cache removal errors
193+
console.warn('Failed to invalidate calls cache:', error);
194+
}
195+
177196
return response.data;
178197
};
179198

@@ -185,5 +204,14 @@ export const closeCall = async (callData: CloseCallRequest) => {
185204
};
186205

187206
const response = await closeCallApi.put<SaveCallResult>(data);
207+
208+
// Invalidate cache after successful mutation
209+
try {
210+
cacheManager.remove('/Calls/GetActiveCalls');
211+
} catch (error) {
212+
// Silently handle cache removal errors
213+
console.warn('Failed to invalidate calls cache:', error);
214+
}
215+
188216
return response.data;
189217
};

src/components/maps/full-screen-location-picker.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const FullScreenLocationPicker: React.FC<FullScreenLocationPickerProps> = ({ ini
6262

6363
if (result && result.length > 0) {
6464
const { street, name, city, region, country, postalCode } = result[0];
65-
let addressParts = [];
65+
const addressParts: string[] = [];
6666

6767
if (street) addressParts.push(street);
6868
if (name && name !== street) addressParts.push(name);
@@ -150,7 +150,7 @@ const FullScreenLocationPicker: React.FC<FullScreenLocationPickerProps> = ({ ini
150150
};
151151
}, [initialLocation, getUserLocation, reverseGeocode]);
152152

153-
const handleMapPress = (event: any) => {
153+
const handleMapPress = (event: GeoJSON.Feature) => {
154154
const { coordinates } = event.geometry;
155155
const newLocation = {
156156
latitude: coordinates[1],
@@ -193,7 +193,7 @@ const FullScreenLocationPicker: React.FC<FullScreenLocationPickerProps> = ({ ini
193193

194194
{/* Location info and confirm button */}
195195
<Box style={[styles.bottomPanel, { paddingBottom: insets.bottom + 16 }]} className="bg-white p-4 shadow-lg">
196-
{!hasUserLocation && <Text className="mb-2 text-center text-amber-600">{t('common.tap_map_to_select')}</Text>}
196+
{!hasUserLocation ? <Text className="mb-2 text-center text-amber-600">{t('common.tap_map_to_select')}</Text> : null}
197197
{isReverseGeocoding ? (
198198
<Text className="mb-2 text-gray-500">{t('common.loading_address')}</Text>
199199
) : address ? (

src/components/maps/location-picker.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ initialLocation, onLoca
5555
return;
5656
}
5757

58-
// Create a timeout promise
58+
// Create a timeout promise with cleanup
59+
let timeoutId: NodeJS.Timeout;
5960
const timeoutPromise = new Promise<never>((_, reject) => {
60-
setTimeout(() => reject(new Error('Location timeout')), LOCATION_TIMEOUT);
61+
timeoutId = setTimeout(() => reject(new Error('Location timeout')), LOCATION_TIMEOUT);
6162
});
6263

6364
// Race between getting location and timeout
@@ -68,6 +69,9 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ initialLocation, onLoca
6869
timeoutPromise,
6970
]);
7071

72+
// Clear timeout if location resolved first
73+
clearTimeout(timeoutId);
74+
7175
if (!isMountedRef.current) return;
7276

7377
const newLocation = {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { logger } from '../lib/logging';
2+
3+
export interface CallKeepConfig {
4+
appName: string;
5+
maximumCallGroups: number;
6+
maximumCallsPerCallGroup: number;
7+
includesCallsInRecents: boolean;
8+
supportsVideo: boolean;
9+
ringtoneSound?: string;
10+
}
11+
12+
/**
13+
* No-op implementation of CallKeepService for Android
14+
* CallKeep functionality is only supported on iOS
15+
*/
16+
export class CallKeepService {
17+
private static instance: CallKeepService | null = null;
18+
19+
private constructor() {}
20+
21+
static getInstance(): CallKeepService {
22+
if (!CallKeepService.instance) {
23+
CallKeepService.instance = new CallKeepService();
24+
}
25+
return CallKeepService.instance;
26+
}
27+
28+
async setup(_config: CallKeepConfig): Promise<void> {
29+
logger.debug({
30+
message: 'CallKeep setup skipped - not supported on Android',
31+
});
32+
}
33+
34+
async startCall(_displayName: string): Promise<string> {
35+
logger.debug({
36+
message: 'CallKeep startCall skipped - not supported on Android',
37+
});
38+
return '';
39+
}
40+
41+
async endCall(): Promise<void> {
42+
logger.debug({
43+
message: 'CallKeep endCall skipped - not supported on Android',
44+
});
45+
}
46+
47+
async setMuted(_muted: boolean): Promise<void> {
48+
logger.debug({
49+
message: 'CallKeep setMuted skipped - not supported on Android',
50+
});
51+
}
52+
53+
setMuteStateCallback(_callback: (muted: boolean) => void | null): void {
54+
logger.debug({
55+
message: 'CallKeep setMuteStateCallback skipped - not supported on Android',
56+
});
57+
}
58+
59+
async updateCallDisplay(_displayName: string): Promise<void> {
60+
logger.debug({
61+
message: 'CallKeep updateCallDisplay skipped - not supported on Android',
62+
});
63+
}
64+
65+
isCallActive(): boolean {
66+
return false;
67+
}
68+
69+
async cleanup(): Promise<void> {
70+
logger.debug({
71+
message: 'CallKeep cleanup skipped - not supported on Android',
72+
});
73+
}
74+
}
75+
76+
// Export singleton instance
77+
export const callKeepService = CallKeepService.getInstance();

src/stores/app/livekit-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { getCanConnectToVoiceSession, getDepartmentVoiceSettings } from '../../a
88
import { logger } from '../../lib/logging';
99
import { type DepartmentVoiceChannelResultData } from '../../models/v4/voice/departmentVoiceResultData';
1010
import { audioService } from '../../services/audio.service';
11-
import { callKeepService } from '../../services/callkeep.service.ios';
11+
import { callKeepService } from '../../services/callkeep.service';
1212
import { useBluetoothAudioStore } from './bluetooth-audio-store';
1313

1414
// Helper function to setup audio routing based on selected devices

src/stores/calls/store.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface CallsState {
1313
callTypes: CallTypeResultData[];
1414
isLoading: boolean;
1515
error: string | null;
16+
lastFetchedAt: number;
1617
fetchCalls: () => Promise<void>;
1718
fetchCallPriorities: () => Promise<void>;
1819
fetchCallTypes: () => Promise<void>;
@@ -25,6 +26,7 @@ export const useCallsStore = create<CallsState>((set, get) => ({
2526
callTypes: [],
2627
isLoading: false,
2728
error: null,
29+
lastFetchedAt: 0,
2830
init: async () => {
2931
set({ isLoading: true, error: null });
3032
const callsResponse = await getCalls();
@@ -35,13 +37,14 @@ export const useCallsStore = create<CallsState>((set, get) => ({
3537
callPriorities: callPrioritiesResponse.Data,
3638
callTypes: callTypesResponse.Data,
3739
isLoading: false,
40+
lastFetchedAt: Date.now(),
3841
});
3942
},
4043
fetchCalls: async () => {
4144
set({ isLoading: true, error: null });
4245
try {
4346
const response = await getCalls();
44-
set({ calls: response.Data, isLoading: false });
47+
set({ calls: response.Data, isLoading: false, lastFetchedAt: Date.now() });
4548
} catch (error) {
4649
set({ error: 'Failed to fetch calls', isLoading: false });
4750
}

src/stores/status/store.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ type DestinationType = 'none' | 'call' | 'station';
2222
// Status type that can accept both custom statuses and regular statuses
2323
type StatusType = CustomStatusResultData | StatusesResultData;
2424

25+
// Store TTL: 5 minutes in milliseconds
26+
const STORE_TTL_MS = 5 * 60 * 1000;
27+
2528
interface StatusBottomSheetStore {
2629
isOpen: boolean;
2730
currentStep: StatusStep;
@@ -77,21 +80,24 @@ export const useStatusBottomSheetStore = create<StatusBottomSheetStore>((set, ge
7780
fetchDestinationData: async (unitId: string) => {
7881
set({ isLoading: true, error: null });
7982
try {
80-
// Check if we already have calls in the calls store to avoid redundant API calls
83+
// Check if we already have calls in the calls store and if they're still fresh
8184
const callsStore = useCallsStore.getState();
8285
const existingCalls = callsStore.calls;
86+
const lastFetchedAt = callsStore.lastFetchedAt || 0;
87+
const isStale = !lastFetchedAt || Date.now() - lastFetchedAt > STORE_TTL_MS;
8388

84-
// Only fetch calls if we don't have any in the store
89+
// Fetch calls if we don't have any or if they're stale
8590
// Groups are cached (2 day TTL) so getAllGroups is already fast
86-
const needsCallsFetch = existingCalls.length === 0;
91+
const needsCallsFetch = existingCalls.length === 0 || isStale;
8792

8893
if (needsCallsFetch) {
8994
// Fetch calls and groups in parallel
9095
const [callsResponse, groupsResponse] = await Promise.all([getCalls(), getAllGroups()]);
9196

92-
// Also update the calls store so other parts of the app benefit
93-
useCallsStore.setState({ calls: callsResponse.Data || [] });
97+
// Update the calls store with fresh data and timestamp
98+
useCallsStore.setState({ calls: callsResponse.Data || [], lastFetchedAt: Date.now() });
9499

100+
// Set availableCalls from the fresh response
95101
set({
96102
availableCalls: callsResponse.Data || [],
97103
availableStations: groupsResponse.Data || [],

0 commit comments

Comments
 (0)