@planningcenter/chat-react-native 4.4.1-qa-staging.15 → 4.4.1-qa-staging.17

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 (38) hide show
  1. package/build/components/conversation/reply_shadow_message.js +120 -104
  2. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  3. package/build/components/conversations/conversations.d.ts.map +1 -1
  4. package/build/components/conversations/conversations.js +55 -39
  5. package/build/components/conversations/conversations.js.map +1 -1
  6. package/build/components/conversations/conversations_blank_state.d.ts.map +1 -1
  7. package/build/components/conversations/conversations_blank_state.js +55 -12
  8. package/build/components/conversations/conversations_blank_state.js.map +1 -1
  9. package/build/components/conversations/filtered_results_footer.d.ts +2 -0
  10. package/build/components/conversations/filtered_results_footer.d.ts.map +1 -0
  11. package/build/components/conversations/filtered_results_footer.js +77 -0
  12. package/build/components/conversations/filtered_results_footer.js.map +1 -0
  13. package/build/hooks/use_clear_conversation_filters.d.ts +2 -0
  14. package/build/hooks/use_clear_conversation_filters.d.ts.map +1 -0
  15. package/build/hooks/use_clear_conversation_filters.js +24 -0
  16. package/build/hooks/use_clear_conversation_filters.js.map +1 -0
  17. package/build/hooks/use_reduce_motion.d.ts.map +1 -1
  18. package/build/hooks/use_reduce_motion.js +2 -1
  19. package/build/hooks/use_reduce_motion.js.map +1 -1
  20. package/build/screens/conversation_filters/components/conversation_filters.d.ts.map +1 -1
  21. package/build/screens/conversation_filters/components/conversation_filters.js +8 -5
  22. package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -1
  23. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  24. package/build/screens/conversation_filters_screen.js +47 -42
  25. package/build/screens/conversation_filters_screen.js.map +1 -1
  26. package/package.json +3 -3
  27. package/src/__tests__/screens/conversation_filters/components/conversation_filters.test.tsx +129 -0
  28. package/src/__tests__/screens/conversation_new/components/services_form.test.tsx +1 -0
  29. package/src/components/conversation/reply_shadow_message.tsx +1 -2
  30. package/src/components/conversations/__tests__/conversations_blank_state.test.tsx +72 -0
  31. package/src/components/conversations/conversations.tsx +13 -2
  32. package/src/components/conversations/conversations_blank_state.tsx +21 -0
  33. package/src/components/conversations/filtered_results_footer.tsx +38 -0
  34. package/src/hooks/__tests__/use_reduce_motion.test.tsx +13 -1
  35. package/src/hooks/use_clear_conversation_filters.ts +15 -0
  36. package/src/hooks/use_reduce_motion.ts +2 -1
  37. package/src/screens/conversation_filters/components/conversation_filters.tsx +8 -5
  38. package/src/screens/conversation_filters_screen.tsx +5 -1
@@ -7,6 +7,7 @@ import React, { useCallback } from 'react';
7
7
  import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
8
8
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
9
  import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet';
10
+ import { useFilterDropdownEnabled } from '../hooks/use_filter_dropdown_enabled';
10
11
  import { ConversationFilters } from './conversation_filters/components/conversation_filters';
11
12
  import { FilterProvider, useFilterContext } from './conversation_filters/context/conversation_filter_context';
12
13
  import { GroupFilters } from './conversation_filters/group_filters';
@@ -100,13 +101,14 @@ export const ConversationFiltersScreen = _props => {
100
101
  return t2;
101
102
  };
102
103
  const FormSheetHeaderWithContext = () => {
103
- const $ = _c(17);
104
+ const $ = _c(19);
104
105
  const {
105
106
  resetFilter,
106
107
  applyFilters,
107
108
  setFormSheetHeaderHeight
108
109
  } = useFilterContext();
109
110
  const route = useRoute();
111
+ const filterDropdownEnabled = useFilterDropdownEnabled();
110
112
  const styles = useStyles();
111
113
  let t0;
112
114
  if ($[0] !== setFormSheetHeaderHeight) {
@@ -119,74 +121,77 @@ const FormSheetHeaderWithContext = () => {
119
121
  t0 = $[1];
120
122
  }
121
123
  const handleFormSheetHeaderLayout = t0;
122
- let t1;
123
- if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
124
- t1 = /*#__PURE__*/_jsx(FormSheet.HeaderTitle, {
125
- children: "Filters"
124
+ const t1 = filterDropdownEnabled ? "More filters" : "Filters";
125
+ let t2;
126
+ if ($[2] !== t1) {
127
+ t2 = /*#__PURE__*/_jsx(FormSheet.HeaderTitle, {
128
+ children: t1
126
129
  });
127
130
  $[2] = t1;
131
+ $[3] = t2;
128
132
  } else {
129
- t1 = $[2];
133
+ t2 = $[3];
130
134
  }
131
- let t2;
132
- if ($[3] !== resetFilter) {
133
- t2 = /*#__PURE__*/_jsx(FormSheet.HeaderTextButton, {
135
+ let t3;
136
+ if ($[4] !== resetFilter) {
137
+ t3 = /*#__PURE__*/_jsx(FormSheet.HeaderTextButton, {
134
138
  onPress: resetFilter,
135
139
  children: "Reset"
136
140
  });
137
- $[3] = resetFilter;
138
- $[4] = t2;
141
+ $[4] = resetFilter;
142
+ $[5] = t3;
139
143
  } else {
140
- t2 = $[4];
144
+ t3 = $[5];
141
145
  }
142
- let t3;
143
- if ($[5] !== applyFilters || $[6] !== route.params) {
144
- t3 = /*#__PURE__*/_jsx(FormSheet.HeaderButton, {
146
+ let t4;
147
+ if ($[6] !== applyFilters || $[7] !== route.params) {
148
+ t4 = /*#__PURE__*/_jsx(FormSheet.HeaderButton, {
145
149
  title: "Apply",
146
150
  onPress: () => applyFilters(route.params)
147
151
  });
148
- $[5] = applyFilters;
149
- $[6] = route.params;
150
- $[7] = t3;
152
+ $[6] = applyFilters;
153
+ $[7] = route.params;
154
+ $[8] = t4;
151
155
  } else {
152
- t3 = $[7];
156
+ t4 = $[8];
153
157
  }
154
- let t4;
155
- if ($[8] !== t2 || $[9] !== t3) {
156
- t4 = /*#__PURE__*/_jsxs(FormSheet.HeaderActions, {
157
- children: [t2, t3]
158
+ let t5;
159
+ if ($[9] !== t3 || $[10] !== t4) {
160
+ t5 = /*#__PURE__*/_jsxs(FormSheet.HeaderActions, {
161
+ children: [t3, t4]
158
162
  });
159
- $[8] = t2;
160
163
  $[9] = t3;
161
164
  $[10] = t4;
165
+ $[11] = t5;
162
166
  } else {
163
- t4 = $[10];
167
+ t5 = $[11];
164
168
  }
165
- let t5;
166
- if ($[11] !== styles.header || $[12] !== t4) {
167
- t5 = /*#__PURE__*/_jsxs(FormSheet.Header, {
169
+ let t6;
170
+ if ($[12] !== styles.header || $[13] !== t2 || $[14] !== t5) {
171
+ t6 = /*#__PURE__*/_jsxs(FormSheet.Header, {
168
172
  style: styles.header,
169
- children: [t1, t4]
173
+ children: [t2, t5]
170
174
  });
171
- $[11] = styles.header;
172
- $[12] = t4;
173
- $[13] = t5;
175
+ $[12] = styles.header;
176
+ $[13] = t2;
177
+ $[14] = t5;
178
+ $[15] = t6;
174
179
  } else {
175
- t5 = $[13];
180
+ t6 = $[15];
176
181
  }
177
- let t6;
178
- if ($[14] !== handleFormSheetHeaderLayout || $[15] !== t5) {
179
- t6 = /*#__PURE__*/_jsx(View, {
182
+ let t7;
183
+ if ($[16] !== handleFormSheetHeaderLayout || $[17] !== t6) {
184
+ t7 = /*#__PURE__*/_jsx(View, {
180
185
  onLayout: handleFormSheetHeaderLayout,
181
- children: t5
186
+ children: t6
182
187
  });
183
- $[14] = handleFormSheetHeaderLayout;
184
- $[15] = t5;
185
- $[16] = t6;
188
+ $[16] = handleFormSheetHeaderLayout;
189
+ $[17] = t6;
190
+ $[18] = t7;
186
191
  } else {
187
- t6 = $[16];
192
+ t7 = $[18];
188
193
  }
189
- return t6;
194
+ return t7;
190
195
  };
191
196
  const useStyles = () => {
192
197
  const $ = _c(4);
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_filters_screen.js","names":["useHeaderHeight","ReactNavigationNative","useRoute","createNativeStackNavigator","React","useCallback","Platform","StyleSheet","useWindowDimensions","View","useSafeAreaInsets","FormSheet","getFormSheetScreenOptions","ConversationFilters","FilterProvider","useFilterContext","GroupFilters","TeamFilters","jsx","_jsx","jsxs","_jsxs","ConversationFiltersScreenOptions","presentation","select","android","ios","sheetAllowedDetents","default","headerTitle","FilterNavigator","initialRouteName","screens","Filters","screen","options","headerShown","headerBackTitle","createStaticNavigationComponent","tree","name","getComponent","createComponentForStaticNavigation","ConversationFiltersScreen","_props","$","_c","styles","useStyles","t0","t1","Symbol","for","FormSheetHeaderWithContext","t2","contentContainer","children","Root","contentStyle","showAndroidSheetGrabber","resetFilter","applyFilters","setFormSheetHeaderHeight","route","event","nativeEvent","layout","height","handleFormSheetHeaderLayout","HeaderTitle","HeaderTextButton","t3","params","HeaderButton","title","onPress","t4","HeaderActions","t5","header","Header","style","t6","top","headerHeight","containerHeight","create","paddingTop","borderBottomWidth"],"sources":["../../src/screens/conversation_filters_screen.tsx"],"sourcesContent":["import { useHeaderHeight } from '@react-navigation/elements'\nimport * as ReactNavigationNative from '@react-navigation/native'\nimport { RouteProp, useRoute } from '@react-navigation/native'\nimport { createNativeStackNavigator } from '@react-navigation/native-stack'\nimport React, { useCallback } from 'react'\nimport { LayoutChangeEvent, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'\nimport { ConversationFilters } from './conversation_filters/components/conversation_filters'\nimport {\n FilterProvider,\n useFilterContext,\n} from './conversation_filters/context/conversation_filter_context'\nimport { GroupFilters } from './conversation_filters/group_filters'\nimport {\n ConversationFiltersScreenProps,\n ConversationFilterStackParamList,\n} from './conversation_filters/screen_props'\nimport { TeamFilters } from './conversation_filters/team_filters'\n\nexport const ConversationFiltersScreenOptions = getFormSheetScreenOptions({\n presentation: Platform.select({ android: 'modal', ios: 'formSheet' }),\n sheetAllowedDetents: Platform.select({\n ios: [0.75, 1],\n default: [1],\n }),\n headerTitle: 'Filters',\n})\n\nconst FilterNavigator = createNativeStackNavigator<ConversationFilterStackParamList>({\n initialRouteName: 'Filters',\n screens: {\n Filters: {\n screen: ConversationFilters,\n options: {\n headerShown: false,\n },\n },\n GroupFilters: {\n screen: GroupFilters,\n options: {\n headerTitle: Platform.select({\n android: 'Groups',\n ios: '',\n }),\n headerBackTitle: 'Groups',\n },\n },\n TeamFilters: {\n screen: TeamFilters,\n options: {\n headerTitle: Platform.select({\n android: 'Teams',\n ios: '',\n }),\n headerBackTitle: 'Teams',\n },\n },\n },\n})\n\n// tree.getComponent() doesn't exist on every host's installed\n// react-navigation/core — hosts on an older version would throw here.\n// Where it does exist, it's the real implementation (the \"deprecated\"\n// createComponentForStaticNavigation is just a console.warn wrapper around\n// the same call on current versions), so prefer it and only reach for the\n// old function on cores where getComponent() genuinely isn't there yet.\nfunction createStaticNavigationComponent<P extends object>(\n tree: { getComponent?: () => React.ComponentType<P> },\n name: string\n): React.ComponentType<P> {\n if (typeof tree.getComponent === 'function') return tree.getComponent()\n\n const { createComponentForStaticNavigation } = ReactNavigationNative as {\n createComponentForStaticNavigation: (tree: unknown, name: string) => React.ComponentType<P>\n }\n\n return createComponentForStaticNavigation(tree, name)\n}\n\nconst Filters = createStaticNavigationComponent(FilterNavigator, 'Filters')\n\nexport const ConversationFiltersScreen = (_props: ConversationFiltersScreenProps) => {\n const styles = useStyles()\n\n return (\n <FilterProvider>\n <FormSheet.Root contentStyle={styles.contentContainer} showAndroidSheetGrabber={false}>\n <FormSheetHeaderWithContext />\n <Filters />\n </FormSheet.Root>\n </FilterProvider>\n )\n}\n\nconst FormSheetHeaderWithContext = () => {\n const { resetFilter, applyFilters, setFormSheetHeaderHeight } = useFilterContext()\n const route = useRoute<RouteProp<ConversationFiltersScreenProps['route']>>()\n const styles = useStyles()\n\n const handleFormSheetHeaderLayout = useCallback(\n (event: LayoutChangeEvent) => {\n setFormSheetHeaderHeight(event.nativeEvent.layout.height)\n },\n [setFormSheetHeaderHeight]\n )\n\n return (\n <View onLayout={handleFormSheetHeaderLayout}>\n <FormSheet.Header style={styles.header}>\n <FormSheet.HeaderTitle>Filters</FormSheet.HeaderTitle>\n <FormSheet.HeaderActions>\n <FormSheet.HeaderTextButton onPress={resetFilter}>Reset</FormSheet.HeaderTextButton>\n <FormSheet.HeaderButton title=\"Apply\" onPress={() => applyFilters(route.params)} />\n </FormSheet.HeaderActions>\n </FormSheet.Header>\n </View>\n )\n}\n\nconst useStyles = () => {\n const { top } = useSafeAreaInsets()\n const { height } = useWindowDimensions()\n const headerHeight = useHeaderHeight()\n\n const containerHeight = Platform.select({\n android: height,\n ios: height - top - headerHeight,\n })\n\n return StyleSheet.create({\n contentContainer: {\n height: containerHeight,\n },\n header: {\n paddingTop: Platform.select({\n ios: 20,\n android: 20 + top,\n }),\n // The filter list scrolls under it, so the sheet header doesn't need a\n // separator the way the non-scrolling reaction sheet does.\n borderBottomWidth: 0,\n },\n })\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"conversation_filters_screen.js","names":["useHeaderHeight","ReactNavigationNative","useRoute","createNativeStackNavigator","React","useCallback","Platform","StyleSheet","useWindowDimensions","View","useSafeAreaInsets","FormSheet","getFormSheetScreenOptions","useFilterDropdownEnabled","ConversationFilters","FilterProvider","useFilterContext","GroupFilters","TeamFilters","jsx","_jsx","jsxs","_jsxs","ConversationFiltersScreenOptions","presentation","select","android","ios","sheetAllowedDetents","default","headerTitle","FilterNavigator","initialRouteName","screens","Filters","screen","options","headerShown","headerBackTitle","createStaticNavigationComponent","tree","name","getComponent","createComponentForStaticNavigation","ConversationFiltersScreen","_props","$","_c","styles","useStyles","t0","t1","Symbol","for","FormSheetHeaderWithContext","t2","contentContainer","children","Root","contentStyle","showAndroidSheetGrabber","resetFilter","applyFilters","setFormSheetHeaderHeight","route","filterDropdownEnabled","event","nativeEvent","layout","height","handleFormSheetHeaderLayout","HeaderTitle","t3","HeaderTextButton","t4","params","HeaderButton","title","onPress","t5","HeaderActions","t6","header","Header","style","t7","top","headerHeight","containerHeight","create","paddingTop","borderBottomWidth"],"sources":["../../src/screens/conversation_filters_screen.tsx"],"sourcesContent":["import { useHeaderHeight } from '@react-navigation/elements'\nimport * as ReactNavigationNative from '@react-navigation/native'\nimport { RouteProp, useRoute } from '@react-navigation/native'\nimport { createNativeStackNavigator } from '@react-navigation/native-stack'\nimport React, { useCallback } from 'react'\nimport { LayoutChangeEvent, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'\nimport { useFilterDropdownEnabled } from '../hooks/use_filter_dropdown_enabled'\nimport { ConversationFilters } from './conversation_filters/components/conversation_filters'\nimport {\n FilterProvider,\n useFilterContext,\n} from './conversation_filters/context/conversation_filter_context'\nimport { GroupFilters } from './conversation_filters/group_filters'\nimport {\n ConversationFiltersScreenProps,\n ConversationFilterStackParamList,\n} from './conversation_filters/screen_props'\nimport { TeamFilters } from './conversation_filters/team_filters'\n\nexport const ConversationFiltersScreenOptions = getFormSheetScreenOptions({\n presentation: Platform.select({ android: 'modal', ios: 'formSheet' }),\n sheetAllowedDetents: Platform.select({\n ios: [0.75, 1],\n default: [1],\n }),\n headerTitle: 'Filters',\n})\n\nconst FilterNavigator = createNativeStackNavigator<ConversationFilterStackParamList>({\n initialRouteName: 'Filters',\n screens: {\n Filters: {\n screen: ConversationFilters,\n options: {\n headerShown: false,\n },\n },\n GroupFilters: {\n screen: GroupFilters,\n options: {\n headerTitle: Platform.select({\n android: 'Groups',\n ios: '',\n }),\n headerBackTitle: 'Groups',\n },\n },\n TeamFilters: {\n screen: TeamFilters,\n options: {\n headerTitle: Platform.select({\n android: 'Teams',\n ios: '',\n }),\n headerBackTitle: 'Teams',\n },\n },\n },\n})\n\n// tree.getComponent() doesn't exist on every host's installed\n// react-navigation/core — hosts on an older version would throw here.\n// Where it does exist, it's the real implementation (the \"deprecated\"\n// createComponentForStaticNavigation is just a console.warn wrapper around\n// the same call on current versions), so prefer it and only reach for the\n// old function on cores where getComponent() genuinely isn't there yet.\nfunction createStaticNavigationComponent<P extends object>(\n tree: { getComponent?: () => React.ComponentType<P> },\n name: string\n): React.ComponentType<P> {\n if (typeof tree.getComponent === 'function') return tree.getComponent()\n\n const { createComponentForStaticNavigation } = ReactNavigationNative as {\n createComponentForStaticNavigation: (tree: unknown, name: string) => React.ComponentType<P>\n }\n\n return createComponentForStaticNavigation(tree, name)\n}\n\nconst Filters = createStaticNavigationComponent(FilterNavigator, 'Filters')\n\nexport const ConversationFiltersScreen = (_props: ConversationFiltersScreenProps) => {\n const styles = useStyles()\n\n return (\n <FilterProvider>\n <FormSheet.Root contentStyle={styles.contentContainer} showAndroidSheetGrabber={false}>\n <FormSheetHeaderWithContext />\n <Filters />\n </FormSheet.Root>\n </FilterProvider>\n )\n}\n\nconst FormSheetHeaderWithContext = () => {\n const { resetFilter, applyFilters, setFormSheetHeaderHeight } = useFilterContext()\n const route = useRoute<RouteProp<ConversationFiltersScreenProps['route']>>()\n const filterDropdownEnabled = useFilterDropdownEnabled()\n const styles = useStyles()\n\n const handleFormSheetHeaderLayout = useCallback(\n (event: LayoutChangeEvent) => {\n setFormSheetHeaderHeight(event.nativeEvent.layout.height)\n },\n [setFormSheetHeaderHeight]\n )\n\n return (\n <View onLayout={handleFormSheetHeaderLayout}>\n <FormSheet.Header style={styles.header}>\n <FormSheet.HeaderTitle>\n {filterDropdownEnabled ? 'More filters' : 'Filters'}\n </FormSheet.HeaderTitle>\n <FormSheet.HeaderActions>\n <FormSheet.HeaderTextButton onPress={resetFilter}>Reset</FormSheet.HeaderTextButton>\n <FormSheet.HeaderButton title=\"Apply\" onPress={() => applyFilters(route.params)} />\n </FormSheet.HeaderActions>\n </FormSheet.Header>\n </View>\n )\n}\n\nconst useStyles = () => {\n const { top } = useSafeAreaInsets()\n const { height } = useWindowDimensions()\n const headerHeight = useHeaderHeight()\n\n const containerHeight = Platform.select({\n android: height,\n ios: height - top - headerHeight,\n })\n\n return StyleSheet.create({\n contentContainer: {\n height: containerHeight,\n },\n header: {\n paddingTop: Platform.select({\n ios: 20,\n android: 20 + top,\n }),\n // The filter list scrolls under it, so the sheet header doesn't need a\n // separator the way the non-scrolling reaction sheet does.\n borderBottomWidth: 0,\n },\n })\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "4.4.1-qa-staging.15",
3
+ "version": "4.4.1-qa-staging.17",
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.4.1-qa-staging.15",
36
+ "@planningcenter/emoji-keyboard": "4.4.1-qa-staging.17",
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": "810a097ac7d4e1d4eeb6ea64a0516e62dd8e0a2c"
87
+ "gitHead": "3f42f16e64f443ba3e1b2c24298954ecc380b52a"
88
88
  }
@@ -0,0 +1,129 @@
1
+ import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
2
+ import { render } from '@testing-library/react-native'
3
+ import React from 'react'
4
+ import { SafeAreaProvider } from 'react-native-safe-area-context'
5
+ import { useFilterDropdownEnabled } from '../../../../hooks/use_filter_dropdown_enabled'
6
+ import { ConversationFilters } from '../../../../screens/conversation_filters/components/conversation_filters'
7
+ import { FilterContext } from '../../../../screens/conversation_filters/context/conversation_filter_context'
8
+ import { FilterTypes } from '../../../../screens/conversation_filters/filter_types'
9
+ import { GroupResource } from '../../../../types/resources/group_resource'
10
+
11
+ jest.mock('react-native-gesture-handler', () => {
12
+ const { FlatList } = jest.requireActual('react-native')
13
+ const ReactActual = jest.requireActual('react')
14
+
15
+ return {
16
+ FlatList: (props: object) =>
17
+ ReactActual.createElement(FlatList, { ...props, initialNumToRender: 50 }),
18
+ }
19
+ })
20
+ jest.mock('@react-navigation/native', () => ({
21
+ ...jest.requireActual('@react-navigation/native'),
22
+ useNavigation: () => ({ navigate: jest.fn(), dispatch: jest.fn(), setParams: jest.fn() }),
23
+ }))
24
+ jest.mock('../../../../hooks/use_api_client', () => ({ useApiClient: () => ({}) }))
25
+ jest.mock('../../../../hooks/use_filter_dropdown_enabled', () => ({
26
+ useFilterDropdownEnabled: jest.fn(() => false),
27
+ }))
28
+ jest.mock('../../../../hooks/use_direct_messages_eligible', () => ({
29
+ useDirectMessagesEligible: () => true,
30
+ }))
31
+ jest.mock('../../../../hooks/use_has_direct_messages', () => ({
32
+ useHasDirectMessages: () => true,
33
+ }))
34
+ jest.mock('../../../../hooks/services/use_services_team', () => ({
35
+ useServicesTeamsMap: () => ({}),
36
+ }))
37
+ jest.mock('../../../../screens/conversation_filters/hooks/filters', () => ({
38
+ useGroupsToFilter: () => ({ groups: mockGroups }),
39
+ useTeamsToFilter: () => ({ teams: mockTeams }),
40
+ }))
41
+
42
+ const buildGroup = (id: number, name: string): GroupResource => ({
43
+ id: `Groups-Group-${id}`,
44
+ type: 'Group',
45
+ links: { self: '' },
46
+ name,
47
+ sourceAppName: 'Groups',
48
+ sourceType: 'Group',
49
+ })
50
+
51
+ const buildTeam = (id: number, name: string): GroupResource => ({
52
+ id: `Services-Team-${id}`,
53
+ type: 'Group',
54
+ links: { self: '' },
55
+ name,
56
+ sourceAppName: 'Services',
57
+ sourceType: 'Team',
58
+ })
59
+
60
+ const mockGroups = Array.from({ length: 6 }, (_, i) => buildGroup(i + 1, `Group ${i + 1}`))
61
+ const mockTeams = [buildTeam(1, 'Worship Team')]
62
+
63
+ const mockedFilterDropdownEnabled = jest.mocked(useFilterDropdownEnabled)
64
+
65
+ const safeAreaMetrics = {
66
+ frame: { x: 0, y: 0, width: 390, height: 844 },
67
+ insets: { top: 0, right: 0, bottom: 0, left: 0 },
68
+ }
69
+
70
+ const renderFilters = () =>
71
+ render(
72
+ <SafeAreaProvider initialMetrics={safeAreaMetrics}>
73
+ <ThemeProvider value={DefaultTheme}>
74
+ <FilterContext.Provider
75
+ value={{
76
+ params: {},
77
+ scrollOffset: 0,
78
+ setScrollOffset: jest.fn(),
79
+ setFormSheetHeaderHeight: jest.fn(),
80
+ resetFilter: jest.fn(),
81
+ setGroupFilter: jest.fn(),
82
+ setAppFilter: jest.fn(),
83
+ applyFilters: jest.fn(),
84
+ platformListPaddingBottom: 0,
85
+ }}
86
+ >
87
+ <ConversationFilters />
88
+ </FilterContext.Provider>
89
+ </ThemeProvider>
90
+ </SafeAreaProvider>
91
+ )
92
+
93
+ const generalFilterLabels = [
94
+ 'General Filters',
95
+ FilterTypes.All,
96
+ FilterTypes.Groups,
97
+ FilterTypes.Teams,
98
+ FilterTypes.DirectMessages,
99
+ ]
100
+
101
+ describe('ConversationFilters', () => {
102
+ beforeEach(() => {
103
+ jest.clearAllMocks()
104
+ })
105
+
106
+ it('renders the General Filters section when the filter dropdown is off', () => {
107
+ mockedFilterDropdownEnabled.mockReturnValue(false)
108
+
109
+ const { getByText } = renderFilters()
110
+
111
+ generalFilterLabels.forEach(label => expect(getByText(label)).toBeTruthy())
112
+ expect(getByText('Groups')).toBeTruthy()
113
+ expect(getByText('Teams')).toBeTruthy()
114
+ })
115
+
116
+ it('hides the General Filters section but keeps Groups and Teams when the filter dropdown is on', () => {
117
+ mockedFilterDropdownEnabled.mockReturnValue(true)
118
+
119
+ const { getByText, queryByText, getAllByText } = renderFilters()
120
+
121
+ generalFilterLabels.forEach(label => expect(queryByText(label)).toBeNull())
122
+ expect(getByText('Groups')).toBeTruthy()
123
+ expect(getByText('Teams')).toBeTruthy()
124
+ expect(getByText('Group 1')).toBeTruthy()
125
+ expect(queryByText('Group 6')).toBeNull()
126
+ expect(getByText('Worship Team')).toBeTruthy()
127
+ expect(getAllByText('View more')).toHaveLength(1)
128
+ })
129
+ })
@@ -9,6 +9,7 @@ import { ServicesForm } from '../../../../screens/conversation_new/components/se
9
9
  jest.mock('react-native-reanimated', () => ({
10
10
  ...jest.requireActual('react-native-reanimated/mock'),
11
11
  LayoutAnimationConfig: ({ children }: { children: React.ReactNode }) => children,
12
+ useReducedMotion: () => false,
12
13
  }))
13
14
  jest.mock('@react-navigation/native', () => ({
14
15
  ...jest.requireActual('@react-navigation/native'),
@@ -110,7 +110,7 @@ function ShadowMessageContent({ conversation_id, ...message }: ShadowMessageCont
110
110
  accessibilityRole="link"
111
111
  >
112
112
  <Animated.View style={[StyleSheet.absoluteFill, animatedBackgroundColor]} />
113
- <View style={styles.message}>
113
+ <View style={[styles.message, { marginBottom: -SPACE_BELOW_PRESSABLE }]}>
114
114
  {!message.mine && (
115
115
  <View>
116
116
  <View style={[styles.avatarWrapper, !isDeleted && styles.avatarOpacity]}>
@@ -297,7 +297,6 @@ const useStyles = ({ mine, imageWidth = 32, imageHeight = 32 }: StylesProps = {}
297
297
  gap: 8,
298
298
  flexDirection: mine ? 'row-reverse' : 'row',
299
299
  paddingHorizontal: CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
300
- marginBottom: -SPACE_BELOW_PRESSABLE,
301
300
  },
302
301
  messageContent: {
303
302
  flex: 1,
@@ -0,0 +1,72 @@
1
+ import { fireEvent, render } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import { ConversationsBlankState } from '../conversations_blank_state'
4
+
5
+ const mockSetParams = jest.fn()
6
+ const mockUseFilterDropdownEnabled = jest.fn()
7
+
8
+ jest.mock('@react-navigation/native', () => ({
9
+ ...jest.requireActual('@react-navigation/native'),
10
+ useNavigation: () => ({ setParams: mockSetParams }),
11
+ }))
12
+
13
+ jest.mock('../../../hooks/use_filter_dropdown_enabled', () => ({
14
+ useFilterDropdownEnabled: () => mockUseFilterDropdownEnabled(),
15
+ }))
16
+
17
+ describe('ConversationsBlankState', () => {
18
+ beforeEach(() => {
19
+ mockSetParams.mockClear()
20
+ })
21
+
22
+ describe('with the filter dropdown on', () => {
23
+ beforeEach(() => {
24
+ mockUseFilterDropdownEnabled.mockReturnValue(true)
25
+ })
26
+
27
+ it('offers to view all conversations when a filter is applied', () => {
28
+ const view = render(
29
+ <ConversationsBlankState isFilterApplied={true} canCreateConversations={true} />
30
+ )
31
+
32
+ expect(view.getByText('No matches')).toBeTruthy()
33
+ expect(view.queryByText('No conversations yet.')).toBeNull()
34
+
35
+ fireEvent.press(view.getByText('View all conversations'))
36
+
37
+ expect(mockSetParams).toHaveBeenCalledTimes(1)
38
+ expect(mockSetParams.mock.calls[0][0]).toStrictEqual({
39
+ title: undefined,
40
+ chat_group_graph_id: undefined,
41
+ group_source_app_name: undefined,
42
+ show_direct_messages: undefined,
43
+ })
44
+ })
45
+
46
+ it('keeps the compose prompt when no filter is applied', () => {
47
+ const view = render(
48
+ <ConversationsBlankState isFilterApplied={false} canCreateConversations={true} />
49
+ )
50
+
51
+ expect(view.getByText('No conversations yet.')).toBeTruthy()
52
+ expect(view.getByText('Tap the compose button to get started.')).toBeTruthy()
53
+ expect(view.queryByText('View all conversations')).toBeNull()
54
+ })
55
+ })
56
+
57
+ describe('with the filter dropdown off', () => {
58
+ beforeEach(() => {
59
+ mockUseFilterDropdownEnabled.mockReturnValue(false)
60
+ })
61
+
62
+ it('keeps the adjust-your-filters copy when a filter is applied', () => {
63
+ const view = render(
64
+ <ConversationsBlankState isFilterApplied={true} canCreateConversations={true} />
65
+ )
66
+
67
+ expect(view.getByText('No conversations yet.')).toBeTruthy()
68
+ expect(view.getByText('Adjust your filters to find conversations.')).toBeTruthy()
69
+ expect(view.queryByText('View all conversations')).toBeNull()
70
+ })
71
+ })
72
+ })
@@ -5,9 +5,11 @@ import { useConversationsContext } from '../../contexts/conversations_context'
5
5
  import { useTheme } from '../../hooks'
6
6
  import { useCanCreateConversations } from '../../hooks/use_chat_permissions'
7
7
  import { useConversationsJoltEvents } from '../../hooks/use_conversations_jolt_events'
8
+ import { useFilterDropdownEnabled } from '../../hooks/use_filter_dropdown_enabled'
8
9
  import { ConversationResource } from '../../types'
9
10
  import { ConversationPreview, ConversationPreviewSkeleton } from './conversation_preview'
10
11
  import { ConversationsBlankState } from './conversations_blank_state'
12
+ import { FilteredResultsFooter } from './filtered_results_footer'
11
13
 
12
14
  interface ConversationsProps {
13
15
  ListHeaderComponent?:
@@ -26,11 +28,19 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
26
28
  refetch,
27
29
  isRefetching,
28
30
  isFetched,
29
- args: { chat_group_graph_id, group_source_app_name },
31
+ hasNextPage,
32
+ args: { chat_group_graph_id, group_source_app_name, show_direct_messages },
30
33
  } = useConversationsContext()
31
34
  const navigation = useNavigation()
32
35
  const canCreateConversations = useCanCreateConversations()
33
- const isFilterApplied = !!chat_group_graph_id || !!group_source_app_name
36
+ const filterDropdownEnabled = useFilterDropdownEnabled()
37
+ const isFilterApplied = !!chat_group_graph_id || !!group_source_app_name || !!show_direct_messages
38
+ const showFilteredResultsFooter =
39
+ filterDropdownEnabled &&
40
+ isFilterApplied &&
41
+ !hasNextPage &&
42
+ !isLoading &&
43
+ conversations.length > 0
34
44
 
35
45
  const showBadges = !chat_group_graph_id
36
46
 
@@ -62,6 +72,7 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
62
72
  canCreateConversations={canCreateConversations}
63
73
  />
64
74
  }
75
+ ListFooterComponent={showFilteredResultsFooter ? <FilteredResultsFooter /> : null}
65
76
  renderItem={({ item }) => {
66
77
  if (item.type === 'loading') {
67
78
  return <ConversationPreviewSkeleton />
@@ -1,5 +1,7 @@
1
1
  import React from 'react'
2
2
  import { StyleSheet, View } from 'react-native'
3
+ import { useClearConversationFilters } from '../../hooks/use_clear_conversation_filters'
4
+ import { useFilterDropdownEnabled } from '../../hooks/use_filter_dropdown_enabled'
3
5
  import BlankState from '../primitive/blank_state_primitive'
4
6
 
5
7
  interface ConversationsBlankStateProps {
@@ -11,6 +13,25 @@ export function ConversationsBlankState({
11
13
  isFilterApplied,
12
14
  canCreateConversations,
13
15
  }: ConversationsBlankStateProps) {
16
+ const filterDropdownEnabled = useFilterDropdownEnabled()
17
+ const clearFilters = useClearConversationFilters()
18
+
19
+ if (filterDropdownEnabled && isFilterApplied) {
20
+ return (
21
+ <View style={styles.container}>
22
+ <BlankState.Root>
23
+ <BlankState.Imagery name="general.outlinedTextMessage" />
24
+ <BlankState.Content>
25
+ <BlankState.Heading>No matches</BlankState.Heading>
26
+ </BlankState.Content>
27
+ <BlankState.TextButton onPress={clearFilters}>
28
+ View all conversations
29
+ </BlankState.TextButton>
30
+ </BlankState.Root>
31
+ </View>
32
+ )
33
+ }
34
+
14
35
  return (
15
36
  <View style={styles.container}>
16
37
  <BlankState.Root>
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { StyleSheet, View } from 'react-native'
3
+ import { useTheme } from '../../hooks'
4
+ import { useClearConversationFilters } from '../../hooks/use_clear_conversation_filters'
5
+ import { Text, TextButton } from '../display'
6
+
7
+ export function FilteredResultsFooter() {
8
+ const styles = useStyles()
9
+ const clearFilters = useClearConversationFilters()
10
+
11
+ return (
12
+ <View style={styles.container}>
13
+ <Text variant="tertiary" style={styles.text}>
14
+ You&rsquo;re viewing filtered results
15
+ </Text>
16
+ <TextButton variant="tertiary" onPress={clearFilters}>
17
+ View all conversations
18
+ </TextButton>
19
+ </View>
20
+ )
21
+ }
22
+
23
+ const useStyles = () => {
24
+ const { colors } = useTheme()
25
+
26
+ return StyleSheet.create({
27
+ container: {
28
+ alignItems: 'center',
29
+ gap: 8,
30
+ paddingHorizontal: 16,
31
+ paddingVertical: 24,
32
+ },
33
+ text: {
34
+ textAlign: 'center',
35
+ color: colors.textColorDefaultSecondary,
36
+ },
37
+ })
38
+ }
@@ -1,5 +1,6 @@
1
1
  import { act, renderHook, waitFor } from '@testing-library/react-native'
2
2
  import { AccessibilityInfo, EmitterSubscription } from 'react-native'
3
+ import * as Reanimated from 'react-native-reanimated'
3
4
  import { useReduceMotion } from '../use_reduce_motion'
4
5
 
5
6
  type ReduceMotionListener = (enabled: boolean) => void
@@ -11,6 +12,7 @@ describe('useReduceMotion', () => {
11
12
  beforeEach(() => {
12
13
  listener = undefined
13
14
  remove.mockClear()
15
+ jest.spyOn(Reanimated, 'useReducedMotion').mockReturnValue(false)
14
16
  jest.spyOn(AccessibilityInfo, 'isReduceMotionEnabled').mockResolvedValue(true)
15
17
  const addEventListener: typeof AccessibilityInfo.addEventListener = (_event, handler) => {
16
18
  listener = handler as ReduceMotionListener
@@ -21,7 +23,17 @@ describe('useReduceMotion', () => {
21
23
 
22
24
  afterEach(() => jest.restoreAllMocks())
23
25
 
24
- it('seeds from the system setting', async () => {
26
+ it('seeds synchronously from the launch-time setting', async () => {
27
+ jest.spyOn(Reanimated, 'useReducedMotion').mockReturnValue(true)
28
+ jest.mocked(AccessibilityInfo.isReduceMotionEnabled).mockResolvedValue(false)
29
+
30
+ const { result } = renderHook(() => useReduceMotion())
31
+
32
+ expect(result.current).toBe(true)
33
+ await waitFor(() => expect(result.current).toBe(false))
34
+ })
35
+
36
+ it('updates to the live setting once it resolves', async () => {
25
37
  const { result } = renderHook(() => useReduceMotion())
26
38
 
27
39
  expect(result.current).toBe(false)
@@ -0,0 +1,15 @@
1
+ import { useNavigation } from '@react-navigation/native'
2
+ import { useCallback } from 'react'
3
+
4
+ export function useClearConversationFilters() {
5
+ const navigation = useNavigation()
6
+
7
+ return useCallback(() => {
8
+ navigation.setParams({
9
+ title: undefined,
10
+ chat_group_graph_id: undefined,
11
+ group_source_app_name: undefined,
12
+ show_direct_messages: undefined,
13
+ })
14
+ }, [navigation])
15
+ }
@@ -1,8 +1,9 @@
1
1
  import { useEffect, useState } from 'react'
2
2
  import { AccessibilityInfo } from 'react-native'
3
+ import { useReducedMotion } from 'react-native-reanimated'
3
4
 
4
5
  export const useReduceMotion = () => {
5
- const [reduceMotion, setReduceMotion] = useState(false)
6
+ const [reduceMotion, setReduceMotion] = useState(useReducedMotion())
6
7
 
7
8
  useEffect(() => {
8
9
  AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion)