@planningcenter/chat-react-native 3.2.0-rc.17 → 3.2.0-rc.19

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 (67) hide show
  1. package/build/hooks/groups/use_group_members_for_new_conversation.d.ts +26 -26
  2. package/build/hooks/use_api.d.ts +41 -41
  3. package/build/hooks/use_api.d.ts.map +1 -1
  4. package/build/hooks/use_api.js.map +1 -1
  5. package/build/hooks/use_chat_permissions.d.ts +14 -14
  6. package/build/hooks/use_groups.d.ts +26 -26
  7. package/build/hooks/use_groups_groups.d.ts +26 -26
  8. package/build/hooks/use_teams.d.ts +26 -26
  9. package/build/navigation/index.d.ts +26 -16
  10. package/build/navigation/index.d.ts.map +1 -1
  11. package/build/navigation/index.js +11 -0
  12. package/build/navigation/index.js.map +1 -1
  13. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.d.ts.map +1 -1
  14. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js +5 -4
  15. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js.map +1 -1
  16. package/build/screens/conversation_filter_recipients/hooks/{use_flattened_array_of_service_types_and_their_teams.d.ts → use_flattened_array_of_service_types_with_teams.d.ts} +2 -2
  17. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.d.ts.map +1 -0
  18. package/build/screens/conversation_filter_recipients/hooks/{use_flattened_array_of_service_types_and_their_teams.js → use_flattened_array_of_service_types_with_teams.js} +2 -2
  19. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js.map +1 -0
  20. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +8 -0
  21. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts.map +1 -0
  22. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js +65 -0
  23. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js.map +1 -0
  24. package/build/screens/conversation_filters/hooks/filters.d.ts +40 -40
  25. package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts +12 -0
  26. package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts.map +1 -0
  27. package/build/screens/conversation_select_recipients/components/recipient_link_row.js +61 -0
  28. package/build/screens/conversation_select_recipients/components/recipient_link_row.js.map +1 -0
  29. package/build/screens/conversation_select_recipients/components/view_more_link_row.d.ts +7 -0
  30. package/build/screens/conversation_select_recipients/components/view_more_link_row.d.ts.map +1 -0
  31. package/build/screens/conversation_select_recipients/components/view_more_link_row.js +21 -0
  32. package/build/screens/conversation_select_recipients/components/view_more_link_row.js.map +1 -0
  33. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts +4 -0
  34. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts.map +1 -0
  35. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js +46 -0
  36. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js.map +1 -0
  37. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts +1 -8
  38. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts.map +1 -1
  39. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js +26 -56
  40. package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js.map +1 -1
  41. package/build/screens/conversation_select_recipients/types/screen_props.d.ts +9 -0
  42. package/build/screens/conversation_select_recipients/types/screen_props.d.ts.map +1 -0
  43. package/build/screens/conversation_select_recipients/types/screen_props.js +2 -0
  44. package/build/screens/conversation_select_recipients/types/screen_props.js.map +1 -0
  45. package/package.json +2 -2
  46. package/src/hooks/use_api.ts +3 -3
  47. package/src/navigation/index.tsx +16 -0
  48. package/src/screens/conversation_filter_recipients/conversation_filter_recipients_screen.tsx +5 -4
  49. package/src/screens/conversation_filter_recipients/hooks/{use_flattened_array_of_service_types_and_their_teams.tsx → use_flattened_array_of_service_types_with_teams.tsx} +1 -1
  50. package/src/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.ts +90 -0
  51. package/src/screens/conversation_select_recipients/components/recipient_link_row.tsx +91 -0
  52. package/src/screens/conversation_select_recipients/components/view_more_link_row.tsx +30 -0
  53. package/src/screens/conversation_select_recipients/conversation_select_group_recipients_screen.tsx +76 -0
  54. package/src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx +39 -73
  55. package/src/screens/conversation_select_recipients/types/screen_props.tsx +11 -0
  56. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_and_their_teams.d.ts.map +0 -1
  57. package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_and_their_teams.js.map +0 -1
  58. package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.d.ts +0 -3
  59. package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.d.ts.map +0 -1
  60. package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.js +0 -133
  61. package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.js.map +0 -1
  62. package/build/screens/conversation_new/utils/fake_member_data.d.ts +0 -3
  63. package/build/screens/conversation_new/utils/fake_member_data.d.ts.map +0 -1
  64. package/build/screens/conversation_new/utils/fake_member_data.js +0 -129
  65. package/build/screens/conversation_new/utils/fake_member_data.js.map +0 -1
  66. package/src/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.ts +0 -134
  67. package/src/screens/conversation_new/utils/fake_member_data.ts +0 -130
@@ -1,20 +1,20 @@
1
- import { PlatformPressable } from '@react-navigation/elements';
2
1
  import { useNavigation } from '@react-navigation/native';
3
2
  import React from 'react';
4
3
  import { ScrollView, StyleSheet, View } from 'react-native';
5
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
- import { Button, Heading, Icon, Image, Text } from '../../components';
7
- import { useTheme } from '../../hooks';
4
+ import { Button, Heading } from '../../components';
8
5
  import { useGroupsGroups } from '../../hooks/use_groups_groups';
9
- import { platformFontWeightMedium, pluralize } from '../../utils';
10
- const ASPECT_RATIO = 16 / 9;
11
- const THUMBNAIL_WIDTH = 80;
12
- const THUMBNAIL_HEIGHT = THUMBNAIL_WIDTH / ASPECT_RATIO;
6
+ import { pluralize } from '../../utils';
7
+ import { RecipientLinkRow } from './components/recipient_link_row';
8
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
+ import { ViewMoreLinkRow } from './components/view_more_link_row';
10
+ const MAX_VISIBLE_GROUPS = 5;
13
11
  export const ConversationSelectRecipientsScreen = ({ route, }) => {
14
12
  const styles = useStyles();
15
13
  const navigation = useNavigation();
16
14
  const { data: groups = [] } = useGroupsGroups();
17
15
  const groupsWithCreatePermission = groups.filter(g => g.canCreateConversation);
16
+ const visibleGroups = groupsWithCreatePermission.slice(0, MAX_VISIBLE_GROUPS);
17
+ const hasMoreGroups = groupsWithCreatePermission.length > MAX_VISIBLE_GROUPS;
18
18
  const handleNavigateToConversationNew = (group) => {
19
19
  navigation.navigate('New', {
20
20
  screen: 'ConversationNew',
@@ -25,25 +25,25 @@ export const ConversationSelectRecipientsScreen = ({ route, }) => {
25
25
  },
26
26
  });
27
27
  };
28
- return (<ScrollView style={styles.container}>
28
+ const handleGroupsViewMore = () => {
29
+ navigation.navigate('New', {
30
+ screen: 'ConversationSelectGroupRecipients',
31
+ params: {
32
+ ...route.params,
33
+ },
34
+ });
35
+ };
36
+ return (<ScrollView contentContainerStyle={styles.contentContainer}>
29
37
  <View style={styles.section}>
30
38
  <View style={styles.sectionHeader}>
31
39
  <Heading variant="h3">My groups</Heading>
32
40
  </View>
33
41
  <View>
34
- {groupsWithCreatePermission.map(group => {
42
+ {visibleGroups.map(group => {
35
43
  const groupAccessibilityLabel = `Select ${group.name} with ${pluralize(group.membershipsCount, 'member')}`;
36
- return (<PlatformPressable key={group.id} style={styles.row} onPress={() => handleNavigateToConversationNew(group)} accessibilityRole="link" accessibilityLabel={groupAccessibilityLabel} accessibilityHint={`Selects group as recipient and navigates to the final screen to finish creating the conversation.`}>
37
- <Image source={{ uri: group.headerImage?.thumbnail }} resizeMode="cover" style={styles.rowImage} alt=""/>
38
- <View style={styles.rowContent}>
39
- <Text style={styles.rowName} numberOfLines={2}>
40
- {group.name}
41
- </Text>
42
- <Text variant="tertiary">{group.membershipsCount} members</Text>
43
- </View>
44
- <Icon name="general.rightChevron" size={16} style={styles.rowIconRight}/>
45
- </PlatformPressable>);
44
+ return (<RecipientLinkRow key={group.id} imageUri={group.headerImage?.thumbnail} title={group.name} subtitle={`${group.membershipsCount} members`} onPress={() => handleNavigateToConversationNew(group)} accessibilityLabel={groupAccessibilityLabel} accessibilityHint={`Selects group as recipient and navigates to the final screen to finish creating the conversation`}/>);
46
45
  })}
46
+ {hasMoreGroups && (<ViewMoreLinkRow onPress={handleGroupsViewMore} accessibilityHint="Navigate to a full list of your groups."/>)}
47
47
  </View>
48
48
  </View>
49
49
  <View style={styles.section}>
@@ -61,52 +61,22 @@ export const ConversationSelectRecipientsScreen = ({ route, }) => {
61
61
  };
62
62
  const useStyles = () => {
63
63
  const { bottom } = useSafeAreaInsets();
64
- const theme = useTheme();
65
64
  return StyleSheet.create({
66
- container: {
67
- flex: 1,
68
- gap: 8,
65
+ contentContainer: {
66
+ gap: 24,
67
+ paddingTop: 16,
68
+ paddingBottom: 16 + bottom,
69
69
  },
70
70
  section: {
71
- padding: 16,
72
71
  flex: 1,
73
72
  },
74
73
  sectionHeader: {
75
74
  flexDirection: 'row',
76
75
  justifyContent: 'space-between',
76
+ paddingHorizontal: 16,
77
+ paddingBottom: 4,
77
78
  },
78
79
  selectTeamsButton: {},
79
- row: {
80
- flexDirection: 'row',
81
- alignItems: 'center',
82
- gap: 12,
83
- paddingVertical: 16,
84
- borderBottomWidth: 1,
85
- borderColor: theme.colors.fillColorNeutral050Base,
86
- },
87
- rowImage: {
88
- width: THUMBNAIL_WIDTH,
89
- height: THUMBNAIL_HEIGHT,
90
- borderRadius: 4,
91
- },
92
- rowName: {
93
- fontWeight: platformFontWeightMedium,
94
- flexShrink: 1,
95
- },
96
- rowContent: {
97
- flexShrink: 1,
98
- },
99
- rowIconRight: {
100
- marginLeft: 'auto',
101
- color: theme.colors.fillColorNeutral030,
102
- },
103
- routeDebug: {
104
- alignContent: 'center',
105
- padding: 16,
106
- paddingBottom: bottom,
107
- borderTopWidth: 1,
108
- borderTopColor: theme.colors.fillColorNeutral050Base,
109
- },
110
80
  });
111
81
  };
112
82
  //# sourceMappingURL=conversation_select_recipients_screen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_select_recipients_screen.js","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport { StaticScreenProps, useNavigation } from '@react-navigation/native'\nimport React from 'react'\nimport { ScrollView, StyleSheet, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Button, Heading, Icon, Image, Text } from '../../components'\nimport { useTheme } from '../../hooks'\nimport { GroupsGroupResource } from '../../types'\nimport { useGroupsGroups } from '../../hooks/use_groups_groups'\nimport { GraphId } from '../../types/resources/group_resource'\nimport { AppName } from '../../types/resources/app_name'\nimport { platformFontWeightMedium, pluralize } from '../../utils'\n\ntype ConversationSelectRecipientsScreenProps = StaticScreenProps<{\n chat_group_graph_id?: GraphId\n group_source_app_name?: AppName\n}>\n\nconst ASPECT_RATIO = 16 / 9\nconst THUMBNAIL_WIDTH = 80\nconst THUMBNAIL_HEIGHT = THUMBNAIL_WIDTH / ASPECT_RATIO\n\nexport const ConversationSelectRecipientsScreen = ({\n route,\n}: ConversationSelectRecipientsScreenProps) => {\n const styles = useStyles()\n const navigation = useNavigation()\n const { data: groups = [] } = useGroupsGroups()\n const groupsWithCreatePermission = groups.filter(g => g.canCreateConversation)\n\n const handleNavigateToConversationNew = (group: GroupsGroupResource) => {\n navigation.navigate('New', {\n screen: 'ConversationNew',\n params: {\n group_id: group.id,\n source_app_name: 'Groups',\n ...route.params,\n },\n })\n }\n\n return (\n <ScrollView style={styles.container}>\n <View style={styles.section}>\n <View style={styles.sectionHeader}>\n <Heading variant=\"h3\">My groups</Heading>\n </View>\n <View>\n {groupsWithCreatePermission.map(group => {\n const groupAccessibilityLabel = `Select ${group.name} with ${pluralize(group.membershipsCount, 'member')}`\n\n return (\n <PlatformPressable\n key={group.id}\n style={styles.row}\n onPress={() => handleNavigateToConversationNew(group)}\n accessibilityRole=\"link\"\n accessibilityLabel={groupAccessibilityLabel}\n accessibilityHint={`Selects group as recipient and navigates to the final screen to finish creating the conversation.`}\n >\n <Image\n source={{ uri: group.headerImage?.thumbnail }}\n resizeMode=\"cover\"\n style={styles.rowImage}\n alt=\"\"\n />\n <View style={styles.rowContent}>\n <Text style={styles.rowName} numberOfLines={2}>\n {group.name}\n </Text>\n <Text variant=\"tertiary\">{group.membershipsCount} members</Text>\n </View>\n <Icon name=\"general.rightChevron\" size={16} style={styles.rowIconRight} />\n </PlatformPressable>\n )\n })}\n </View>\n </View>\n <View style={styles.section}>\n <View style={styles.sectionHeader}>\n <Heading variant=\"h3\">Teams I lead</Heading>\n <Button\n style={styles.selectTeamsButton}\n onPress={() =>\n navigation.navigate('New', {\n screen: 'ConversationFilterRecipients',\n params: {\n source_app_name: 'Services',\n },\n })\n }\n title=\"Select teams\"\n variant=\"outline\"\n iconNameLeft=\"general.search\"\n />\n </View>\n </View>\n </ScrollView>\n )\n}\n\nconst useStyles = () => {\n const { bottom } = useSafeAreaInsets()\n const theme = useTheme()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n gap: 8,\n },\n section: {\n padding: 16,\n flex: 1,\n },\n sectionHeader: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n },\n selectTeamsButton: {},\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 12,\n paddingVertical: 16,\n borderBottomWidth: 1,\n borderColor: theme.colors.fillColorNeutral050Base,\n },\n rowImage: {\n width: THUMBNAIL_WIDTH,\n height: THUMBNAIL_HEIGHT,\n borderRadius: 4,\n },\n rowName: {\n fontWeight: platformFontWeightMedium,\n flexShrink: 1,\n },\n rowContent: {\n flexShrink: 1,\n },\n rowIconRight: {\n marginLeft: 'auto',\n color: theme.colors.fillColorNeutral030,\n },\n routeDebug: {\n alignContent: 'center',\n padding: 16,\n paddingBottom: bottom,\n borderTopWidth: 1,\n borderTopColor: theme.colors.fillColorNeutral050Base,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"conversation_select_recipients_screen.js","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useNavigation } from '@react-navigation/native'\nimport React from 'react'\nimport { ScrollView, StyleSheet, View } from 'react-native'\nimport { Button, Heading } from '../../components'\nimport { GroupsGroupResource } from '../../types'\nimport { useGroupsGroups } from '../../hooks/use_groups_groups'\nimport { pluralize } from '../../utils'\nimport { RecipientLinkRow } from './components/recipient_link_row'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { ConversationSelectRecipientsScreenProps } from './types/screen_props'\nimport { ViewMoreLinkRow } from './components/view_more_link_row'\n\nconst MAX_VISIBLE_GROUPS = 5\n\nexport const ConversationSelectRecipientsScreen = ({\n route,\n}: ConversationSelectRecipientsScreenProps) => {\n const styles = useStyles()\n const navigation = useNavigation()\n const { data: groups = [] } = useGroupsGroups()\n const groupsWithCreatePermission = groups.filter(g => g.canCreateConversation)\n const visibleGroups = groupsWithCreatePermission.slice(0, MAX_VISIBLE_GROUPS)\n const hasMoreGroups = groupsWithCreatePermission.length > MAX_VISIBLE_GROUPS\n\n const handleNavigateToConversationNew = (group: GroupsGroupResource) => {\n navigation.navigate('New', {\n screen: 'ConversationNew',\n params: {\n group_id: group.id,\n source_app_name: 'Groups',\n ...route.params,\n },\n })\n }\n\n const handleGroupsViewMore = () => {\n navigation.navigate('New', {\n screen: 'ConversationSelectGroupRecipients',\n params: {\n ...route.params,\n },\n })\n }\n\n return (\n <ScrollView contentContainerStyle={styles.contentContainer}>\n <View style={styles.section}>\n <View style={styles.sectionHeader}>\n <Heading variant=\"h3\">My groups</Heading>\n </View>\n <View>\n {visibleGroups.map(group => {\n const groupAccessibilityLabel = `Select ${group.name} with ${pluralize(group.membershipsCount, 'member')}`\n\n return (\n <RecipientLinkRow\n key={group.id}\n imageUri={group.headerImage?.thumbnail}\n title={group.name}\n subtitle={`${group.membershipsCount} members`}\n onPress={() => handleNavigateToConversationNew(group)}\n accessibilityLabel={groupAccessibilityLabel}\n accessibilityHint={`Selects group as recipient and navigates to the final screen to finish creating the conversation`}\n />\n )\n })}\n {hasMoreGroups && (\n <ViewMoreLinkRow\n onPress={handleGroupsViewMore}\n accessibilityHint=\"Navigate to a full list of your groups.\"\n />\n )}\n </View>\n </View>\n <View style={styles.section}>\n <View style={styles.sectionHeader}>\n <Heading variant=\"h3\">Teams I lead</Heading>\n <Button\n style={styles.selectTeamsButton}\n onPress={() =>\n navigation.navigate('New', {\n screen: 'ConversationFilterRecipients',\n params: {\n source_app_name: 'Services',\n },\n })\n }\n title=\"Select teams\"\n variant=\"outline\"\n iconNameLeft=\"general.search\"\n />\n </View>\n </View>\n </ScrollView>\n )\n}\n\nconst useStyles = () => {\n const { bottom } = useSafeAreaInsets()\n\n return StyleSheet.create({\n contentContainer: {\n gap: 24,\n paddingTop: 16,\n paddingBottom: 16 + bottom,\n },\n section: {\n flex: 1,\n },\n sectionHeader: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n paddingHorizontal: 16,\n paddingBottom: 4,\n },\n selectTeamsButton: {},\n })\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { StaticScreenProps } from '@react-navigation/native';
2
+ import { AppName } from '../../../types/resources/app_name';
3
+ import { GraphId } from '../../../types/resources/group_resource';
4
+ export type ConversationSelectRecipientsParams = {
5
+ chat_group_graph_id?: GraphId;
6
+ group_source_app_name?: AppName;
7
+ };
8
+ export type ConversationSelectRecipientsScreenProps = StaticScreenProps<ConversationSelectRecipientsParams>;
9
+ //# sourceMappingURL=screen_props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen_props.d.ts","sourceRoot":"","sources":["../../../../src/screens/conversation_select_recipients/types/screen_props.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAA;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAA;AAEjE,MAAM,MAAM,kCAAkC,GAAG;IAC/C,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,uCAAuC,GACjD,iBAAiB,CAAC,kCAAkC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=screen_props.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen_props.js","sourceRoot":"","sources":["../../../../src/screens/conversation_select_recipients/types/screen_props.tsx"],"names":[],"mappings":"","sourcesContent":["import { StaticScreenProps } from '@react-navigation/native'\nimport { AppName } from '../../../types/resources/app_name'\nimport { GraphId } from '../../../types/resources/group_resource'\n\nexport type ConversationSelectRecipientsParams = {\n chat_group_graph_id?: GraphId\n group_source_app_name?: AppName\n}\n\nexport type ConversationSelectRecipientsScreenProps =\n StaticScreenProps<ConversationSelectRecipientsParams>\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "3.2.0-rc.17",
3
+ "version": "3.2.0-rc.19",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -55,5 +55,5 @@
55
55
  "prettier": "^3.4.2",
56
56
  "typescript": "<5.6.0"
57
57
  },
58
- "gitHead": "e08fb91663287cbdec079e616e66908c09535bb8"
58
+ "gitHead": "5b6f02db88d2113ee904fff72b294f412cb58ae8"
59
59
  }
@@ -4,7 +4,7 @@ import {
4
4
  useInfiniteQuery,
5
5
  useQuery,
6
6
  } from '@tanstack/react-query'
7
- import { ApiCollection, ApiResource, ResourceObject } from '../types'
7
+ import { ApiCollection, ApiError, ApiResource, ResourceObject } from '../types'
8
8
  import { GetRequest, RequestData } from '../utils/client/types'
9
9
  import { App, useApiClient } from './use_api_client'
10
10
  import { getRequestQueryKey, RequestQueryKey } from './use_suspense_api'
@@ -18,7 +18,7 @@ export const useApiGet = <T extends ResourceObject | ResourceObject[]>(args: Api
18
18
  type Resource = ApiResource<T>
19
19
  const apiClient = useApiClient()
20
20
 
21
- const { data, ...query } = useQuery<Resource, Response>({
21
+ const { data, ...query } = useQuery<Resource, ApiError>({
22
22
  queryKey: getRequestQueryKey(args),
23
23
  queryFn: ({ queryKey }) => {
24
24
  const [url, d, headers, app = 'chat'] = queryKey as RequestQueryKey
@@ -48,7 +48,7 @@ export const useApiPaginator = <T extends ResourceObject>(
48
48
  const apiClient = useApiClient()
49
49
  const query = useInfiniteQuery<
50
50
  ApiCollection<T>,
51
- Response,
51
+ ApiError,
52
52
  InfiniteData<ApiCollection<T>>,
53
53
  any,
54
54
  Partial<RequestData> | undefined
@@ -29,6 +29,7 @@ import {
29
29
  ConversationFiltersScreenOptions,
30
30
  } from '../screens/conversation_filters_screen'
31
31
  import { ConversationFiltersParams } from '../screens/conversation_filters/screen_props'
32
+ import { ConversationSelectGroupRecipientsScreen } from '../screens/conversation_select_recipients/conversation_select_group_recipients_screen'
32
33
 
33
34
  export const NewConversationStack = createNativeStackNavigator({
34
35
  initialRouteName: 'ConversationSelectRecipients',
@@ -46,6 +47,21 @@ export const NewConversationStack = createNativeStackNavigator({
46
47
  Cancel
47
48
  </HeaderRightButton>
48
49
  ),
50
+ headerBackVisible: false,
51
+ }),
52
+ },
53
+ ConversationSelectGroupRecipients: {
54
+ screen: ConversationSelectGroupRecipientsScreen,
55
+ options: ({ navigation, route }) => ({
56
+ title: 'New conversation',
57
+ headerRight: (props: NativeStackHeaderRightProps) => (
58
+ <HeaderRightButton
59
+ {...props}
60
+ onPress={() => navigation.popTo('Conversations', route.params)}
61
+ >
62
+ Cancel
63
+ </HeaderRightButton>
64
+ ),
49
65
  }),
50
66
  },
51
67
  ConversationFilterRecipients: {
@@ -5,8 +5,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'
5
5
  import { FlashList } from '@shopify/flash-list'
6
6
  import { Heading } from '../../components'
7
7
  import { useTheme } from '../../hooks'
8
- import { useFlattenedArrayOfServiceTypesAndTheirTeams } from './hooks/use_flattened_array_of_service_types_and_their_teams'
9
- import { FAKE_SERVICE_TYPE_WITH_TEAMS_DATA } from './utils/fake_service_type_with_teams_data'
8
+ import { useFlattenedArrayOfServiceTypesWithTeams } from './hooks/use_flattened_array_of_service_types_with_teams'
10
9
  import { ConversationFilterRecipientsScreenProps, SectionTypes } from './types'
11
10
  import { HeaderRow } from './components/header_row'
12
11
  import { tokens } from '../../vendor/tapestry/tokens'
@@ -14,6 +13,7 @@ import { CheckboxRow } from './components/checkbox_row'
14
13
  import { useHeaderHeight } from '@react-navigation/elements'
15
14
  import { FilterRecipientsProvider } from './context/conversation_filter_recipients_context'
16
15
  import { HeaderRightWithContext, FilterHeaderTitle } from './components/navigation_header'
16
+ import { useServiceTypesWithTeams } from './hooks/use_service_types_with_teams'
17
17
 
18
18
  const SERVICE_TYPE_LABELLED_BY_PREFIX = 'header-'
19
19
 
@@ -33,8 +33,9 @@ export const ConversationFilterReceipientsScreenOptions: NativeStackNavigationOp
33
33
  export const ConversationFilterRecipientsScreen = ({}: ConversationFilterRecipientsScreenProps) => {
34
34
  const styles = useStyles()
35
35
 
36
- const data = useFlattenedArrayOfServiceTypesAndTheirTeams({
37
- data: FAKE_SERVICE_TYPE_WITH_TEAMS_DATA,
36
+ const { serviceTypes } = useServiceTypesWithTeams()
37
+ const data = useFlattenedArrayOfServiceTypesWithTeams({
38
+ data: serviceTypes,
38
39
  firstRowStyle: styles.firstRow,
39
40
  lastRowStyle: styles.lastRow,
40
41
  })
@@ -8,7 +8,7 @@ interface Props {
8
8
  lastRowStyle: ViewStyle
9
9
  }
10
10
 
11
- export function useFlattenedArrayOfServiceTypesAndTheirTeams({
11
+ export function useFlattenedArrayOfServiceTypesWithTeams({
12
12
  data,
13
13
  firstRowStyle,
14
14
  lastRowStyle,
@@ -0,0 +1,90 @@
1
+ import { useCallback, useMemo } from 'react'
2
+ import { useApiGet } from '../../../hooks/use_api'
3
+ import { ResourceObject } from '../../../types'
4
+ import { ServiceTypeWithTeams } from '../types'
5
+ import { uniqBy } from 'lodash'
6
+
7
+ export function useServiceTypesWithTeams() {
8
+ const response = useApiGet<ServicesChatResource>({
9
+ url: `/chat`,
10
+ data: {
11
+ fields: {
12
+ Chat: ['teams_i_lead'],
13
+ },
14
+ },
15
+ app: 'services',
16
+ })
17
+
18
+ const { data, isFetching, isError, error } = response
19
+
20
+ const teamsILead = data?.teamsILead || stableEmptyTeamResponseArray
21
+
22
+ const decoratedResponse = useMemo(() => {
23
+ return decorateTeamResponseItems(teamsILead)
24
+ }, [teamsILead])
25
+
26
+ const hasNoServicesAccess = useCallback(() => {
27
+ if (isError && error) {
28
+ const errorArray = error
29
+ const allErrors = errorArray.errors.map(e => e.detail)
30
+ if (allErrors.some(e => e.includes(NOT_IN_APPLICATION_CODE))) {
31
+ return true
32
+ }
33
+ }
34
+ return false
35
+ }, [isError, error])
36
+
37
+ return {
38
+ serviceTypes: decoratedResponse,
39
+ isFetching,
40
+ isError,
41
+ noServicesAccess: hasNoServicesAccess(),
42
+ }
43
+ }
44
+
45
+ function decorateTeamResponseItems(teamResponseItems: TeamResponseItem[]) {
46
+ return teamResponseItems
47
+ .map(({ value, serviceTypeName, teamName }) => ({
48
+ service_type: {
49
+ id: value.serviceTypeId,
50
+ name: serviceTypeName,
51
+ },
52
+ team: {
53
+ id: value.teamId,
54
+ name: teamName,
55
+ },
56
+ }))
57
+ .reduce((acc: ServiceTypeWithTeams[], { service_type, team }) => {
58
+ let serviceTypeEntry = acc.find(entry => entry.id === service_type.id)
59
+
60
+ if (!serviceTypeEntry) {
61
+ serviceTypeEntry = {
62
+ id: service_type.id,
63
+ name: service_type.name,
64
+ teams: [],
65
+ }
66
+ acc.push(serviceTypeEntry)
67
+ }
68
+ const initialTeams = serviceTypeEntry.teams
69
+ serviceTypeEntry.teams = uniqBy([...initialTeams, team], 'id')
70
+ return acc
71
+ }, [])
72
+ }
73
+
74
+ interface TeamResponseItem {
75
+ value: {
76
+ teamId: number
77
+ serviceTypeId: number
78
+ }
79
+ name: string
80
+ teamName: string
81
+ serviceTypeName: string
82
+ order: string[]
83
+ }
84
+
85
+ interface ServicesChatResource extends ResourceObject {
86
+ teamsILead: TeamResponseItem[]
87
+ }
88
+
89
+ const stableEmptyTeamResponseArray: TeamResponseItem[] = []
90
+ const NOT_IN_APPLICATION_CODE = 'TRASH_PANDA'
@@ -0,0 +1,91 @@
1
+ import { PlatformPressable } from '@react-navigation/elements'
2
+ import React from 'react'
3
+ import { StyleSheet, View } from 'react-native'
4
+ import { Icon, Image, Text } from '../../../components'
5
+ import { useTheme } from '../../../hooks'
6
+ import { platformFontWeightMedium } from '../../../utils'
7
+
8
+ interface RecipientLinkRowProps {
9
+ onPress: () => void
10
+ accessibilityLabel: string
11
+ accessibilityHint: string
12
+ imageUri?: string
13
+ title: string
14
+ subtitle: string
15
+ }
16
+
17
+ export const RecipientLinkRow = ({
18
+ onPress,
19
+ accessibilityLabel,
20
+ accessibilityHint,
21
+ imageUri,
22
+ title,
23
+ subtitle,
24
+ }: RecipientLinkRowProps) => {
25
+ const styles = useStyles()
26
+
27
+ return (
28
+ <PlatformPressable
29
+ style={styles.row}
30
+ onPress={onPress}
31
+ accessibilityRole="link"
32
+ accessibilityLabel={accessibilityLabel}
33
+ accessibilityHint={accessibilityHint}
34
+ >
35
+ <View style={styles.innerContainer}>
36
+ {imageUri && (
37
+ <Image source={{ uri: imageUri }} resizeMode="cover" style={styles.image} alt="" />
38
+ )}
39
+ <View style={styles.content}>
40
+ <Text style={styles.title} numberOfLines={2}>
41
+ {title}
42
+ </Text>
43
+ <Text variant="tertiary" numberOfLines={1}>
44
+ {subtitle}
45
+ </Text>
46
+ </View>
47
+ <Icon name="general.rightChevron" size={16} style={styles.icon} />
48
+ </View>
49
+ </PlatformPressable>
50
+ )
51
+ }
52
+
53
+ const ASPECT_RATIO = 16 / 9
54
+ const THUMBNAIL_WIDTH = 80
55
+ const THUMBNAIL_HEIGHT = THUMBNAIL_WIDTH / ASPECT_RATIO
56
+
57
+ const useStyles = () => {
58
+ const theme = useTheme()
59
+
60
+ return StyleSheet.create({
61
+ row: {
62
+ paddingLeft: 16,
63
+ },
64
+ innerContainer: {
65
+ flexDirection: 'row',
66
+ alignItems: 'center',
67
+ gap: 12,
68
+ paddingVertical: 16,
69
+ paddingRight: 16,
70
+ borderBottomWidth: 1,
71
+ borderColor: theme.colors.fillColorNeutral050Base,
72
+ },
73
+ image: {
74
+ width: THUMBNAIL_WIDTH,
75
+ height: THUMBNAIL_HEIGHT,
76
+ borderRadius: 4,
77
+ },
78
+ title: {
79
+ fontWeight: platformFontWeightMedium,
80
+ flexShrink: 1,
81
+ },
82
+ content: {
83
+ flexShrink: 1,
84
+ gap: 2,
85
+ },
86
+ icon: {
87
+ marginLeft: 'auto',
88
+ color: theme.colors.fillColorNeutral030,
89
+ },
90
+ })
91
+ }
@@ -0,0 +1,30 @@
1
+ import { StyleSheet, View } from 'react-native'
2
+ import { TextButton } from '../../../components'
3
+
4
+ interface ViewMoreLinkRowProps {
5
+ onPress: () => void
6
+ accessibilityHint: string
7
+ }
8
+
9
+ export const ViewMoreLinkRow = ({ accessibilityHint, onPress }: ViewMoreLinkRowProps) => {
10
+ const styles = useRowStyles()
11
+
12
+ return (
13
+ <View style={[styles.row]}>
14
+ <TextButton onPress={onPress} accessibilityHint={accessibilityHint} accessibilityRole="link">
15
+ View more
16
+ </TextButton>
17
+ </View>
18
+ )
19
+ }
20
+
21
+ const useRowStyles = () => {
22
+ return StyleSheet.create({
23
+ row: {
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ paddingVertical: 12,
27
+ paddingHorizontal: 16,
28
+ },
29
+ })
30
+ }
@@ -0,0 +1,76 @@
1
+ import { useNavigation } from '@react-navigation/native'
2
+ import React from 'react'
3
+ import { StyleSheet, View } from 'react-native'
4
+ import { FlashList } from '@shopify/flash-list'
5
+ import { Heading } from '../../components'
6
+ import { GroupsGroupResource } from '../../types'
7
+ import { useGroupsGroups } from '../../hooks/use_groups_groups'
8
+ import { pluralize } from '../../utils'
9
+ import { RecipientLinkRow } from './components/recipient_link_row'
10
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
11
+ import { ConversationSelectRecipientsScreenProps } from './types/screen_props'
12
+
13
+ export const ConversationSelectGroupRecipientsScreen = ({
14
+ route,
15
+ }: ConversationSelectRecipientsScreenProps) => {
16
+ const styles = useStyles()
17
+ const navigation = useNavigation()
18
+ const { data: groups = [] } = useGroupsGroups()
19
+ const groupsWithCreatePermission = groups.filter(g => g.canCreateConversation)
20
+
21
+ const handleNavigateToConversationNew = (group: GroupsGroupResource) => {
22
+ navigation.navigate('New', {
23
+ screen: 'ConversationNew',
24
+ params: {
25
+ group_id: group.id,
26
+ source_app_name: 'Groups',
27
+ ...route.params,
28
+ },
29
+ })
30
+ }
31
+
32
+ return (
33
+ <FlashList
34
+ data={groupsWithCreatePermission}
35
+ keyExtractor={item => item.id.toString()}
36
+ estimatedItemSize={65}
37
+ contentContainerStyle={styles.contentContainer}
38
+ ListHeaderComponent={
39
+ <View style={styles.sectionHeader}>
40
+ <Heading variant="h3">All my groups</Heading>
41
+ </View>
42
+ }
43
+ renderItem={({ item: group }: { item: GroupsGroupResource }) => {
44
+ const groupAccessibilityLabel = `Select ${group.name} with ${pluralize(group.membershipsCount, 'member')}`
45
+
46
+ return (
47
+ <RecipientLinkRow
48
+ key={group.id}
49
+ imageUri={group.headerImage?.thumbnail}
50
+ title={group.name}
51
+ subtitle={`${group.membershipsCount} members`}
52
+ onPress={() => handleNavigateToConversationNew(group)}
53
+ accessibilityLabel={groupAccessibilityLabel}
54
+ accessibilityHint={`Selects group as recipient and navigates to the final screen to finish creating the conversation`}
55
+ />
56
+ )
57
+ }}
58
+ />
59
+ )
60
+ }
61
+
62
+ const useStyles = () => {
63
+ const { bottom } = useSafeAreaInsets()
64
+
65
+ return StyleSheet.create({
66
+ contentContainer: {
67
+ paddingBottom: bottom,
68
+ },
69
+ sectionHeader: {
70
+ flexDirection: 'row',
71
+ justifyContent: 'space-between',
72
+ padding: 16,
73
+ paddingBottom: 4,
74
+ },
75
+ })
76
+ }