Skip to content

Commit 2bd8ea1

Browse files
authored
Merge pull request #198 from Resgrid/develop
RU-T46 Fixing issue with call creation map, perf fix for action. Live…
2 parents 05a4e7f + 8da9211 commit 2bd8ea1

26 files changed

Lines changed: 774 additions & 344 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Mock for @livekit/react-native-webrtc
2+
export const RTCAudioSession = {
3+
configure: jest.fn().mockResolvedValue(undefined),
4+
setCategory: jest.fn().mockResolvedValue(undefined),
5+
setMode: jest.fn().mockResolvedValue(undefined),
6+
getActiveAudioSession: jest.fn().mockReturnValue(null),
7+
setActive: jest.fn().mockResolvedValue(undefined),
8+
};
9+
10+
export default {
11+
RTCAudioSession,
12+
};

expo-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/// <reference types="expo/types" />
22

3-
// NOTE: This file should not be edited and should be in your git ignore
3+
// NOTE: This file should not be edited and should be in your git ignore

src/api/calls/calls.ts

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
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';
45
import { type SaveCallResult } from '@/models/v4/calls/saveCallResult';
56

7+
import { createCachedApiEndpoint } from '../common/cached-client';
68
import { createApiEndpoint } from '../common/client';
79

8-
const callsApi = createApiEndpoint('/Calls/GetActiveCalls');
10+
const callsApi = createCachedApiEndpoint('/Calls/GetActiveCalls', {
11+
ttl: 30 * 1000, // Cache for 30 seconds - calls can change frequently
12+
enabled: true,
13+
});
914
const getCallApi = createApiEndpoint('/Calls/GetCall');
1015
const getCallExtraDataApi = createApiEndpoint('/Calls/GetCallExtraData');
1116
const createCallApi = createApiEndpoint('/Calls/SaveCall');
@@ -78,33 +83,34 @@ export interface CloseCallRequest {
7883
note?: string;
7984
}
8085

81-
export const createCall = async (callData: CreateCallRequest) => {
82-
let dispatchList = '';
83-
84-
if (callData.dispatchEveryone) {
85-
dispatchList = '0';
86-
} else {
87-
const dispatchEntries: string[] = [];
88-
89-
if (callData.dispatchUsers) {
90-
//dispatchEntries.push(...callData.dispatchUsers.map((user) => `U:${user}`));
91-
dispatchEntries.push(...callData.dispatchUsers);
92-
}
93-
if (callData.dispatchGroups) {
94-
//dispatchEntries.push(...callData.dispatchGroups.map((group) => `G:${group}`));
95-
dispatchEntries.push(...callData.dispatchGroups);
96-
}
97-
if (callData.dispatchRoles) {
98-
//dispatchEntries.push(...callData.dispatchRoles.map((role) => `R:${role}`));
99-
dispatchEntries.push(...callData.dispatchRoles);
100-
}
101-
if (callData.dispatchUnits) {
102-
//dispatchEntries.push(...callData.dispatchUnits.map((unit) => `U:${unit}`));
103-
dispatchEntries.push(...callData.dispatchUnits);
104-
}
105-
106-
dispatchList = dispatchEntries.join('|');
86+
/**
87+
* Helper function to build the dispatch list string from dispatch data
88+
*/
89+
const buildDispatchList = (data: { dispatchEveryone?: boolean; dispatchUsers?: string[]; dispatchGroups?: string[]; dispatchRoles?: string[]; dispatchUnits?: string[] }): string => {
90+
if (data.dispatchEveryone) {
91+
return '0';
92+
}
93+
94+
const dispatchEntries: string[] = [];
95+
96+
if (data.dispatchUsers) {
97+
dispatchEntries.push(...data.dispatchUsers);
98+
}
99+
if (data.dispatchGroups) {
100+
dispatchEntries.push(...data.dispatchGroups);
101+
}
102+
if (data.dispatchRoles) {
103+
dispatchEntries.push(...data.dispatchRoles);
107104
}
105+
if (data.dispatchUnits) {
106+
dispatchEntries.push(...data.dispatchUnits);
107+
}
108+
109+
return dispatchEntries.join('|');
110+
};
111+
112+
export const createCall = async (callData: CreateCallRequest) => {
113+
const dispatchList = buildDispatchList(callData);
108114

109115
const data = {
110116
Name: callData.name,
@@ -122,36 +128,20 @@ export const createCall = async (callData: CreateCallRequest) => {
122128
};
123129

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

128143
export const updateCall = async (callData: UpdateCallRequest) => {
129-
let dispatchList = '';
130-
131-
if (callData.dispatchEveryone) {
132-
dispatchList = '0';
133-
} else {
134-
const dispatchEntries: string[] = [];
135-
136-
if (callData.dispatchUsers) {
137-
//dispatchEntries.push(...callData.dispatchUsers.map((user) => `U:${user}`));
138-
dispatchEntries.push(...callData.dispatchUsers);
139-
}
140-
if (callData.dispatchGroups) {
141-
//dispatchEntries.push(...callData.dispatchGroups.map((group) => `G:${group}`));
142-
dispatchEntries.push(...callData.dispatchGroups);
143-
}
144-
if (callData.dispatchRoles) {
145-
//dispatchEntries.push(...callData.dispatchRoles.map((role) => `R:${role}`));
146-
dispatchEntries.push(...callData.dispatchRoles);
147-
}
148-
if (callData.dispatchUnits) {
149-
//dispatchEntries.push(...callData.dispatchUnits.map((unit) => `U:${unit}`));
150-
dispatchEntries.push(...callData.dispatchUnits);
151-
}
152-
153-
dispatchList = dispatchEntries.join('|');
154-
}
144+
const dispatchList = buildDispatchList(callData);
155145

156146
const data = {
157147
CallId: callData.callId,
@@ -170,6 +160,15 @@ export const updateCall = async (callData: UpdateCallRequest) => {
170160
};
171161

172162
const response = await updateCallApi.post<SaveCallResult>(data);
163+
164+
// Invalidate cache after successful mutation
165+
try {
166+
cacheManager.remove('/Calls/GetActiveCalls');
167+
} catch (error) {
168+
// Silently handle cache removal errors
169+
console.warn('Failed to invalidate calls cache:', error);
170+
}
171+
173172
return response.data;
174173
};
175174

@@ -181,5 +180,14 @@ export const closeCall = async (callData: CloseCallRequest) => {
181180
};
182181

183182
const response = await closeCallApi.put<SaveCallResult>(data);
183+
184+
// Invalidate cache after successful mutation
185+
try {
186+
cacheManager.remove('/Calls/GetActiveCalls');
187+
} catch (error) {
188+
// Silently handle cache removal errors
189+
console.warn('Failed to invalidate calls cache:', error);
190+
}
191+
184192
return response.data;
185193
};

src/components/calls/__tests__/call-files-modal.test.tsx

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,29 @@ const defaultMockFiles = [
3737
},
3838
];
3939

40-
let mockStoreState: any = {
41-
callFiles: defaultMockFiles,
40+
// Create a single object that will be mutated - never reassign!
41+
const mockStoreState = {
42+
callFiles: defaultMockFiles as any,
4243
isLoadingFiles: false,
43-
errorFiles: null,
44+
errorFiles: null as string | null,
4445
fetchCallFiles: mockFetchCallFiles,
4546
clearFiles: mockClearFiles,
4647
};
4748

49+
// Helper function to update mock state without replacing the object
50+
const setMockStoreState = (updates: Partial<typeof mockStoreState>) => {
51+
Object.assign(mockStoreState, updates);
52+
};
53+
54+
// Reset mock state to defaults
55+
const resetMockStoreState = () => {
56+
mockStoreState.callFiles = defaultMockFiles;
57+
mockStoreState.isLoadingFiles = false;
58+
mockStoreState.errorFiles = null;
59+
mockStoreState.fetchCallFiles = mockFetchCallFiles;
60+
mockStoreState.clearFiles = mockClearFiles;
61+
};
62+
4863
jest.mock('@/stores/calls/detail-store', () => ({
4964
useCallDetailStore: () => mockStoreState,
5065
}));
@@ -301,13 +316,7 @@ describe('CallFilesModal', () => {
301316
beforeEach(() => {
302317
jest.clearAllMocks();
303318
// Reset to default state
304-
mockStoreState = {
305-
callFiles: defaultMockFiles,
306-
isLoadingFiles: false,
307-
errorFiles: null,
308-
fetchCallFiles: mockFetchCallFiles,
309-
clearFiles: mockClearFiles,
310-
};
319+
resetMockStoreState();
311320
});
312321

313322
it('renders correctly when closed', () => {
@@ -421,13 +430,11 @@ describe('CallFilesModal', () => {
421430
describe('Loading States', () => {
422431
beforeEach(() => {
423432
// Mock loading state
424-
mockStoreState = {
433+
setMockStoreState({
425434
callFiles: null,
426435
isLoadingFiles: true,
427436
errorFiles: null,
428-
fetchCallFiles: mockFetchCallFiles,
429-
clearFiles: mockClearFiles,
430-
};
437+
});
431438
});
432439

433440
it('displays loading spinner when fetching files', () => {
@@ -443,13 +450,11 @@ describe('CallFilesModal', () => {
443450
describe('Error States', () => {
444451
beforeEach(() => {
445452
// Mock error state
446-
mockStoreState = {
453+
setMockStoreState({
447454
callFiles: [],
448455
isLoadingFiles: false,
449456
errorFiles: 'Network error occurred',
450-
fetchCallFiles: mockFetchCallFiles,
451-
clearFiles: mockClearFiles,
452-
};
457+
});
453458
});
454459

455460
it('displays error message when file fetch fails', () => {
@@ -476,13 +481,11 @@ describe('CallFilesModal', () => {
476481
describe('Empty States', () => {
477482
beforeEach(() => {
478483
// Mock empty state
479-
mockStoreState = {
484+
setMockStoreState({
480485
callFiles: [],
481486
isLoadingFiles: false,
482487
errorFiles: null,
483-
fetchCallFiles: mockFetchCallFiles,
484-
clearFiles: mockClearFiles,
485-
};
488+
});
486489
});
487490

488491
it('displays empty state when no files available', () => {
@@ -503,12 +506,11 @@ describe('CallFilesModal', () => {
503506

504507
beforeEach(() => {
505508
// Reset to default state with files
506-
mockStoreState = {
509+
setMockStoreState({
507510
callFiles: defaultMockFiles,
508511
isLoadingFiles: false,
509512
errorFiles: null,
510-
fetchCallFiles: mockFetchCallFiles,
511-
};
513+
});
512514
});
513515

514516
it('downloads and shares file when clicked', async () => {
@@ -591,13 +593,7 @@ describe('CallFilesModal', () => {
591593
describe('File Format Utilities', () => {
592594
beforeEach(() => {
593595
// Reset to default state
594-
mockStoreState = {
595-
callFiles: defaultMockFiles,
596-
isLoadingFiles: false,
597-
errorFiles: null,
598-
fetchCallFiles: mockFetchCallFiles,
599-
clearFiles: mockClearFiles,
600-
};
596+
resetMockStoreState();
601597
});
602598

603599
it('formats file sizes correctly', () => {
@@ -625,13 +621,7 @@ describe('CallFilesModal', () => {
625621
beforeEach(() => {
626622
jest.clearAllMocks();
627623
// Reset to default state
628-
mockStoreState = {
629-
callFiles: defaultMockFiles,
630-
isLoadingFiles: false,
631-
errorFiles: null,
632-
fetchCallFiles: mockFetchCallFiles,
633-
clearFiles: mockClearFiles,
634-
};
624+
resetMockStoreState();
635625
});
636626

637627
it('should track analytics event when modal is opened', () => {
@@ -653,10 +643,9 @@ describe('CallFilesModal', () => {
653643
});
654644

655645
it('should track analytics event with loading state', () => {
656-
mockStoreState = {
657-
...mockStoreState,
646+
setMockStoreState({
658647
isLoadingFiles: true,
659-
};
648+
});
660649

661650
render(<CallFilesModal {...defaultProps} isOpen={true} callId="test-call-456" />);
662651

@@ -670,10 +659,9 @@ describe('CallFilesModal', () => {
670659
});
671660

672661
it('should track analytics event with error state', () => {
673-
mockStoreState = {
674-
...mockStoreState,
662+
setMockStoreState({
675663
errorFiles: 'Failed to load files',
676-
};
664+
});
677665

678666
render(<CallFilesModal {...defaultProps} isOpen={true} callId="test-call-error" />);
679667

@@ -687,10 +675,9 @@ describe('CallFilesModal', () => {
687675
});
688676

689677
it('should track analytics event with no files', () => {
690-
mockStoreState = {
691-
...mockStoreState,
678+
setMockStoreState({
692679
callFiles: [],
693-
};
680+
});
694681

695682
render(<CallFilesModal {...defaultProps} isOpen={true} callId="test-call-no-files" />);
696683

0 commit comments

Comments
 (0)