@planningcenter/chat-react-native 4.0.1-qa-staging.2 → 4.0.1-qa-staging.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/build/components/conversation/message.d.ts.map +1 -1
  2. package/build/components/conversation/message.js +2 -1
  3. package/build/components/conversation/message.js.map +1 -1
  4. package/build/contexts/api_provider.d.ts.map +1 -1
  5. package/build/contexts/api_provider.js +12 -1
  6. package/build/contexts/api_provider.js.map +1 -1
  7. package/build/hooks/use_features.d.ts +16 -0
  8. package/build/hooks/use_features.d.ts.map +1 -1
  9. package/build/hooks/use_features.js +43 -47
  10. package/build/hooks/use_features.js.map +1 -1
  11. package/build/screens/conversation_new/components/groups_form.js +80 -71
  12. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  13. package/build/screens/conversations/components/chat_group_badge.d.ts.map +1 -1
  14. package/build/screens/conversations/components/chat_group_badge.js +1 -0
  15. package/build/screens/conversations/components/chat_group_badge.js.map +1 -1
  16. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  17. package/build/screens/conversations/components/list_header_component.js +157 -150
  18. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  19. package/build/screens/conversations/components/search_list_header_component.d.ts +2 -0
  20. package/build/screens/conversations/components/search_list_header_component.d.ts.map +1 -0
  21. package/build/screens/conversations/components/search_list_header_component.js +496 -0
  22. package/build/screens/conversations/components/search_list_header_component.js.map +1 -0
  23. package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
  24. package/build/screens/conversations/conversations_screen.js +28 -16
  25. package/build/screens/conversations/conversations_screen.js.map +1 -1
  26. package/build/utils/time.d.ts +1 -0
  27. package/build/utils/time.d.ts.map +1 -1
  28. package/build/utils/time.js +1 -0
  29. package/build/utils/time.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/__tests__/hooks/use_features.test.ts +61 -0
  32. package/src/components/conversation/message.tsx +6 -1
  33. package/src/contexts/api_provider.tsx +4 -0
  34. package/src/hooks/use_features.ts +26 -14
  35. package/src/screens/conversation_new/components/groups_form.tsx +7 -2
  36. package/src/screens/conversations/components/__tests__/chat_group_badge.test.tsx +32 -0
  37. package/src/screens/conversations/components/__tests__/list_header_component.test.tsx +66 -0
  38. package/src/screens/conversations/components/chat_group_badge.tsx +1 -0
  39. package/src/screens/conversations/components/list_header_component.tsx +18 -28
  40. package/src/screens/conversations/components/search_list_header_component.tsx +291 -0
  41. package/src/screens/conversations/conversations_screen.tsx +6 -1
  42. package/src/utils/time.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/C,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAS1D;AAED,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,cAAc,QAAsB,CAAA;AACjD,eAAO,MAAM,QAAQ,QAAmB,CAAA"}
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/C,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAS1D;AAED,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,UAAU,QAAqB,CAAA;AAC5C,eAAO,MAAM,YAAY,QAAqB,CAAA;AAC9C,eAAO,MAAM,cAAc,QAAsB,CAAA;AACjD,eAAO,MAAM,QAAQ,QAAmB,CAAA"}
@@ -9,6 +9,7 @@ export function toMs(value, unit) {
9
9
  }
10
10
  }
11
11
  export const FIVE_SECONDS = toMs(5, 'seconds');
12
+ export const ONE_MINUTE = toMs(1, 'minutes');
12
13
  export const FIVE_MINUTES = toMs(5, 'minutes');
13
14
  export const THIRTY_MINUTES = toMs(30, 'minutes');
14
15
  export const ONE_HOUR = toMs(1, 'hours');
@@ -1 +1 @@
1
- {"version":3,"file":"time.js","names":["toMs","value","unit","FIVE_SECONDS","FIVE_MINUTES","THIRTY_MINUTES","ONE_HOUR"],"sources":["../../src/utils/time.ts"],"sourcesContent":["type TimeUnit = 'hours' | 'minutes' | 'seconds'\n\nexport function toMs(value: number, unit: TimeUnit): number {\n switch (unit) {\n case 'hours':\n return value * 60 * 60 * 1000\n case 'minutes':\n return value * 60 * 1000\n case 'seconds':\n return value * 1000\n }\n}\n\nexport const FIVE_SECONDS = toMs(5, 'seconds')\nexport const FIVE_MINUTES = toMs(5, 'minutes')\nexport const THIRTY_MINUTES = toMs(30, 'minutes')\nexport const ONE_HOUR = toMs(1, 'hours')\n"],"mappings":"AAEA,OAAO,SAASA,IAAIA,CAACC,KAAa,EAAEC,IAAc,EAAU;EAC1D,QAAQA,IAAI;IACV,KAAK,OAAO;MACV,OAAOD,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IAC/B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,EAAE,GAAG,IAAI;IAC1B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,IAAI;EACvB;AACF;AAEA,OAAO,MAAME,YAAY,GAAGH,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMI,YAAY,GAAGJ,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMK,cAAc,GAAGL,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC;AACjD,OAAO,MAAMM,QAAQ,GAAGN,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"time.js","names":["toMs","value","unit","FIVE_SECONDS","ONE_MINUTE","FIVE_MINUTES","THIRTY_MINUTES","ONE_HOUR"],"sources":["../../src/utils/time.ts"],"sourcesContent":["type TimeUnit = 'hours' | 'minutes' | 'seconds'\n\nexport function toMs(value: number, unit: TimeUnit): number {\n switch (unit) {\n case 'hours':\n return value * 60 * 60 * 1000\n case 'minutes':\n return value * 60 * 1000\n case 'seconds':\n return value * 1000\n }\n}\n\nexport const FIVE_SECONDS = toMs(5, 'seconds')\nexport const ONE_MINUTE = toMs(1, 'minutes')\nexport const FIVE_MINUTES = toMs(5, 'minutes')\nexport const THIRTY_MINUTES = toMs(30, 'minutes')\nexport const ONE_HOUR = toMs(1, 'hours')\n"],"mappings":"AAEA,OAAO,SAASA,IAAIA,CAACC,KAAa,EAAEC,IAAc,EAAU;EAC1D,QAAQA,IAAI;IACV,KAAK,OAAO;MACV,OAAOD,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IAC/B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,EAAE,GAAG,IAAI;IAC1B,KAAK,SAAS;MACZ,OAAOA,KAAK,GAAG,IAAI;EACvB;AACF;AAEA,OAAO,MAAME,YAAY,GAAGH,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMI,UAAU,GAAGJ,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC5C,OAAO,MAAMK,YAAY,GAAGL,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;AAC9C,OAAO,MAAMM,cAAc,GAAGN,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC;AACjD,OAAO,MAAMO,QAAQ,GAAGP,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "4.0.1-qa-staging.2",
3
+ "version": "4.0.1-qa-staging.4",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
35
35
  "@fortawesome/react-native-fontawesome": "^0.3.2",
36
- "@planningcenter/emoji-keyboard": "4.0.1-qa-staging.2",
36
+ "@planningcenter/emoji-keyboard": "4.0.1-qa-staging.4",
37
37
  "lodash-inflection": "^1.5.0"
38
38
  },
39
39
  "peerDependencies": {
@@ -84,5 +84,5 @@
84
84
  "sf-symbols-typescript": "^2.2.0",
85
85
  "typescript": "~6.0.3"
86
86
  },
87
- "gitHead": "092bd6133bddee97b0fbdaab954d5a0984988a56"
87
+ "gitHead": "a7faa8fb4d6db21faac3abab923e22e66ffbbb70"
88
88
  }
@@ -0,0 +1,61 @@
1
+ import { buildTestQueryClient } from '../../__utils__/query_client'
2
+ import type { ApiClient } from '../../hooks/use_api_client'
3
+ import { featuresQueryOptions } from '../../hooks/use_features'
4
+
5
+ const features = {
6
+ data: [{ type: 'Feature', id: '1', name: 'ROLLOUT_search', enabled: true }],
7
+ links: {},
8
+ meta: { count: 1, totalCount: 1 },
9
+ }
10
+
11
+ const buildClient = (get: jest.Mock) => ({ chat: { get } }) as unknown as ApiClient
12
+
13
+ const fetchTwice = async (get: jest.Mock, secondsBetween: number) => {
14
+ const queryClient = buildTestQueryClient()
15
+ const options = featuresQueryOptions(buildClient(get))
16
+
17
+ await queryClient.fetchQuery(options)
18
+ jest.advanceTimersByTime(secondsBetween * 1000)
19
+ await queryClient.fetchQuery(options)
20
+ }
21
+
22
+ describe('featuresQueryOptions', () => {
23
+ beforeEach(() => {
24
+ jest.useFakeTimers()
25
+ })
26
+
27
+ afterEach(() => {
28
+ jest.useRealTimers()
29
+ })
30
+
31
+ it('keeps successful flags fresh for five minutes', async () => {
32
+ const get = jest.fn().mockResolvedValue(features)
33
+
34
+ await fetchTwice(get, 4 * 60)
35
+
36
+ expect(get).toHaveBeenCalledTimes(1)
37
+ })
38
+
39
+ it('refetches right away after a failed fetch', async () => {
40
+ const get = jest.fn().mockRejectedValue({ status: 503 })
41
+
42
+ await fetchTwice(get, 1)
43
+
44
+ expect(get).toHaveBeenCalledTimes(2)
45
+ })
46
+
47
+ it('backs off for a minute after a 429', async () => {
48
+ const get = jest.fn().mockRejectedValue({ status: 429 })
49
+ const queryClient = buildTestQueryClient()
50
+ const options = featuresQueryOptions(buildClient(get))
51
+
52
+ await queryClient.fetchQuery(options)
53
+ jest.advanceTimersByTime(30 * 1000)
54
+ await queryClient.fetchQuery(options)
55
+ expect(get).toHaveBeenCalledTimes(1)
56
+
57
+ jest.advanceTimersByTime(31 * 1000)
58
+ await queryClient.fetchQuery(options)
59
+ expect(get).toHaveBeenCalledTimes(2)
60
+ })
61
+ })
@@ -107,9 +107,14 @@ export const Message = memo(function MessageRow({
107
107
  const messageIsReplyLabel = replyToReplyRootMessage ? 'Reply' : ''
108
108
  const attachmentLabel = some(attachments) ? pluralize(attachments.length, 'attachment') : ''
109
109
  const replyCountLabel = isReplyRootMessage ? replyCountText : ''
110
+ const saveStatusLabel = error
111
+ ? `Failed to ${isPersisted ? 'edit' : 'send'}`
112
+ : pending
113
+ ? messagePendingLabel
114
+ : ''
110
115
  const accessibilityLabel = `${author?.name || ''} ${messageIsReplyLabel} ${attachmentLabel} ${
111
116
  messageText || ''
112
- } ${timestamp} ${replyCountLabel}`
117
+ } ${saveStatusLabel} ${timestamp} ${replyCountLabel}`
113
118
 
114
119
  useEffect(() => {
115
120
  if (pending) {
@@ -10,6 +10,7 @@ import { ViewProps } from 'react-native'
10
10
  import { ApiClient, useApiClient } from '../hooks/use_api_client'
11
11
  import { useAppState } from '../hooks/use_app_state'
12
12
  import { appGrantsRequestArgs } from '../hooks/use_chat_permissions'
13
+ import { featuresQueryOptions } from '../hooks/use_features'
13
14
  import { getRequestQueryKey, RequestQueryKey } from '../hooks/use_suspense_api'
14
15
  import { emitAuthRefresh } from '../utils/auth_events'
15
16
  import { ResponseError } from '../utils/response_error'
@@ -81,10 +82,13 @@ export function ApiProvider({ children }: ViewProps) {
81
82
  // This needs to live in the provider so that it can access the api client
82
83
  // and the chat query client
83
84
  const PrefetchQueries = () => {
85
+ const client = useApiClient()
86
+
84
87
  usePrefetchQuery({
85
88
  queryKey: getRequestQueryKey(appGrantsRequestArgs),
86
89
  queryFn: defaultQueryFn,
87
90
  })
91
+ usePrefetchQuery(featuresQueryOptions(client))
88
92
 
89
93
  return null
90
94
  }
@@ -1,26 +1,20 @@
1
- import { useSuspenseQuery } from '@tanstack/react-query'
1
+ import { queryOptions, useSuspenseQuery } from '@tanstack/react-query'
2
2
  import { useCallback } from 'react'
3
3
  import { ApiCollection } from '../types'
4
4
  import type { FeatureResource } from '../types/resources/feature_resource'
5
5
  import { getFeaturesRequestArgs, getFeaturesQueryKey } from '../utils/request/get_features'
6
- import { FIVE_MINUTES } from '../utils/time'
7
- import { useApiClient } from './use_api_client'
6
+ import { FIVE_MINUTES, ONE_MINUTE } from '../utils/time'
7
+ import { ApiClient, useApiClient } from './use_api_client'
8
8
 
9
9
  type FeatureName = (typeof availableFeatures)[keyof typeof availableFeatures]
10
10
 
11
+ type FeaturesCollection = ApiCollection<FeatureResource> & {
12
+ failure?: { status?: number }
13
+ }
14
+
11
15
  export function useFeatures() {
12
16
  const apiClient = useApiClient()
13
- const requestArgs = getFeaturesRequestArgs()
14
-
15
- const { data } = useSuspenseQuery({
16
- queryKey: getFeaturesQueryKey(),
17
- queryFn: () => {
18
- return apiClient.chat
19
- .get<ApiCollection<FeatureResource>>(requestArgs)
20
- .catch(() => stableEmptyFeatures)
21
- },
22
- staleTime: FIVE_MINUTES,
23
- })
17
+ const { data } = useSuspenseQuery(featuresQueryOptions(apiClient))
24
18
 
25
19
  const features = data.data
26
20
 
@@ -36,6 +30,23 @@ export function useFeatures() {
36
30
  }
37
31
  }
38
32
 
33
+ export const featuresQueryOptions = (apiClient: ApiClient) =>
34
+ queryOptions({
35
+ queryKey: getFeaturesQueryKey(),
36
+ queryFn: (): Promise<FeaturesCollection> => {
37
+ return apiClient.chat
38
+ .get<ApiCollection<FeatureResource>>(getFeaturesRequestArgs())
39
+ .catch(error => ({ ...stableEmptyFeatures, failure: { status: error?.status } }))
40
+ },
41
+ staleTime: query => {
42
+ const failure = query.state.data?.failure
43
+ if (!failure) return FIVE_MINUTES
44
+
45
+ return failure.status === 429 ? ONE_MINUTE : 0
46
+ },
47
+ gcTime: Infinity,
48
+ })
49
+
39
50
  export const availableFeatures = {
40
51
  gender_specific_conversations: 'ROLLOUT_gender_specific_conversations',
41
52
  message_reporting: 'ROLLOUT_MOBILE_message_reporting',
@@ -44,6 +55,7 @@ export const availableFeatures = {
44
55
  conversation_safety_lock: 'ROLLOUT_conversation_safety_lock',
45
56
  video_moderation: 'ROLLOUT_MOBILE_video_moderation',
46
57
  dm_search_clear_fix: 'ROLLOUT_MOBILE_dm_search_clear_fix',
58
+ search: 'ROLLOUT_search',
47
59
  } as const satisfies Record<string, `ROLLOUT_${string}`>
48
60
 
49
61
  const stableEmptyFeatures: ApiCollection<FeatureResource> = {
@@ -212,9 +212,11 @@ function FormContent({
212
212
  </View>
213
213
  <Divider />
214
214
  <Pressable style={styles.titleSection} onPress={handleTitleSectionPress}>
215
- <Heading variant="h3">Title</Heading>
215
+ <Heading variant="h3">
216
+ Title <Text style={styles.required}>*</Text>
217
+ </Heading>
216
218
  <AutoFocusAwareTextInput
217
- placeholder="Topic of conversation (required)"
219
+ placeholder="Topic of conversation"
218
220
  value={title}
219
221
  onChangeText={setTitle}
220
222
  style={styles.titleInput}
@@ -291,6 +293,9 @@ const useStyles = () => {
291
293
  fontSize: 18,
292
294
  color: colors.textColorDefaultPrimary,
293
295
  },
296
+ required: {
297
+ color: colors.statusErrorText,
298
+ },
294
299
  memberSection: {
295
300
  padding: sectionPadding,
296
301
  paddingBottom: 0,
@@ -0,0 +1,32 @@
1
+ import { fireEvent, render } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import { ChatGroupBadge } from '../chat_group_badge'
4
+
5
+ const mockSetParams = jest.fn()
6
+
7
+ jest.mock('@react-navigation/native', () => ({
8
+ ...jest.requireActual('@react-navigation/native'),
9
+ useNavigation: () => ({ setParams: mockSetParams }),
10
+ useRoute: () => ({
11
+ params: { chat_group_graph_id: 'Groups/Group/1', title: 'Waco Family Group' },
12
+ }),
13
+ }))
14
+
15
+ jest.mock('../../../../hooks/use_api', () => ({
16
+ useApiGet: () => ({ data: { name: 'Waco Family Group' } }),
17
+ }))
18
+
19
+ describe('ChatGroupBadge', () => {
20
+ it('clears the group filter and its title when dismissed', () => {
21
+ const view = render(<ChatGroupBadge rowPaddingHorizontal={16} />)
22
+
23
+ fireEvent.press(view.getByText('Waco Family Group'))
24
+
25
+ expect(mockSetParams).toHaveBeenCalledTimes(1)
26
+ expect(mockSetParams.mock.calls[0][0]).toStrictEqual({
27
+ title: undefined,
28
+ chat_group_graph_id: undefined,
29
+ group_source_app_name: undefined,
30
+ })
31
+ })
32
+ })
@@ -0,0 +1,66 @@
1
+ import { fireEvent, render } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import { ListHeaderComponent } from '../list_header_component'
4
+
5
+ const mockSetParams = jest.fn()
6
+
7
+ jest.mock('@react-navigation/native', () => ({
8
+ ...jest.requireActual('@react-navigation/native'),
9
+ useNavigation: () => ({ setParams: mockSetParams, navigate: jest.fn() }),
10
+ useRoute: () => ({
11
+ params: { chat_group_graph_id: 'Groups/Group/1', title: 'Waco Family Group' },
12
+ }),
13
+ }))
14
+
15
+ jest.mock('../../../../hooks/use_groups', () => ({
16
+ useCanDisplayGroups: () => true,
17
+ }))
18
+ jest.mock('../../../../hooks/use_direct_messages_eligible', () => ({
19
+ useDirectMessagesEligible: () => true,
20
+ }))
21
+ jest.mock('../../../../hooks/use_has_direct_messages', () => ({
22
+ useHasDirectMessages: () => true,
23
+ }))
24
+ jest.mock('../../../../hooks/use_chat_permissions', () => ({
25
+ useCanCreateConversations: () => false,
26
+ }))
27
+ jest.mock('../../../../hooks/use_new_conversation_entry', () => ({
28
+ useNewConversationEntry: () => 'select_type',
29
+ }))
30
+ jest.mock('../../../../hooks/use_current_person', () => ({
31
+ useCurrentPersonCache: () => ({ update: jest.fn() }),
32
+ }))
33
+ jest.mock('../../../../hooks/use_conversations_actions', () => ({
34
+ useMarkAllRead: () => ({ markAllRead: jest.fn(), isPending: false }),
35
+ }))
36
+ jest.mock('../../../../hooks/use_new_conversation_from_filter', () => ({
37
+ useNewConversationFromFilter: () => undefined,
38
+ }))
39
+ jest.mock('../../../../hooks/use_api', () => ({
40
+ useApiGet: () => ({ data: undefined }),
41
+ }))
42
+
43
+ const clearedFilter = {
44
+ title: undefined,
45
+ chat_group_graph_id: undefined,
46
+ group_source_app_name: undefined,
47
+ show_direct_messages: undefined,
48
+ }
49
+
50
+ describe('ListHeaderComponent filter toggles', () => {
51
+ beforeEach(() => mockSetParams.mockClear())
52
+
53
+ it.each([
54
+ ['Show all conversations', clearedFilter],
55
+ ['Filter to group conversations', { ...clearedFilter, group_source_app_name: 'Groups' }],
56
+ ['Filter to team conversations', { ...clearedFilter, group_source_app_name: 'Services' }],
57
+ ['Filter to direct message conversations', { ...clearedFilter, show_direct_messages: true }],
58
+ ])('%s drops the group title along with the group filter', (label, expected) => {
59
+ const view = render(<ListHeaderComponent />)
60
+
61
+ fireEvent.press(view.getByLabelText(label))
62
+
63
+ expect(mockSetParams).toHaveBeenCalledTimes(1)
64
+ expect(mockSetParams.mock.calls[0][0]).toStrictEqual(expected)
65
+ })
66
+ })
@@ -18,6 +18,7 @@ export const ChatGroupBadge = ({ rowPaddingHorizontal }: { rowPaddingHorizontal:
18
18
 
19
19
  const handleBadgePress = useCallback(() => {
20
20
  navigation.setParams({
21
+ title: undefined,
21
22
  chat_group_graph_id: undefined,
22
23
  group_source_app_name: undefined,
23
24
  })
@@ -17,6 +17,7 @@ import { useNewConversationFromFilter } from '../../../hooks/use_new_conversatio
17
17
  import { GraphId } from '../../../types/resources/group_resource'
18
18
  import { MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../../utils'
19
19
  import { Haptic } from '../../../utils/native_adapters'
20
+ import { ConversationFiltersParams } from '../../conversation_filters/screen_props'
20
21
  import { ConversationsScreenProps } from '../conversations_screen'
21
22
  import { ChatGroupBadge } from './chat_group_badge'
22
23
  import { ListHeaderActionButton } from './list_header_action_button'
@@ -96,6 +97,19 @@ export const ListHeaderComponent = () => {
96
97
  }
97
98
  }, [navigation, route.params, entryMode, newConversationFromFilter])
98
99
 
100
+ const handleSetFilter = useCallback(
101
+ (params: ConversationFiltersParams) => {
102
+ navigation.setParams({
103
+ title: undefined,
104
+ chat_group_graph_id: undefined,
105
+ group_source_app_name: undefined,
106
+ show_direct_messages: undefined,
107
+ ...params,
108
+ })
109
+ },
110
+ [navigation]
111
+ )
112
+
99
113
  const handleChatSettings = useCallback(() => {
100
114
  navigation.navigate('ChatSettings', {})
101
115
  }, [navigation])
@@ -187,13 +201,7 @@ export const ListHeaderComponent = () => {
187
201
  <ToggleButton
188
202
  title={FilterTypes.All}
189
203
  active={active === FilterTypes.All}
190
- onPress={() =>
191
- navigation.setParams({
192
- chat_group_graph_id: undefined,
193
- group_source_app_name: undefined,
194
- show_direct_messages: undefined,
195
- })
196
- }
204
+ onPress={() => handleSetFilter({})}
197
205
  accessibilityLabel="Show all conversations"
198
206
  style={styles.filterButton}
199
207
  />
@@ -201,13 +209,7 @@ export const ListHeaderComponent = () => {
201
209
  <ToggleButton
202
210
  title={FilterTypes.Groups}
203
211
  active={active === FilterTypes.Groups}
204
- onPress={() =>
205
- navigation.setParams({
206
- chat_group_graph_id: undefined,
207
- group_source_app_name: 'Groups',
208
- show_direct_messages: undefined,
209
- })
210
- }
212
+ onPress={() => handleSetFilter({ group_source_app_name: 'Groups' })}
211
213
  accessibilityLabel="Filter to group conversations"
212
214
  style={styles.filterButton}
213
215
  />
@@ -216,13 +218,7 @@ export const ListHeaderComponent = () => {
216
218
  <ToggleButton
217
219
  title={FilterTypes.Teams}
218
220
  active={active === FilterTypes.Teams}
219
- onPress={() =>
220
- navigation.setParams({
221
- chat_group_graph_id: undefined,
222
- group_source_app_name: 'Services',
223
- show_direct_messages: undefined,
224
- })
225
- }
221
+ onPress={() => handleSetFilter({ group_source_app_name: 'Services' })}
226
222
  accessibilityLabel="Filter to team conversations"
227
223
  style={styles.filterButton}
228
224
  />
@@ -231,13 +227,7 @@ export const ListHeaderComponent = () => {
231
227
  <ToggleButton
232
228
  title={FilterTypes.DMs}
233
229
  active={active === FilterTypes.DMs}
234
- onPress={() =>
235
- navigation.setParams({
236
- chat_group_graph_id: undefined,
237
- group_source_app_name: undefined,
238
- show_direct_messages: true,
239
- })
240
- }
230
+ onPress={() => handleSetFilter({ show_direct_messages: true })}
241
231
  accessibilityLabel="Filter to direct message conversations"
242
232
  style={styles.filterButton}
243
233
  />