Skip to content

Commit f713d52

Browse files
committed
refactor(voip): peer autocomplete slice + NewMediaCall trims
- Pass username/sipEnabled into getPeerAutocompleteOptions (remove auxStore) - Thread auth from FilterHeader via useSelector into fetchOptions - Remove setFilter/clearSelection; use setState/setSelectedPeer(null) - Async fetch: filter+peer snapshot + monotonic seq; reset invalidates in-flight - Inline NewMediaCall screen chrome; remove Container + Container story - PeerList passes option directly; composite FlatList keys - Add getPeerAutocompleteOptions tests; extend store tests (reset, stale, seq) Made-with: Cursor
1 parent 38ca1df commit f713d52

13 files changed

Lines changed: 326 additions & 245 deletions

app/containers/NewMediaCall/Container.stories.tsx

Lines changed: 0 additions & 40 deletions
This file was deleted.

app/containers/NewMediaCall/Container.tsx

Lines changed: 0 additions & 18 deletions
This file was deleted.

app/containers/NewMediaCall/FilterHeader.test.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,11 @@ describe('FilterHeader', () => {
3737
});
3838

3939
it('should update filter, clear selection and fetch options after debounce', () => {
40-
const setFilter = jest.fn();
41-
const clearSelection = jest.fn();
4240
const fetchOptions = jest.fn();
4341
usePeerAutocompleteStore.setState({
4442
filter: '',
4543
selectedPeer: null,
4644
options: [],
47-
setFilter,
48-
clearSelection,
4945
fetchOptions
5046
});
5147

@@ -57,15 +53,21 @@ describe('FilterHeader', () => {
5753

5854
fireEvent.changeText(getByTestId('new-media-call-search-input'), 'alice');
5955

60-
expect(setFilter).toHaveBeenCalledWith('alice');
61-
expect(clearSelection).toHaveBeenCalledTimes(1);
56+
expect(usePeerAutocompleteStore.getState().filter).toBe('alice');
57+
expect(usePeerAutocompleteStore.getState().selectedPeer).toBeNull();
6258
expect(fetchOptions).not.toHaveBeenCalled();
6359

6460
act(() => {
6561
jest.advanceTimersByTime(textInputDebounceTime);
6662
});
6763

68-
expect(fetchOptions).toHaveBeenCalledWith('alice');
64+
expect(fetchOptions).toHaveBeenCalledTimes(1);
65+
expect(fetchOptions.mock.calls[0][0]).toBe('alice');
66+
expect(fetchOptions.mock.calls[0][1]).toEqual(
67+
expect.objectContaining({
68+
sipEnabled: expect.any(Boolean)
69+
})
70+
);
6971
});
7072
});
7173

app/containers/NewMediaCall/FilterHeader.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
1-
import React from 'react';
1+
import React, { useCallback } from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
3+
import { useSelector } from 'react-redux';
34

45
import I18n from '../../i18n';
56
import { useTheme } from '../../theme';
67
import { FormTextInput } from '../TextInput';
78
import sharedStyles from '../../views/Styles';
89
import { textInputDebounceTime } from '../../lib/constants/debounceConfig';
910
import { useDebounce } from '../../lib/methods/helpers';
11+
import type { IApplicationState } from '../../definitions';
1012
import { usePeerAutocompleteStore } from '../../lib/services/voip/usePeerAutocompleteStore';
1113

1214
export const FilterHeader = (): React.ReactElement => {
1315
const { colors } = useTheme();
1416

1517
const filter = usePeerAutocompleteStore(state => state.filter);
16-
const setFilter = usePeerAutocompleteStore(state => state.setFilter);
17-
const fetchOptions = usePeerAutocompleteStore(state => state.fetchOptions);
18-
const clearSelection = usePeerAutocompleteStore(state => state.clearSelection);
1918

20-
const debouncedFetchOptions = useDebounce((value: string) => {
21-
fetchOptions(value);
22-
}, textInputDebounceTime);
19+
const username = useSelector((state: IApplicationState) => state.login.user?.username);
20+
const sipEnabled = useSelector((state: IApplicationState) =>
21+
Boolean(state.settings.VoIP_TeamCollab_SIP_Integration_For_Internal_Calls)
22+
);
23+
24+
const debouncedFetchOptions = useDebounce(
25+
useCallback(
26+
(value: string) => {
27+
usePeerAutocompleteStore.getState().fetchOptions(value, { username, sipEnabled });
28+
},
29+
[username, sipEnabled]
30+
),
31+
textInputDebounceTime
32+
);
2333

2434
const handleChangeText = (value: string) => {
25-
setFilter(value);
26-
clearSelection();
35+
usePeerAutocompleteStore.setState({ filter: value, selectedPeer: null });
2736
debouncedFetchOptions(value);
2837
};
2938

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
import React, { useEffect } from 'react';
2+
import { StyleSheet, View } from 'react-native';
23

34
import { PeerList } from './PeerList';
45
import { SelectedPeer } from './SelectedPeer';
5-
import { Container } from './Container';
66
import { CreateCall } from './CreateCall';
77
import { FilterHeader } from './FilterHeader';
88
import { usePeerAutocompleteStore } from '../../lib/services/voip/usePeerAutocompleteStore';
9+
import { useTheme } from '../../theme';
910

1011
export const NewMediaCall = (): React.ReactElement => {
11-
// reset the store when the action sheet is closed
12+
const { colors } = useTheme();
1213
const reset = usePeerAutocompleteStore(state => state.reset);
1314
useEffect(() => () => reset(), [reset]);
1415

1516
return (
16-
<Container>
17+
<View style={[styles.screen, { backgroundColor: colors.surfaceLight }]}>
1718
<FilterHeader />
1819
<SelectedPeer />
1920
<PeerList />
2021
<CreateCall />
21-
</Container>
22+
</View>
2223
);
2324
};
25+
26+
const styles = StyleSheet.create({
27+
screen: {
28+
flex: 1,
29+
paddingHorizontal: 16,
30+
paddingTop: 16
31+
}
32+
});

app/containers/NewMediaCall/PeerList.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,7 @@ export const PeerList = () => {
1313
if (selectedPeer) return null;
1414

1515
const handleSelectOption = (option: TPeerItem) => {
16-
const peerItem: TPeerItem =
17-
option.type === 'sip'
18-
? { type: 'sip', value: option.value, label: option.label }
19-
: {
20-
type: 'user',
21-
value: option.value,
22-
label: option.label,
23-
username: option.username,
24-
callerId: option.callerId
25-
};
26-
27-
setSelectedPeer(peerItem);
16+
setSelectedPeer(option);
2817
};
2918

3019
const renderItem = ({ item }: { item: TPeerItem }) => <PeerItem item={item} onSelectOption={handleSelectOption} />;
@@ -33,7 +22,7 @@ export const PeerList = () => {
3322
<FlatList
3423
data={options}
3524
contentContainerStyle={{ flexShrink: 1 }}
36-
keyExtractor={item => item.value}
25+
keyExtractor={item => `${item.type}:${item.value}`}
3726
keyboardShouldPersistTaps='always'
3827
renderItem={renderItem}
3928
ItemSeparatorComponent={List.Separator}

app/containers/NewMediaCall/SelectedPeer.test.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ describe('SelectedPeer', () => {
6969
expect(getByText('+55 11 99999-9999')).toBeTruthy();
7070
});
7171

72-
it('should call clearSelection when clear button is pressed', () => {
73-
const clearSelection = jest.fn();
74-
usePeerAutocompleteStore.setState({ selectedPeer: userPeer, clearSelection });
72+
it('should clear selected peer when clear button is pressed', () => {
73+
usePeerAutocompleteStore.setState({ selectedPeer: userPeer });
7574

7675
const { getByTestId } = render(
7776
<Wrapper>
@@ -80,7 +79,7 @@ describe('SelectedPeer', () => {
8079
);
8180

8281
fireEvent.press(getByTestId('new-media-call-clear-selected-peer'));
83-
expect(clearSelection).toHaveBeenCalledTimes(1);
82+
expect(usePeerAutocompleteStore.getState().selectedPeer).toBeNull();
8483
});
8584
});
8685

app/containers/NewMediaCall/SelectedPeer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const SelectedPeer = () => {
1515

1616
export const SelectedPeerInner = ({ selectedPeer }: { selectedPeer: TPeerItem | null }) => {
1717
const { colors } = useTheme();
18-
const clearSelection = usePeerAutocompleteStore(state => state.clearSelection);
18+
const setSelectedPeer = usePeerAutocompleteStore(state => state.setSelectedPeer);
1919

2020
if (!selectedPeer) return null;
2121

@@ -24,7 +24,7 @@ export const SelectedPeerInner = ({ selectedPeer }: { selectedPeer: TPeerItem |
2424
<View style={[styles.selectedTag, { backgroundColor: colors.buttonBackgroundSecondaryDefault }]}>
2525
<PeerItemInner item={selectedPeer} />
2626
<BorderlessButton
27-
onPress={clearSelection}
27+
onPress={() => setSelectedPeer(null)}
2828
testID='new-media-call-clear-selected-peer'
2929
rippleColor={colors.buttonBackgroundSecondaryPress}
3030
foreground

app/containers/NewMediaCall/__snapshots__/SelectedPeer.test.tsx.snap

Lines changed: 6 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -201,19 +201,7 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
201201
onActiveStateChange={[Function]}
202202
onGestureHandlerEvent={[Function]}
203203
onGestureHandlerStateChange={[Function]}
204-
onPress={
205-
[MockFunction] {
206-
"calls": [
207-
[],
208-
],
209-
"results": [
210-
{
211-
"type": "return",
212-
"value": undefined,
213-
},
214-
],
215-
}
216-
}
204+
onPress={[Function]}
217205
rippleColor="#9EA2A8"
218206
style={
219207
[
@@ -386,19 +374,7 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
386374
onActiveStateChange={[Function]}
387375
onGestureHandlerEvent={[Function]}
388376
onGestureHandlerStateChange={[Function]}
389-
onPress={
390-
[MockFunction] {
391-
"calls": [
392-
[],
393-
],
394-
"results": [
395-
{
396-
"type": "return",
397-
"value": undefined,
398-
},
399-
],
400-
}
401-
}
377+
onPress={[Function]}
402378
rippleColor="#9EA2A8"
403379
style={
404380
[
@@ -633,19 +609,7 @@ exports[`Story Snapshots: All should match snapshot 1`] = `
633609
onActiveStateChange={[Function]}
634610
onGestureHandlerEvent={[Function]}
635611
onGestureHandlerStateChange={[Function]}
636-
onPress={
637-
[MockFunction] {
638-
"calls": [
639-
[],
640-
],
641-
"results": [
642-
{
643-
"type": "return",
644-
"value": undefined,
645-
},
646-
],
647-
}
648-
}
612+
onPress={[Function]}
649613
rippleColor="#9EA2A8"
650614
style={
651615
[
@@ -893,19 +857,7 @@ exports[`Story Snapshots: LongUsername should match snapshot 1`] = `
893857
onActiveStateChange={[Function]}
894858
onGestureHandlerEvent={[Function]}
895859
onGestureHandlerStateChange={[Function]}
896-
onPress={
897-
[MockFunction] {
898-
"calls": [
899-
[],
900-
],
901-
"results": [
902-
{
903-
"type": "return",
904-
"value": undefined,
905-
},
906-
],
907-
}
908-
}
860+
onPress={[Function]}
909861
rippleColor="#9EA2A8"
910862
style={
911863
[
@@ -1091,19 +1043,7 @@ exports[`Story Snapshots: Sip should match snapshot 1`] = `
10911043
onActiveStateChange={[Function]}
10921044
onGestureHandlerEvent={[Function]}
10931045
onGestureHandlerStateChange={[Function]}
1094-
onPress={
1095-
[MockFunction] {
1096-
"calls": [
1097-
[],
1098-
],
1099-
"results": [
1100-
{
1101-
"type": "return",
1102-
"value": undefined,
1103-
},
1104-
],
1105-
}
1106-
}
1046+
onPress={[Function]}
11071047
rippleColor="#9EA2A8"
11081048
style={
11091049
[
@@ -1351,19 +1291,7 @@ exports[`Story Snapshots: User should match snapshot 1`] = `
13511291
onActiveStateChange={[Function]}
13521292
onGestureHandlerEvent={[Function]}
13531293
onGestureHandlerStateChange={[Function]}
1354-
onPress={
1355-
[MockFunction] {
1356-
"calls": [
1357-
[],
1358-
],
1359-
"results": [
1360-
{
1361-
"type": "return",
1362-
"value": undefined,
1363-
},
1364-
],
1365-
}
1366-
}
1294+
onPress={[Function]}
13671295
rippleColor="#9EA2A8"
13681296
style={
13691297
[

0 commit comments

Comments
 (0)