@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.
- package/build/hooks/groups/use_group_members_for_new_conversation.d.ts +26 -26
- package/build/hooks/use_api.d.ts +41 -41
- package/build/hooks/use_api.d.ts.map +1 -1
- package/build/hooks/use_api.js.map +1 -1
- package/build/hooks/use_chat_permissions.d.ts +14 -14
- package/build/hooks/use_groups.d.ts +26 -26
- package/build/hooks/use_groups_groups.d.ts +26 -26
- package/build/hooks/use_teams.d.ts +26 -26
- package/build/navigation/index.d.ts +26 -16
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +11 -0
- package/build/navigation/index.js.map +1 -1
- package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js +5 -4
- package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.js.map +1 -1
- 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
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.d.ts.map +1 -0
- 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
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js.map +1 -0
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +8 -0
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts.map +1 -0
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js +65 -0
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js.map +1 -0
- package/build/screens/conversation_filters/hooks/filters.d.ts +40 -40
- package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts +12 -0
- package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts.map +1 -0
- package/build/screens/conversation_select_recipients/components/recipient_link_row.js +61 -0
- package/build/screens/conversation_select_recipients/components/recipient_link_row.js.map +1 -0
- package/build/screens/conversation_select_recipients/components/view_more_link_row.d.ts +7 -0
- package/build/screens/conversation_select_recipients/components/view_more_link_row.d.ts.map +1 -0
- package/build/screens/conversation_select_recipients/components/view_more_link_row.js +21 -0
- package/build/screens/conversation_select_recipients/components/view_more_link_row.js.map +1 -0
- package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts +4 -0
- package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts.map +1 -0
- package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js +46 -0
- package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js.map +1 -0
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts +1 -8
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.d.ts.map +1 -1
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js +26 -56
- package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js.map +1 -1
- package/build/screens/conversation_select_recipients/types/screen_props.d.ts +9 -0
- package/build/screens/conversation_select_recipients/types/screen_props.d.ts.map +1 -0
- package/build/screens/conversation_select_recipients/types/screen_props.js +2 -0
- package/build/screens/conversation_select_recipients/types/screen_props.js.map +1 -0
- package/package.json +2 -2
- package/src/hooks/use_api.ts +3 -3
- package/src/navigation/index.tsx +16 -0
- package/src/screens/conversation_filter_recipients/conversation_filter_recipients_screen.tsx +5 -4
- 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
- package/src/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.ts +90 -0
- package/src/screens/conversation_select_recipients/components/recipient_link_row.tsx +91 -0
- package/src/screens/conversation_select_recipients/components/view_more_link_row.tsx +30 -0
- package/src/screens/conversation_select_recipients/conversation_select_group_recipients_screen.tsx +76 -0
- package/src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx +39 -73
- package/src/screens/conversation_select_recipients/types/screen_props.tsx +11 -0
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_and_their_teams.d.ts.map +0 -1
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_and_their_teams.js.map +0 -1
- package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.d.ts +0 -3
- package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.d.ts.map +0 -1
- package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.js +0 -133
- package/build/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.js.map +0 -1
- package/build/screens/conversation_new/utils/fake_member_data.d.ts +0 -3
- package/build/screens/conversation_new/utils/fake_member_data.d.ts.map +0 -1
- package/build/screens/conversation_new/utils/fake_member_data.js +0 -129
- package/build/screens/conversation_new/utils/fake_member_data.js.map +0 -1
- package/src/screens/conversation_filter_recipients/utils/fake_service_type_with_teams_data.ts +0 -134
- package/src/screens/conversation_new/utils/fake_member_data.ts +0 -130
package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
42
|
+
{visibleGroups.map(group => {
|
|
35
43
|
const groupAccessibilityLabel = `Select ${group.name} with ${pluralize(group.membershipsCount, 'member')}`;
|
|
36
|
-
return (<
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
package/build/screens/conversation_select_recipients/conversation_select_recipients_screen.js.map
CHANGED
|
@@ -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":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"conversation_select_recipients_screen.js","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_recipients_screen.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEjE,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAE5B,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,EACjD,KAAK,GACmC,EAAE,EAAE;IAC5C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAClC,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,eAAe,EAAE,CAAA;IAC/C,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAA;IAC9E,MAAM,aAAa,GAAG,0BAA0B,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAA;IAC7E,MAAM,aAAa,GAAG,0BAA0B,CAAC,MAAM,GAAG,kBAAkB,CAAA;IAE5E,MAAM,+BAA+B,GAAG,CAAC,KAA0B,EAAE,EAAE;QACrE,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE;YACzB,MAAM,EAAE,iBAAiB;YACzB,MAAM,EAAE;gBACN,QAAQ,EAAE,KAAK,CAAC,EAAE;gBAClB,eAAe,EAAE,QAAQ;gBACzB,GAAG,KAAK,CAAC,MAAM;aAChB;SACF,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,MAAM,oBAAoB,GAAG,GAAG,EAAE;QAChC,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE;YACzB,MAAM,EAAE,mCAAmC;YAC3C,MAAM,EAAE;gBACN,GAAG,KAAK,CAAC,MAAM;aAChB;SACF,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,OAAO,CACL,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CACzD;MAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAC1B;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC;UAAA,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAC1C;QAAA,EAAE,IAAI,CACN;QAAA,CAAC,IAAI,CACH;UAAA,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YACzB,MAAM,uBAAuB,GAAG,UAAU,KAAK,CAAC,IAAI,SAAS,SAAS,CAAC,KAAK,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAAE,CAAA;YAE1G,OAAO,CACL,CAAC,gBAAgB,CACf,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CACd,QAAQ,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CACvC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAClB,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,gBAAgB,UAAU,CAAC,CAC9C,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,+BAA+B,CAAC,KAAK,CAAC,CAAC,CACtD,kBAAkB,CAAC,CAAC,uBAAuB,CAAC,CAC5C,iBAAiB,CAAC,CAAC,kGAAkG,CAAC,EACtH,CACH,CAAA;QACH,CAAC,CAAC,CACF;UAAA,CAAC,aAAa,IAAI,CAChB,CAAC,eAAe,CACd,OAAO,CAAC,CAAC,oBAAoB,CAAC,CAC9B,iBAAiB,CAAC,yCAAyC,EAC3D,CACH,CACH;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,IAAI,CACN;MAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAC1B;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC;UAAA,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAC3C;UAAA,CAAC,MAAM,CACL,KAAK,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAChC,OAAO,CAAC,CAAC,GAAG,EAAE,CACZ,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE;YACzB,MAAM,EAAE,8BAA8B;YACtC,MAAM,EAAE;gBACN,eAAe,EAAE,UAAU;aAC5B;SACF,CACH,CAAC,CACD,KAAK,CAAC,cAAc,CACpB,OAAO,CAAC,SAAS,CACjB,YAAY,CAAC,gBAAgB,EAEjC;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,IAAI,CACR;IAAA,EAAE,UAAU,CAAC,CACd,CAAA;AACH,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAA;IAEtC,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,gBAAgB,EAAE;YAChB,GAAG,EAAE,EAAE;YACP,UAAU,EAAE,EAAE;YACd,aAAa,EAAE,EAAE,GAAG,MAAM;SAC3B;QACD,OAAO,EAAE;YACP,IAAI,EAAE,CAAC;SACR;QACD,aAAa,EAAE;YACb,aAAa,EAAE,KAAK;YACpB,cAAc,EAAE,eAAe;YAC/B,iBAAiB,EAAE,EAAE;YACrB,aAAa,EAAE,CAAC;SACjB;QACD,iBAAiB,EAAE,EAAE;KACtB,CAAC,CAAA;AACJ,CAAC,CAAA","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 @@
|
|
|
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.
|
|
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": "
|
|
58
|
+
"gitHead": "5b6f02db88d2113ee904fff72b294f412cb58ae8"
|
|
59
59
|
}
|
package/src/hooks/use_api.ts
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
51
|
+
ApiError,
|
|
52
52
|
InfiniteData<ApiCollection<T>>,
|
|
53
53
|
any,
|
|
54
54
|
Partial<RequestData> | undefined
|
package/src/navigation/index.tsx
CHANGED
|
@@ -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: {
|
package/src/screens/conversation_filter_recipients/conversation_filter_recipients_screen.tsx
CHANGED
|
@@ -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 {
|
|
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
|
|
37
|
-
|
|
36
|
+
const { serviceTypes } = useServiceTypesWithTeams()
|
|
37
|
+
const data = useFlattenedArrayOfServiceTypesWithTeams({
|
|
38
|
+
data: serviceTypes,
|
|
38
39
|
firstRowStyle: styles.firstRow,
|
|
39
40
|
lastRowStyle: styles.lastRow,
|
|
40
41
|
})
|
|
@@ -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
|
+
}
|
package/src/screens/conversation_select_recipients/conversation_select_group_recipients_screen.tsx
ADDED
|
@@ -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
|
+
}
|