@planningcenter/chat-react-native 4.4.1-qa-staging.39 → 4.4.1-qa-1036.2

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 (87) hide show
  1. package/build/components/conversation/message_list.d.ts.map +1 -1
  2. package/build/components/conversation/message_list.js +123 -155
  3. package/build/components/conversation/message_list.js.map +1 -1
  4. package/build/components/conversations/conversations.d.ts.map +1 -1
  5. package/build/components/conversations/conversations.js +0 -1
  6. package/build/components/conversations/conversations.js.map +1 -1
  7. package/build/components/display/keyboard_scroll_view.d.ts.map +1 -1
  8. package/build/components/display/keyboard_scroll_view.js +0 -1
  9. package/build/components/display/keyboard_scroll_view.js.map +1 -1
  10. package/build/components/page/page_error_boundary.d.ts +1 -9
  11. package/build/components/page/page_error_boundary.d.ts.map +1 -1
  12. package/build/components/page/page_error_boundary.js +31 -56
  13. package/build/components/page/page_error_boundary.js.map +1 -1
  14. package/build/navigation/index.d.ts +2 -0
  15. package/build/navigation/index.d.ts.map +1 -1
  16. package/build/navigation/index.js +6 -11
  17. package/build/navigation/index.js.map +1 -1
  18. package/build/navigation/screenLayout.d.ts.map +1 -1
  19. package/build/navigation/screenLayout.js +44 -13
  20. package/build/navigation/screenLayout.js.map +1 -1
  21. package/build/screens/age_check/age_check_required_screen.d.ts.map +1 -1
  22. package/build/screens/age_check/age_check_required_screen.js +0 -1
  23. package/build/screens/age_check/age_check_required_screen.js.map +1 -1
  24. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  25. package/build/screens/conversation_details_screen.js +0 -1
  26. package/build/screens/conversation_details_screen.js.map +1 -1
  27. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts.map +1 -1
  28. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js +0 -1
  29. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js.map +1 -1
  30. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.d.ts.map +1 -1
  31. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js +291 -332
  32. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js.map +1 -1
  33. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts.map +1 -1
  34. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js +0 -1
  35. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js.map +1 -1
  36. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.d.ts.map +1 -1
  37. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js +0 -1
  38. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js.map +1 -1
  39. package/build/screens/get_help_screen.js +62 -56
  40. package/build/screens/get_help_screen.js.map +1 -1
  41. package/build/screens/group_notification_settings_screen.js +2 -3
  42. package/build/screens/group_notification_settings_screen.js.map +1 -1
  43. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  44. package/build/screens/notification_settings_screen.js +0 -1
  45. package/build/screens/notification_settings_screen.js.map +1 -1
  46. package/build/screens/preferred_app_selection_screen.js +39 -45
  47. package/build/screens/preferred_app_selection_screen.js.map +1 -1
  48. package/build/screens/search/components/search_results_list.d.ts +1 -4
  49. package/build/screens/search/components/search_results_list.d.ts.map +1 -1
  50. package/build/screens/search/components/search_results_list.js +110 -136
  51. package/build/screens/search/components/search_results_list.js.map +1 -1
  52. package/build/screens/search/search_screen.d.ts.map +1 -1
  53. package/build/screens/search/search_screen.js +28 -21
  54. package/build/screens/search/search_screen.js.map +1 -1
  55. package/build/screens/settings_screen.d.ts.map +1 -1
  56. package/build/screens/settings_screen.js +6 -7
  57. package/build/screens/settings_screen.js.map +1 -1
  58. package/package.json +3 -3
  59. package/src/components/conversation/__tests__/message_list.test.tsx +0 -4
  60. package/src/components/conversation/message_list.tsx +0 -6
  61. package/src/components/conversations/conversations.tsx +0 -1
  62. package/src/components/display/keyboard_scroll_view.tsx +0 -1
  63. package/src/components/page/__tests__/page_error_boundary.test.tsx +0 -33
  64. package/src/components/page/page_error_boundary.tsx +12 -43
  65. package/src/navigation/index.tsx +3 -11
  66. package/src/navigation/screenLayout.tsx +19 -4
  67. package/src/screens/age_check/age_check_required_screen.tsx +1 -5
  68. package/src/screens/conversation_details_screen.tsx +0 -1
  69. package/src/screens/conversation_select_recipients/conversation_new_entry_screen.tsx +1 -4
  70. package/src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx +68 -78
  71. package/src/screens/conversation_select_recipients/conversation_select_group_recipients_screen.tsx +0 -1
  72. package/src/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.tsx +0 -1
  73. package/src/screens/get_help_screen.tsx +4 -4
  74. package/src/screens/group_notification_settings_screen.tsx +3 -3
  75. package/src/screens/notification_settings_screen.tsx +0 -1
  76. package/src/screens/preferred_app_selection_screen.tsx +4 -4
  77. package/src/screens/search/__tests__/search_screen.test.tsx +7 -5
  78. package/src/screens/search/components/__tests__/search_results_list.test.tsx +2 -6
  79. package/src/screens/search/components/search_results_list.tsx +13 -34
  80. package/src/screens/search/search_screen.tsx +18 -8
  81. package/src/screens/settings_screen.tsx +25 -25
  82. package/build/navigation/page_error_fallbacks.d.ts +0 -7
  83. package/build/navigation/page_error_fallbacks.d.ts.map +0 -1
  84. package/build/navigation/page_error_fallbacks.js +0 -7
  85. package/build/navigation/page_error_fallbacks.js.map +0 -1
  86. package/src/navigation/page_error_fallbacks.ts +0 -14
  87. package/src/screens/conversation_select_recipients/__tests__/conversation_select_direct_message_recipients_screen.test.tsx +0 -100
@@ -1,10 +1,11 @@
1
1
  import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StackActions, useNavigation } from '@react-navigation/native'
3
- import { ReactElement, RefObject, useRef, useState } from 'react'
3
+ import { RefObject, useRef, useState } from 'react'
4
4
  import { FlatList, Pressable, StyleSheet, TextInput, View } from 'react-native'
5
5
  import { Avatar, Badge, Banner, Icon, KeyboardView, Spinner, Text } from '../../components'
6
6
  import { ActionButton } from '../../components/display/action_button'
7
7
  import { AutoFocusAwareTextInput } from '../../components/display/text_input'
8
+ import { ComponentErrorBoundary } from '../../components/page/component_error_boundary'
8
9
  import { useTheme } from '../../hooks'
9
10
  import { useDirectMessageAffiliations } from '../../hooks/direct_messages/use_direct_message_affiliations'
10
11
  import { useDirectMessageCandidates } from '../../hooks/direct_messages/use_direct_message_candidates'
@@ -91,31 +92,38 @@ export function ConversationSelectDirectMessageRecipientsScreen({
91
92
 
92
93
  return (
93
94
  <KeyboardView>
95
+ {isError && (
96
+ <Banner
97
+ appearance="error"
98
+ description="There was an issue starting the conversation. Please try again."
99
+ style={styles.errorBanner}
100
+ />
101
+ )}
102
+ <ToField
103
+ selectedPeople={selectedPeople}
104
+ searchTerm={searchTerm}
105
+ onSearchChange={setSearchTerm}
106
+ onRemove={removePerson}
107
+ onClear={() => setSelectedPeople(new Map())}
108
+ searchInputRef={searchInputRef}
109
+ />
94
110
  <View style={styles.candidateResultsContainer}>
95
- <CandidateResults
96
- term={debouncedTerm}
97
- selectedPeople={selectedPeople}
98
- onSelect={selectPerson}
99
- header={
100
- <>
101
- {isError && (
102
- <Banner
103
- appearance="error"
104
- description="There was an issue starting the conversation. Please try again."
105
- style={styles.errorBanner}
106
- />
107
- )}
108
- <ToField
109
- selectedPeople={selectedPeople}
110
- searchTerm={searchTerm}
111
- onSearchChange={setSearchTerm}
112
- onRemove={removePerson}
113
- onClear={() => setSelectedPeople(new Map())}
114
- searchInputRef={searchInputRef}
115
- />
116
- </>
111
+ <ComponentErrorBoundary
112
+ key={debouncedTerm}
113
+ fallback={
114
+ <Banner
115
+ appearance="error"
116
+ description="Couldn't load people right now. Try again."
117
+ style={styles.errorBanner}
118
+ />
117
119
  }
118
- />
120
+ >
121
+ <CandidateResults
122
+ term={debouncedTerm}
123
+ selectedPeople={selectedPeople}
124
+ onSelect={selectPerson}
125
+ />
126
+ </ComponentErrorBoundary>
119
127
  </View>
120
128
  <ActionButton
121
129
  title={buttonText}
@@ -206,57 +214,53 @@ function CandidateResults({
206
214
  term,
207
215
  selectedPeople,
208
216
  onSelect,
209
- header,
210
217
  }: {
211
218
  term: string
212
219
  selectedPeople: Map<string, DirectMessageCandidateResource>
213
220
  onSelect: (candidate: DirectMessageCandidateResource) => void
214
- header: ReactElement
215
221
  }) {
216
222
  const styles = useStyles()
217
223
  const { data, isFetching } = useDirectMessageCandidates(term)
218
224
  const { groups, teams, resultsDescription } = useDirectMessageAffiliations()
219
225
 
220
- const unselectedCandidates = (data?.data ?? []).filter(c => !selectedPeople.has(c.id))
221
- const hasTruncated = (data?.meta.totalCount ?? 0) > (data?.data.length ?? 0)
222
- const isAtLimit = selectedPeople.size >= MAX_RECIPIENTS
223
- const showsResults = term.length >= 1 && !isAtLimit && unselectedCandidates.length > 0
226
+ if (term.length < 1) {
227
+ if (selectedPeople.size > 0) return null
224
228
 
225
- const emptyState = (() => {
226
- if (showsResults) return null
229
+ return (
230
+ <View style={styles.emptyState}>
231
+ <Icon name="churchCenter.people" size={40} style={styles.emptyStateIcon} />
232
+ <Text style={styles.emptyStateTitle}>Search for people to direct message</Text>
233
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
234
+ Start a conversation with people in your groups and teams.
235
+ </Text>
236
+ </View>
237
+ )
238
+ }
227
239
 
228
- if (term.length < 1) {
229
- if (selectedPeople.size > 0) return null
240
+ const isAtLimit = selectedPeople.size >= MAX_RECIPIENTS
230
241
 
231
- return (
232
- <View style={styles.emptyState}>
233
- <Icon name="churchCenter.people" size={40} style={styles.emptyStateIcon} />
234
- <Text style={styles.emptyStateTitle}>Search for people to direct message</Text>
235
- <Text variant="secondary" style={styles.emptyStateSubtitle}>
236
- Start a conversation with people in your groups and teams.
237
- </Text>
238
- </View>
239
- )
240
- }
242
+ if (isAtLimit) {
243
+ return (
244
+ <View style={styles.emptyState}>
245
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
246
+ You can add up to {MAX_RECIPIENTS} people to a direct message.
247
+ </Text>
248
+ </View>
249
+ )
250
+ }
241
251
 
242
- if (isAtLimit) {
243
- return (
244
- <View style={styles.emptyState}>
245
- <Text variant="secondary" style={styles.emptyStateSubtitle}>
246
- You can add up to {MAX_RECIPIENTS} people to a direct message.
247
- </Text>
248
- </View>
249
- )
250
- }
252
+ const unselectedCandidates = (data?.data ?? []).filter(c => !selectedPeople.has(c.id))
253
+ const hasTruncated = (data?.meta.totalCount ?? 0) > (data?.data.length ?? 0)
251
254
 
252
- if (isFetching) {
253
- return (
254
- <View style={styles.emptyState}>
255
- <Spinner />
256
- </View>
257
- )
258
- }
255
+ if (isFetching && unselectedCandidates.length === 0) {
256
+ return (
257
+ <View style={styles.emptyState}>
258
+ <Spinner />
259
+ </View>
260
+ )
261
+ }
259
262
 
263
+ if (!isFetching && unselectedCandidates.length === 0) {
260
264
  return (
261
265
  <View style={styles.emptyState}>
262
266
  <Icon name="general.search" size={32} style={styles.emptyStateIcon} />
@@ -266,15 +270,13 @@ function CandidateResults({
266
270
  </Text>
267
271
  </View>
268
272
  )
269
- })()
273
+ }
270
274
 
271
275
  return (
272
276
  <CandidateList
273
- header={header}
274
- candidates={showsResults ? unselectedCandidates : []}
277
+ candidates={unselectedCandidates}
275
278
  hasTruncated={hasTruncated}
276
279
  description={resultsDescription}
277
- emptyState={emptyState}
278
280
  groups={groups}
279
281
  teams={teams}
280
282
  onSelect={onSelect}
@@ -307,33 +309,27 @@ function candidateAffiliationNames(
307
309
  }
308
310
 
309
311
  function CandidateList({
310
- header,
311
312
  candidates,
312
313
  hasTruncated,
313
314
  description,
314
- emptyState,
315
315
  groups,
316
316
  teams,
317
317
  onSelect,
318
318
  }: {
319
- header: ReactElement
320
319
  candidates: DirectMessageCandidateResource[]
321
320
  hasTruncated: boolean
322
321
  description: string | null
323
- emptyState: ReactElement | null
324
322
  groups: Map<number, string>
325
323
  teams: Map<number, string>
326
324
  onSelect: (candidate: DirectMessageCandidateResource) => void
327
325
  }) {
328
326
  const styles = useStyles()
329
- const hasCandidates = candidates.length > 0
330
327
  const available = candidates.filter(c => !c.directMessagesDisabled)
331
328
  const dmDisabled = candidates.filter(c => c.directMessagesDisabled)
332
329
 
333
330
  return (
334
331
  <FlatList<DirectMessageCandidateResource>
335
332
  data={available}
336
- contentInsetAdjustmentBehavior="automatic"
337
333
  keyboardShouldPersistTaps="handled"
338
334
  keyExtractor={item => item.id}
339
335
  renderItem={({ item }) => (
@@ -344,13 +340,7 @@ function CandidateList({
344
340
  onSelect={() => onSelect(item)}
345
341
  />
346
342
  )}
347
- ListHeaderComponent={
348
- <>
349
- {header}
350
- {hasCandidates && <ResultsHeading description={description} />}
351
- </>
352
- }
353
- ListEmptyComponent={emptyState}
343
+ ListHeaderComponent={<ResultsHeading description={description} />}
354
344
  ListFooterComponent={
355
345
  <>
356
346
  {dmDisabled.length > 0 && (
@@ -371,7 +361,7 @@ function CandidateList({
371
361
  ))}
372
362
  </>
373
363
  )}
374
- {hasCandidates && hasTruncated && (
364
+ {hasTruncated && (
375
365
  <View style={styles.truncatedFooter}>
376
366
  <Text variant="secondary" style={styles.truncatedText}>
377
367
  Keep typing to narrow your results.
@@ -60,7 +60,6 @@ export const ConversationSelectGroupRecipientsScreen = ({
60
60
 
61
61
  return (
62
62
  <SectionList<GroupsGroupResource, GroupsSection>
63
- contentInsetAdjustmentBehavior="automatic"
64
63
  sections={sections}
65
64
  keyExtractor={item => item.id.toString()}
66
65
  stickySectionHeadersEnabled={false}
@@ -32,7 +32,6 @@ export const ConversationSelectTeamsILeadRecipientsScreen = ({
32
32
 
33
33
  return (
34
34
  <FlatList
35
- contentInsetAdjustmentBehavior="automatic"
36
35
  data={serviceTypes}
37
36
  keyExtractor={item => item.id.toString()}
38
37
  contentContainerStyle={styles.contentContainer}
@@ -1,6 +1,6 @@
1
1
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
2
2
  import { useCallback, useMemo } from 'react'
3
- import { Alert, Linking, ScrollView, StyleSheet, View } from 'react-native'
3
+ import { Alert, Linking, StyleSheet, View } from 'react-native'
4
4
  import { Heading, PressableRow, Text, TextInlineButton } from '../components'
5
5
  import { useApiGet, useTheme } from '../hooks'
6
6
  import { useAppName } from '../hooks/use_app_name'
@@ -56,7 +56,7 @@ export const GetHelpScreen = ({ route }: GetHelpScreenProps) => {
56
56
  }, [navigation])
57
57
 
58
58
  return (
59
- <ScrollView contentInsetAdjustmentBehavior="automatic" contentContainerStyle={styles.container}>
59
+ <View style={[styles.container]}>
60
60
  {appSpecificHelpUrls.length > 0 && (
61
61
  <Heading variant="h2" style={[styles.heading, styles.headingBottomBorder]}>
62
62
  How-to articles
@@ -90,7 +90,7 @@ export const GetHelpScreen = ({ route }: GetHelpScreenProps) => {
90
90
  to the development team.
91
91
  </Text>
92
92
  </View>
93
- </ScrollView>
93
+ </View>
94
94
  )
95
95
  }
96
96
 
@@ -145,7 +145,7 @@ const useStyles = () => {
145
145
 
146
146
  return StyleSheet.create({
147
147
  container: {
148
- flexGrow: 1,
148
+ flex: 1,
149
149
  },
150
150
  text: {
151
151
  paddingVertical: 4,
@@ -2,7 +2,7 @@ import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
3
3
  import type { NativeStackNavigationProp } from '@react-navigation/native-stack'
4
4
  import React, { useEffect } from 'react'
5
- import { Platform, ScrollView, StyleSheet, View } from 'react-native'
5
+ import { Platform, StyleSheet, View } from 'react-native'
6
6
  import { Heading, Icon, Text } from '../components'
7
7
  import { useTheme } from '../hooks'
8
8
  import { platformFontWeightBold } from '../utils/styles'
@@ -38,7 +38,7 @@ export function GroupNotificationSettingsScreen({ route }: GroupNotificationSett
38
38
  }
39
39
 
40
40
  return (
41
- <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.container}>
41
+ <View style={styles.container}>
42
42
  <View style={styles.sectionOuter}>
43
43
  <View style={styles.sectionInner}>
44
44
  <Heading variant="h2" style={styles.sectionHeading}>
@@ -76,7 +76,7 @@ export function GroupNotificationSettingsScreen({ route }: GroupNotificationSett
76
76
  )}
77
77
  </View>
78
78
  </PlatformPressable>
79
- </ScrollView>
79
+ </View>
80
80
  )
81
81
  }
82
82
 
@@ -123,7 +123,6 @@ export function NotificationSettingsScreen({}: NotificationSettingsScreenProps)
123
123
  return (
124
124
  <View style={styles.listContainer}>
125
125
  <FlatList
126
- contentInsetAdjustmentBehavior="automatic"
127
126
  data={listData as SectionListData}
128
127
  contentContainerStyle={styles.contentContainer}
129
128
  renderItem={({ item, index }) => {
@@ -2,7 +2,7 @@ import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps } from '@react-navigation/native'
3
3
  import { useMutation } from '@tanstack/react-query'
4
4
  import React, { type PropsWithChildren } from 'react'
5
- import { Image, ScrollView, StyleSheet, View, type ViewStyle } from 'react-native'
5
+ import { Image, StyleSheet, View, type ViewStyle } from 'react-native'
6
6
  import { Heading, Icon, Text } from '../components'
7
7
  import { useApiClient, useTheme } from '../hooks'
8
8
  import { useAppName } from '../hooks/use_app_name'
@@ -45,7 +45,7 @@ export function PreferredAppSelectionScreen({ route }: PreferredAppSelectionScre
45
45
  const sectionTitle = chatType ? chatTypeLabel(chatType.title) : undefined
46
46
 
47
47
  return (
48
- <ScrollView contentInsetAdjustmentBehavior="automatic" contentContainerStyle={styles.container}>
48
+ <View style={styles.container}>
49
49
  <View style={styles.section}>
50
50
  <Heading variant="h2" style={styles.sectionHeading}>
51
51
  {sectionTitle}
@@ -64,7 +64,7 @@ export function PreferredAppSelectionScreen({ route }: PreferredAppSelectionScre
64
64
  </PressableRow>
65
65
  ))}
66
66
  </View>
67
- </ScrollView>
67
+ </View>
68
68
  )
69
69
  }
70
70
 
@@ -118,7 +118,7 @@ const PressableRow = ({
118
118
 
119
119
  const styles = StyleSheet.create({
120
120
  container: {
121
- flexGrow: 1,
121
+ flex: 1,
122
122
  },
123
123
  section: {
124
124
  paddingTop: 24,
@@ -106,13 +106,12 @@ describe('SearchScreen', () => {
106
106
  fireEvent.changeText(input, ' budget ')
107
107
 
108
108
  expect(view.getByText('Search chat')).toBeTruthy()
109
- const searchedTerms = () =>
110
- new Set(mockUseSearchResults.mock.calls.map(([term]) => term).filter(term => term.trim()))
111
- expect([...searchedTerms()]).toEqual([])
109
+ expect(mockUseSearchResults).not.toHaveBeenCalled()
112
110
 
113
111
  act(() => jest.advanceTimersByTime(200))
114
112
 
115
- expect([...searchedTerms()]).toEqual(['budget'])
113
+ const searchedTerms = new Set(mockUseSearchResults.mock.calls.map(([term]) => term))
114
+ expect([...searchedTerms]).toEqual(['budget'])
116
115
  expect(view.queryByText('Search chat')).toBeNull()
117
116
  expect(view.getByLabelText('Loading results')).toBeTruthy()
118
117
  })
@@ -122,7 +121,10 @@ describe('SearchScreen', () => {
122
121
 
123
122
  const scrollView = view.UNSAFE_getByType(ScrollView)
124
123
  expect(scrollView.props.keyboardShouldPersistTaps).toBe('handled')
125
- expect(scrollView.props.contentContainerStyle).toMatchObject({ flexGrow: 1 })
124
+ expect(scrollView.props.contentContainerStyle).toMatchObject({
125
+ flexGrow: 1,
126
+ justifyContent: 'center',
127
+ })
126
128
  expect(view.getByText('Search chat')).toBeTruthy()
127
129
  })
128
130
 
@@ -49,13 +49,9 @@ const messageResult = (id: string): MessageSearchResultResource => ({
49
49
  },
50
50
  })
51
51
 
52
- const slots = { header: <></>, prompt: <></> }
53
-
54
52
  const renderList = (state: Partial<SearchState>, onSettled = jest.fn()) => {
55
53
  mockUseSearchResults.mockReturnValue(searchState(state))
56
- const list = () => (
57
- <SearchResultsList term="potluck" onSelect={jest.fn()} onSettled={onSettled} {...slots} />
58
- )
54
+ const list = () => <SearchResultsList term="potluck" onSelect={jest.fn()} onSettled={onSettled} />
59
55
  const view = render(list())
60
56
  const rerenderWith = (next: Partial<SearchState>) => {
61
57
  mockUseSearchResults.mockReturnValue(searchState(next))
@@ -134,7 +130,7 @@ describe('SearchResultsList', () => {
134
130
  const results = [conversationResult('1'), messageResult('1'), messageResult('2')]
135
131
  mockUseSearchResults.mockReturnValue(searchState({ results }))
136
132
  const view = render(
137
- <SearchResultsList term="potluck" onSelect={onSelect} onSettled={jest.fn()} {...slots} />
133
+ <SearchResultsList term="potluck" onSelect={onSelect} onSettled={jest.fn()} />
138
134
  )
139
135
 
140
136
  fireEvent.press(view.getByLabelText(/^Message 2/))
@@ -1,4 +1,4 @@
1
- import React, { ReactElement, useEffect, useRef } from 'react'
1
+ import React, { useEffect, useRef } from 'react'
2
2
  import { FlatList, StyleSheet, View } from 'react-native'
3
3
  import { Banner, Spinner, Text, TextButton } from '../../../components'
4
4
  import { useSearchResults } from '../../../hooks/use_search_results'
@@ -23,17 +23,9 @@ interface SearchResultsListProps {
23
23
  term: string
24
24
  onSelect: (result: SearchResultResource, position: number) => void
25
25
  onSettled: (term: string, counts: SearchResultCounts) => void
26
- header: ReactElement
27
- prompt: ReactElement
28
26
  }
29
27
 
30
- export function SearchResultsList({
31
- term,
32
- onSelect,
33
- onSettled,
34
- header,
35
- prompt,
36
- }: SearchResultsListProps) {
28
+ export function SearchResultsList({ term, onSelect, onSettled }: SearchResultsListProps) {
37
29
  const {
38
30
  results,
39
31
  failedTypes,
@@ -44,10 +36,9 @@ export function SearchResultsList({
44
36
  hasNextPage,
45
37
  isFetchingNextPage,
46
38
  } = useSearchResults(term)
47
- const searching = term.trim().length > 0
48
- const conversationResults = searching ? results.filter(isConversationResult) : []
49
- const messageResults = searching ? results.filter(isMessageResult) : []
50
- const settled = searching && !isPending && !isError
39
+ const conversationResults = results.filter(isConversationResult)
40
+ const messageResults = results.filter(isMessageResult)
41
+ const settled = !isPending && !isError
51
42
 
52
43
  const settledTermRef = useRef<string | undefined>(undefined)
53
44
 
@@ -65,7 +56,6 @@ export function SearchResultsList({
65
56
  <FlatList
66
57
  data={messageResults}
67
58
  keyExtractor={item => item.id}
68
- contentInsetAdjustmentBehavior="automatic"
69
59
  keyboardShouldPersistTaps="handled"
70
60
  keyboardDismissMode="on-drag"
71
61
  contentContainerStyle={styles.content}
@@ -74,27 +64,20 @@ export function SearchResultsList({
74
64
  )}
75
65
  ListHeaderComponent={
76
66
  <>
77
- {header}
78
67
  {conversationResults.length > 0 && (
79
68
  <ConversationResults results={conversationResults} onSelect={onSelect} />
80
69
  )}
81
- {searching && failedTypes.includes('conversation') && (
82
- <SectionFailure type="conversation" />
83
- )}
70
+ {failedTypes.includes('conversation') && <SectionFailure type="conversation" />}
84
71
  {messageResults.length > 0 && <ResultsSection title="Messages" />}
85
72
  </>
86
73
  }
87
74
  ListEmptyComponent={
88
- searching ? (
89
- <ListEmpty
90
- term={term}
91
- isError={isError}
92
- isPending={isPending}
93
- noResults={results.length === 0 && failedTypes.length === 0}
94
- />
95
- ) : (
96
- <View style={styles.prompt}>{prompt}</View>
97
- )
75
+ <ListEmpty
76
+ term={term}
77
+ isError={isError}
78
+ isPending={isPending}
79
+ noResults={results.length === 0 && failedTypes.length === 0}
80
+ />
98
81
  }
99
82
  ListFooterComponent={
100
83
  <>
@@ -108,7 +91,7 @@ export function SearchResultsList({
108
91
  Try again
109
92
  </TextButton>
110
93
  )}
111
- {searching && failedTypes.includes('message') && <SectionFailure type="message" />}
94
+ {failedTypes.includes('message') && <SectionFailure type="message" />}
112
95
  </>
113
96
  }
114
97
  onEndReached={() => {
@@ -166,10 +149,6 @@ const styles = StyleSheet.create({
166
149
  content: {
167
150
  flexGrow: 1,
168
151
  },
169
- prompt: {
170
- flexGrow: 1,
171
- justifyContent: 'center',
172
- },
173
152
  banner: {
174
153
  margin: 16,
175
154
  },
@@ -4,7 +4,7 @@ import {
4
4
  NativeStackScreenProps,
5
5
  } from '@react-navigation/native-stack'
6
6
  import React, { useRef, useState } from 'react'
7
- import { Keyboard, StyleSheet, View } from 'react-native'
7
+ import { Keyboard, ScrollView, StyleSheet, View } from 'react-native'
8
8
  import { KeyboardView } from '../../components'
9
9
  import {
10
10
  composeOptions,
@@ -64,13 +64,19 @@ export function SearchScreen() {
64
64
  return (
65
65
  <KeyboardView>
66
66
  <View style={styles.container} testID="search-screen">
67
- <SearchResultsList
68
- term={debouncedTerm}
69
- onSelect={openResult}
70
- onSettled={publishSearch}
71
- header={<SearchField term={term} onChange={setTerm} />}
72
- prompt={<SearchPromptBlankState />}
73
- />
67
+ <SearchField term={term} onChange={setTerm} />
68
+ {debouncedTerm.length > 0 ? (
69
+ <SearchResultsList
70
+ key={debouncedTerm}
71
+ term={debouncedTerm}
72
+ onSelect={openResult}
73
+ onSettled={publishSearch}
74
+ />
75
+ ) : (
76
+ <ScrollView contentContainerStyle={styles.prompt} keyboardShouldPersistTaps="handled">
77
+ <SearchPromptBlankState />
78
+ </ScrollView>
79
+ )}
74
80
  </View>
75
81
  </KeyboardView>
76
82
  )
@@ -80,4 +86,8 @@ const styles = StyleSheet.create({
80
86
  container: {
81
87
  flex: 1,
82
88
  },
89
+ prompt: {
90
+ flexGrow: 1,
91
+ justifyContent: 'center',
92
+ },
83
93
  })
@@ -1,7 +1,7 @@
1
1
  import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
3
3
  import React, { useCallback } from 'react'
4
- import { Platform, ScrollView, StyleSheet, View } from 'react-native'
4
+ import { Platform, StyleSheet, View } from 'react-native'
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
6
6
  import { Heading, Icon, Text, VersionFooter } from '../components'
7
7
  import { Switch } from '../components/display/switch'
@@ -28,31 +28,29 @@ export function SettingsScreen({}: SettingsScreenProps) {
28
28
 
29
29
  return (
30
30
  <View style={styles.container}>
31
- <ScrollView contentInsetAdjustmentBehavior="automatic">
32
- <View style={styles.sectionOuter}>
33
- <View style={styles.sectionHeader}>
34
- <Heading variant="h2">Messages</Heading>
35
- </View>
36
- {directMessagesEligible && (
37
- <View style={styles.settingRow}>
38
- <View style={styles.settingRowText}>
39
- <Text>Allow direct messages</Text>
40
- <Text variant="tertiary">
41
- When off, you can't be added to new direct messages, and any you're already in
42
- will be turned off.
43
- </Text>
44
- </View>
45
- <Switch
46
- value={!directMessagesDisabled}
47
- onValueChange={enabled => setDirectMessagesDisabled(!enabled)}
48
- disabled={isMutating}
49
- />
50
- </View>
51
- )}
52
- <NavRow label="Notification settings" onPress={handleNotificationSettings} />
53
- {appName !== 'services' && <NavRow label="Get help" onPress={handleGetHelp} />}
31
+ <View style={styles.sectionOuter}>
32
+ <View style={styles.sectionHeader}>
33
+ <Heading variant="h2">Messages</Heading>
54
34
  </View>
55
- </ScrollView>
35
+ {directMessagesEligible && (
36
+ <View style={styles.settingRow}>
37
+ <View style={styles.settingRowText}>
38
+ <Text>Allow direct messages</Text>
39
+ <Text variant="tertiary">
40
+ When off, you can't be added to new direct messages, and any you're already in will
41
+ be turned off.
42
+ </Text>
43
+ </View>
44
+ <Switch
45
+ value={!directMessagesDisabled}
46
+ onValueChange={enabled => setDirectMessagesDisabled(!enabled)}
47
+ disabled={isMutating}
48
+ />
49
+ </View>
50
+ )}
51
+ <NavRow label="Notification settings" onPress={handleNotificationSettings} />
52
+ {appName !== 'services' && <NavRow label="Get help" onPress={handleGetHelp} />}
53
+ </View>
56
54
  <View style={styles.footer}>
57
55
  <VersionFooter />
58
56
  </View>
@@ -92,6 +90,8 @@ const useStyles = () => {
92
90
  flex: 1,
93
91
  },
94
92
  footer: {
93
+ flex: 1,
94
+ justifyContent: 'flex-end',
95
95
  paddingBottom: Math.max(insets.bottom, 8),
96
96
  },
97
97
  sectionOuter: {
@@ -1,7 +0,0 @@
1
- import type { StaticParamList } from '@react-navigation/native';
2
- import type { PageErrorFallback } from '../components/page/page_error_boundary';
3
- import type { NewConversationStack } from './index';
4
- type RouteName = keyof ReactNavigation.RootParamList | keyof StaticParamList<typeof NewConversationStack>;
5
- export declare const PAGE_ERROR_FALLBACKS: Partial<Record<RouteName, PageErrorFallback>>;
6
- export {};
7
- //# sourceMappingURL=page_error_fallbacks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"page_error_fallbacks.d.ts","sourceRoot":"","sources":["../../src/navigation/page_error_fallbacks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAC/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnD,KAAK,SAAS,GACV,MAAM,eAAe,CAAC,aAAa,GACnC,MAAM,eAAe,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEtD,eAAO,MAAM,oBAAoB,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAK9E,CAAA"}
@@ -1,7 +0,0 @@
1
- export const PAGE_ERROR_FALLBACKS = {
2
- ConversationSelectDirectMessageRecipients: {
3
- heading: "Couldn't load people right now",
4
- body: 'Try again.'
5
- }
6
- };
7
- //# sourceMappingURL=page_error_fallbacks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"page_error_fallbacks.js","names":["PAGE_ERROR_FALLBACKS","ConversationSelectDirectMessageRecipients","heading","body"],"sources":["../../src/navigation/page_error_fallbacks.ts"],"sourcesContent":["import type { StaticParamList } from '@react-navigation/native'\nimport type { PageErrorFallback } from '../components/page/page_error_boundary'\nimport type { NewConversationStack } from './index'\n\ntype RouteName =\n | keyof ReactNavigation.RootParamList\n | keyof StaticParamList<typeof NewConversationStack>\n\nexport const PAGE_ERROR_FALLBACKS: Partial<Record<RouteName, PageErrorFallback>> = {\n ConversationSelectDirectMessageRecipients: {\n heading: \"Couldn't load people right now\",\n body: 'Try again.',\n },\n}\n"],"mappings":"AAQA,OAAO,MAAMA,oBAAmE,GAAG;EACjFC,yCAAyC,EAAE;IACzCC,OAAO,EAAE,gCAAgC;IACzCC,IAAI,EAAE;EACR;AACF,CAAC","ignoreList":[]}