@planningcenter/chat-react-native 3.2.0 → 3.4.0-rc.0

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 (48) hide show
  1. package/build/hooks/services/use_find_or_create_services_conversation.d.ts +9 -0
  2. package/build/hooks/services/use_find_or_create_services_conversation.d.ts.map +1 -0
  3. package/build/hooks/services/use_find_or_create_services_conversation.js +72 -0
  4. package/build/hooks/services/use_find_or_create_services_conversation.js.map +1 -0
  5. package/build/hooks/services/use_team_members_for_new_conversation.d.ts +176 -0
  6. package/build/hooks/services/use_team_members_for_new_conversation.d.ts.map +1 -0
  7. package/build/hooks/services/use_team_members_for_new_conversation.js +29 -0
  8. package/build/hooks/services/use_team_members_for_new_conversation.js.map +1 -0
  9. package/build/hooks/services/use_team_plans.d.ts +8 -0
  10. package/build/hooks/services/use_team_plans.d.ts.map +1 -0
  11. package/build/hooks/services/use_team_plans.js +37 -0
  12. package/build/hooks/services/use_team_plans.js.map +1 -0
  13. package/build/hooks/services/use_teams_i_lead.d.ts +8 -0
  14. package/build/hooks/services/use_teams_i_lead.d.ts.map +1 -0
  15. package/build/hooks/services/use_teams_i_lead.js +34 -0
  16. package/build/hooks/services/use_teams_i_lead.js.map +1 -0
  17. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +1 -1
  18. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts.map +1 -1
  19. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js +4 -28
  20. package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js.map +1 -1
  21. package/build/screens/conversation_new/components/filter_by_plan_options.d.ts +8 -0
  22. package/build/screens/conversation_new/components/filter_by_plan_options.d.ts.map +1 -0
  23. package/build/screens/conversation_new/components/filter_by_plan_options.js +37 -0
  24. package/build/screens/conversation_new/components/filter_by_plan_options.js.map +1 -0
  25. package/build/screens/conversation_new/components/services_form.d.ts +8 -0
  26. package/build/screens/conversation_new/components/services_form.d.ts.map +1 -0
  27. package/build/screens/conversation_new/components/services_form.js +137 -0
  28. package/build/screens/conversation_new/components/services_form.js.map +1 -0
  29. package/build/screens/conversation_new/conversation_new_screen.js +2 -2
  30. package/build/screens/conversation_new/conversation_new_screen.js.map +1 -1
  31. package/build/types/resources/services/team_resource.d.ts +6 -3
  32. package/build/types/resources/services/team_resource.d.ts.map +1 -1
  33. package/build/types/resources/services/team_resource.js.map +1 -1
  34. package/package.json +2 -2
  35. package/src/hooks/services/use_find_or_create_services_conversation.ts +97 -0
  36. package/src/hooks/services/use_team_members_for_new_conversation.ts +40 -0
  37. package/src/hooks/services/use_team_plans.ts +42 -0
  38. package/src/hooks/services/use_teams_i_lead.ts +44 -0
  39. package/src/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.ts +5 -48
  40. package/src/screens/conversation_new/components/filter_by_plan_options.tsx +64 -0
  41. package/src/screens/conversation_new/components/services_form.tsx +229 -0
  42. package/src/screens/conversation_new/conversation_new_screen.tsx +2 -2
  43. package/src/types/resources/services/team_resource.ts +7 -3
  44. package/build/screens/conversation_new/components/team_form.d.ts +0 -8
  45. package/build/screens/conversation_new/components/team_form.d.ts.map +0 -1
  46. package/build/screens/conversation_new/components/team_form.js +0 -11
  47. package/build/screens/conversation_new/components/team_form.js.map +0 -1
  48. package/src/screens/conversation_new/components/team_form.tsx +0 -18
@@ -0,0 +1,229 @@
1
+ import { StackActions, useNavigation } from '@react-navigation/native'
2
+ import React, { useCallback, useMemo, useState } from 'react'
3
+ import { StyleSheet, View } from 'react-native'
4
+ import { Badge, Banner, ChildNotice, Heading, Switch, Text } from '../../../components'
5
+ import { ActionButton } from '../../../components/display/action_button'
6
+ import { Divider, FormList } from './form_list'
7
+ import { pluralize } from '../../../utils'
8
+ import { uniq } from 'lodash'
9
+ import { useTeamsILead } from '../../../hooks/services/use_teams_i_lead'
10
+ import { FilterByPlanOptions } from './filter_by_plan_options'
11
+ import { useTeamMembersForNewConversation } from '../../../hooks/services/use_team_members_for_new_conversation'
12
+ import { useFindOrCreateServicesConversation } from '../../../hooks/services/use_find_or_create_services_conversation'
13
+ import { ConversationResource, MemberResource } from '../../../types'
14
+
15
+ type ServicesFormProps = {
16
+ initialTeamIds?: number[]
17
+ initialPlanId?: number
18
+ }
19
+
20
+ export const ServicesForm = ({ initialTeamIds, initialPlanId }: ServicesFormProps) => {
21
+ const styles = useStyles()
22
+ const [selectedPlanId, setSelectedPlanId] = useState<number | undefined>(initialPlanId)
23
+ const initialState = uniq(initialTeamIds) || [] // Uniq here because services can send duplicates in the teams_i_lead response.
24
+ const [selectedTeamIds, setSelectedTeamIds] = useState<number[]>(initialState)
25
+
26
+ const removeSelection = useCallback(
27
+ (teamId: number) => {
28
+ setSelectedTeamIds(selectedTeamIds.filter(id => id !== teamId))
29
+ },
30
+ [selectedTeamIds]
31
+ )
32
+
33
+ const [filerByPlan, setFilterByPlan] = useState(false)
34
+
35
+ const { members, isError: isMemberError } = useTeamMembersForNewConversation({
36
+ teamIds: selectedTeamIds,
37
+ planId: selectedPlanId,
38
+ })
39
+
40
+ const navigation = useNavigation()
41
+ const { mutate: createConversation } = useFindOrCreateServicesConversation({
42
+ teamIds: selectedTeamIds,
43
+ planId: filerByPlan ? selectedPlanId : undefined,
44
+ onSuccess: (conversation: ConversationResource) => {
45
+ // exit from the create stack
46
+ navigation.getParent()?.goBack()
47
+ // navigate to the conversation screen
48
+ navigation.dispatch(
49
+ StackActions.push('Conversation', {
50
+ conversation_id: conversation.id,
51
+ })
52
+ )
53
+ },
54
+ })
55
+
56
+ return (
57
+ <View style={styles.formContainer}>
58
+ <FormList
59
+ memberData={members}
60
+ FormContent={
61
+ <FormContent
62
+ selectedTeamIds={selectedTeamIds}
63
+ removeSelection={removeSelection}
64
+ selectedPlanId={selectedPlanId}
65
+ setSelectedPlanId={setSelectedPlanId}
66
+ filterByPlan={filerByPlan}
67
+ setFilterByPlan={setFilterByPlan}
68
+ members={members}
69
+ isMemberError={isMemberError}
70
+ />
71
+ }
72
+ />
73
+ <ActionButton
74
+ disabled={!selectedTeamIds.length}
75
+ title="Start Conversation"
76
+ onPress={createConversation}
77
+ infoText="Conversation will be automatically updated if any members are added or removed from included teams."
78
+ />
79
+ </View>
80
+ )
81
+ }
82
+
83
+ interface FormContentProps {
84
+ selectedTeamIds: number[]
85
+ removeSelection: (teamId: number) => void
86
+ selectedPlanId?: number
87
+ setSelectedPlanId: (planId: number | undefined) => void
88
+ filterByPlan: boolean
89
+ setFilterByPlan: (value: boolean) => void
90
+ members: MemberResource[]
91
+ isMemberError: boolean
92
+ }
93
+
94
+ function FormContent({
95
+ selectedTeamIds,
96
+ removeSelection,
97
+ selectedPlanId,
98
+ setSelectedPlanId,
99
+ filterByPlan,
100
+ setFilterByPlan,
101
+ members,
102
+ isMemberError,
103
+ }: FormContentProps) {
104
+ const { teamsILead } = useTeamsILead()
105
+ const selectedTeamsILead = useMemo(() => {
106
+ return teamsILead.filter(team => selectedTeamIds.includes(team.value.teamId))
107
+ }, [selectedTeamIds, teamsILead])
108
+
109
+ const styles = useStyles()
110
+ const teamCountHeader = pluralize(selectedTeamIds.length, 'team')
111
+ const memberCount = members.length
112
+ const childMembers = members.filter(member => member.child)
113
+ const hasChildren = childMembers.length > 0
114
+ const multipleServiceTypes =
115
+ uniq(selectedTeamsILead.map(team => team.value.serviceTypeId)).length > 1
116
+ const firstTeamId = selectedTeamIds[0]
117
+ filterByPlan = filterByPlan && !!firstTeamId && !multipleServiceTypes
118
+
119
+ return (
120
+ <View style={styles.formContent}>
121
+ <View style={styles.toSection}>
122
+ <View style={styles.toSectionRow}>
123
+ <Heading variant="h3">To:</Heading>
124
+ <Text>{teamCountHeader}</Text>
125
+ </View>
126
+ <View style={styles.toSectionRow}>
127
+ {selectedTeamsILead.map(team => (
128
+ <View key={team.value.teamId} style={styles.badgeRow}>
129
+ <Badge
130
+ iconNameRight="general.x"
131
+ label={team.name}
132
+ onPress={() => removeSelection(team.value.teamId)}
133
+ />
134
+ </View>
135
+ ))}
136
+ </View>
137
+ </View>
138
+ <Divider />
139
+ <View style={styles.filterByPlanSection}>
140
+ <View style={styles.filterByPlanSectionLead}>
141
+ <Heading variant="h3">Filter by plan</Heading>
142
+ <Switch
143
+ value={filterByPlan}
144
+ onValueChange={setFilterByPlan}
145
+ disabled={multipleServiceTypes}
146
+ />
147
+ </View>
148
+ {multipleServiceTypes ? (
149
+ <Banner
150
+ appearance="neutral"
151
+ description="Plan filtering is not possible using teams from multiple service types. Try choosing teams above with only one service type."
152
+ />
153
+ ) : filterByPlan ? (
154
+ <FilterByPlanOptions
155
+ teamIds={selectedTeamIds}
156
+ planId={selectedPlanId}
157
+ onChange={setSelectedPlanId}
158
+ />
159
+ ) : null}
160
+ </View>
161
+ <Divider />
162
+ <View style={styles.memberSection}>
163
+ <Heading variant="h3">{pluralize(memberCount, 'member')} selected</Heading>
164
+ {hasChildren && <ChildNotice childMembers={childMembers} style={styles.banner} />}
165
+ {isMemberError && (
166
+ <Banner
167
+ appearance="error"
168
+ description="There was an issue loading team members, please refresh and try again."
169
+ style={styles.banner}
170
+ />
171
+ )}
172
+ </View>
173
+ </View>
174
+ )
175
+ }
176
+
177
+ const useStyles = () => {
178
+ const sectionPadding = 16
179
+
180
+ return StyleSheet.create({
181
+ formContainer: {
182
+ flex: 1,
183
+ },
184
+ formContent: {
185
+ paddingVertical: sectionPadding,
186
+ flex: 1,
187
+ },
188
+ toSection: {
189
+ padding: sectionPadding,
190
+ gap: 8,
191
+ },
192
+ toSectionRow: {
193
+ flexDirection: 'row',
194
+ gap: 8,
195
+ alignItems: 'baseline',
196
+ flexWrap: 'wrap',
197
+ },
198
+ badgeRow: {
199
+ flexDirection: 'row',
200
+ },
201
+ groupName: {
202
+ fontSize: 18,
203
+ },
204
+ filterByPlanSection: {
205
+ padding: sectionPadding,
206
+ gap: 8,
207
+ },
208
+ filterByPlanSectionLead: {
209
+ flexDirection: 'row',
210
+ gap: 8,
211
+ justifyContent: 'space-between',
212
+ alignItems: 'center',
213
+ },
214
+ filterByPlanSectionRow: {
215
+ flexDirection: 'row',
216
+ gap: 8,
217
+ },
218
+ titleInput: {
219
+ fontSize: 18,
220
+ },
221
+ memberSection: {
222
+ padding: sectionPadding,
223
+ paddingBottom: 0,
224
+ },
225
+ banner: {
226
+ marginTop: 16,
227
+ },
228
+ })
229
+ }
@@ -1,7 +1,7 @@
1
1
  import { StaticScreenProps } from '@react-navigation/native'
2
2
  import React from 'react'
3
3
  import { GroupsForm } from './components/groups_form'
4
- import { TeamsForm } from './components/team_form'
4
+ import { ServicesForm } from './components/services_form'
5
5
  import { SourceAppErrorCard } from './components/source_app_error_card'
6
6
  import { AppName } from '../../types/resources/app_name'
7
7
  import { GraphId } from '../../types/resources/group_resource'
@@ -26,7 +26,7 @@ export const ConversationNewScreen = ({ route }: ConversationNewScreenProps) =>
26
26
  <SourceAppErrorCard />
27
27
  )
28
28
  case 'Services':
29
- return <TeamsForm initialTeamIds={team_ids} initialPlanId={plan_id} />
29
+ return <ServicesForm initialTeamIds={team_ids} initialPlanId={plan_id} />
30
30
  default:
31
31
  return <SourceAppErrorCard />
32
32
  }
@@ -38,16 +38,16 @@ export interface PlansResource extends ServicesChatResource {
38
38
  plans: PlansResponseItem[]
39
39
  }
40
40
 
41
- interface PlansResponseItem {
41
+ export interface PlansResponseItem {
42
42
  value: number
43
43
  name: string
44
44
  }
45
45
 
46
46
  export interface TeamPeopleResource extends ServicesChatResource {
47
- people: PersonResponseItem[]
47
+ people: TeamPersonResponseItem[]
48
48
  }
49
49
 
50
- interface PersonResponseItem {
50
+ export interface TeamPersonResponseItem {
51
51
  id: number
52
52
  name: string
53
53
  avatar: string
@@ -58,3 +58,7 @@ interface PersonResponseItem {
58
58
  export interface ServicesChatPayloadResource extends ServicesChatResource {
59
59
  payload: string
60
60
  }
61
+
62
+ export interface ServicesChatGroupIdentifiersResource extends ServicesChatResource {
63
+ groupIdentifiers: string[]
64
+ }
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- type TeamFormProps = {
3
- initialTeamIds?: number[];
4
- initialPlanId?: number;
5
- };
6
- export declare const TeamsForm: ({ initialTeamIds, initialPlanId }: TeamFormProps) => React.JSX.Element;
7
- export {};
8
- //# sourceMappingURL=team_form.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"team_form.d.ts","sourceRoot":"","sources":["../../../../src/screens/conversation_new/components/team_form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,KAAK,aAAa,GAAG;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,SAAS,sCAAuC,aAAa,sBAQzE,CAAA"}
@@ -1,11 +0,0 @@
1
- import React from 'react';
2
- import { View } from 'react-native';
3
- import { Text } from '../../../components';
4
- export const TeamsForm = ({ initialTeamIds, initialPlanId }) => {
5
- console.log('initialTeamIds', initialTeamIds);
6
- console.log('initialPlanId', initialPlanId);
7
- return (<View>
8
- <Text>Services team form coming soon</Text>
9
- </View>);
10
- };
11
- //# sourceMappingURL=team_form.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"team_form.js","sourceRoot":"","sources":["../../../../src/screens/conversation_new/components/team_form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAO1C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAE,cAAc,EAAE,aAAa,EAAiB,EAAE,EAAE;IAC5E,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAA;IAC7C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,aAAa,CAAC,CAAA;IAC3C,OAAO,CACL,CAAC,IAAI,CACH;MAAA,CAAC,IAAI,CAAC,8BAA8B,EAAE,IAAI,CAC5C;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC,CAAA","sourcesContent":["import React from 'react'\nimport { View } from 'react-native'\nimport { Text } from '../../../components'\n\ntype TeamFormProps = {\n initialTeamIds?: number[]\n initialPlanId?: number\n}\n\nexport const TeamsForm = ({ initialTeamIds, initialPlanId }: TeamFormProps) => {\n console.log('initialTeamIds', initialTeamIds)\n console.log('initialPlanId', initialPlanId)\n return (\n <View>\n <Text>Services team form coming soon</Text>\n </View>\n )\n}\n"]}
@@ -1,18 +0,0 @@
1
- import React from 'react'
2
- import { View } from 'react-native'
3
- import { Text } from '../../../components'
4
-
5
- type TeamFormProps = {
6
- initialTeamIds?: number[]
7
- initialPlanId?: number
8
- }
9
-
10
- export const TeamsForm = ({ initialTeamIds, initialPlanId }: TeamFormProps) => {
11
- console.log('initialTeamIds', initialTeamIds)
12
- console.log('initialPlanId', initialPlanId)
13
- return (
14
- <View>
15
- <Text>Services team form coming soon</Text>
16
- </View>
17
- )
18
- }