@planningcenter/chat-react-native 3.32.1-rc.0 → 3.33.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.
- package/build/components/conversation/message_form.d.ts.map +1 -1
- package/build/components/conversation/message_form.js +22 -1
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/components/display/emoji_avatar.d.ts.map +1 -1
- package/build/components/display/emoji_avatar.js +2 -0
- package/build/components/display/emoji_avatar.js.map +1 -1
- package/build/components/display/icon_avatar.d.ts.map +1 -1
- package/build/components/display/icon_avatar.js +2 -0
- package/build/components/display/icon_avatar.js.map +1 -1
- package/build/components/display/utils/avatar_gradient_colors.d.ts +3 -0
- package/build/components/display/utils/avatar_gradient_colors.d.ts.map +1 -1
- package/build/components/display/utils/avatar_gradient_colors.js +8 -3
- package/build/components/display/utils/avatar_gradient_colors.js.map +1 -1
- package/build/components/page/error_boundary.d.ts.map +1 -1
- package/build/components/page/error_boundary.js +13 -10
- package/build/components/page/error_boundary.js.map +1 -1
- package/build/components/primitive/avatar_primitive.d.ts +3 -1
- package/build/components/primitive/avatar_primitive.d.ts.map +1 -1
- package/build/components/primitive/avatar_primitive.js +10 -2
- package/build/components/primitive/avatar_primitive.js.map +1 -1
- package/build/contexts/api_provider.d.ts.map +1 -1
- package/build/contexts/api_provider.js +2 -0
- package/build/contexts/api_provider.js.map +1 -1
- package/build/hooks/attachments/fallback_chat_configuration.d.ts +4 -0
- package/build/hooks/attachments/fallback_chat_configuration.d.ts.map +1 -0
- package/build/hooks/attachments/fallback_chat_configuration.js +59 -0
- package/build/hooks/attachments/fallback_chat_configuration.js.map +1 -0
- package/build/hooks/groups/use_groups_conversation_create.d.ts.map +1 -1
- package/build/hooks/groups/use_groups_conversation_create.js +1 -1
- package/build/hooks/groups/use_groups_conversation_create.js.map +1 -1
- package/build/hooks/services/use_find_or_create_services_conversation.d.ts +43 -11
- package/build/hooks/services/use_find_or_create_services_conversation.d.ts.map +1 -1
- package/build/hooks/services/use_find_or_create_services_conversation.js +5 -5
- package/build/hooks/services/use_find_or_create_services_conversation.js.map +1 -1
- package/build/hooks/use_attachment_uploader.d.ts.map +1 -1
- package/build/hooks/use_attachment_uploader.js +39 -14
- package/build/hooks/use_attachment_uploader.js.map +1 -1
- package/build/hooks/use_chat_configuration.d.ts +6 -0
- package/build/hooks/use_chat_configuration.d.ts.map +1 -0
- package/build/hooks/use_chat_configuration.js +41 -0
- package/build/hooks/use_chat_configuration.js.map +1 -0
- package/build/hooks/use_conversation_avatar_update.d.ts +26 -0
- package/build/hooks/use_conversation_avatar_update.d.ts.map +1 -0
- package/build/hooks/use_conversation_avatar_update.js +130 -0
- package/build/hooks/use_conversation_avatar_update.js.map +1 -0
- package/build/hooks/use_features.d.ts +1 -0
- package/build/hooks/use_features.d.ts.map +1 -1
- package/build/hooks/use_features.js +1 -0
- package/build/hooks/use_features.js.map +1 -1
- package/build/navigation/index.d.ts +16 -0
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +9 -0
- package/build/navigation/index.js.map +1 -1
- package/build/screens/avatar_picker/avatar_picker_screen.d.ts +12 -0
- package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -0
- package/build/screens/avatar_picker/avatar_picker_screen.js +193 -0
- package/build/screens/avatar_picker/avatar_picker_screen.js.map +1 -0
- package/build/screens/avatar_picker/avatar_picker_state.d.ts +38 -0
- package/build/screens/avatar_picker/avatar_picker_state.d.ts.map +1 -0
- package/build/screens/avatar_picker/avatar_picker_state.js +101 -0
- package/build/screens/avatar_picker/avatar_picker_state.js.map +1 -0
- package/build/screens/avatar_picker/avatar_preview.d.ts +9 -0
- package/build/screens/avatar_picker/avatar_preview.d.ts.map +1 -0
- package/build/screens/avatar_picker/avatar_preview.js +39 -0
- package/build/screens/avatar_picker/avatar_preview.js.map +1 -0
- package/build/screens/avatar_picker/color_picker.d.ts +9 -0
- package/build/screens/avatar_picker/color_picker.d.ts.map +1 -0
- package/build/screens/avatar_picker/color_picker.js +53 -0
- package/build/screens/avatar_picker/color_picker.js.map +1 -0
- package/build/screens/avatar_picker/constants.d.ts +3 -0
- package/build/screens/avatar_picker/constants.d.ts.map +1 -0
- package/build/screens/avatar_picker/constants.js +53 -0
- package/build/screens/avatar_picker/constants.js.map +1 -0
- package/build/screens/avatar_picker/emoji_tab.d.ts +7 -0
- package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -0
- package/build/screens/avatar_picker/emoji_tab.js +55 -0
- package/build/screens/avatar_picker/emoji_tab.js.map +1 -0
- package/build/screens/avatar_picker/icon_grid.d.ts +8 -0
- package/build/screens/avatar_picker/icon_grid.d.ts.map +1 -0
- package/build/screens/avatar_picker/icon_grid.js +48 -0
- package/build/screens/avatar_picker/icon_grid.js.map +1 -0
- package/build/screens/avatar_picker/upload_tab.d.ts +9 -0
- package/build/screens/avatar_picker/upload_tab.d.ts.map +1 -0
- package/build/screens/avatar_picker/upload_tab.js +39 -0
- package/build/screens/avatar_picker/upload_tab.js.map +1 -0
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +37 -1
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_new/components/avatar_selection_row.d.ts +12 -0
- package/build/screens/conversation_new/components/avatar_selection_row.d.ts.map +1 -0
- package/build/screens/conversation_new/components/avatar_selection_row.js +60 -0
- package/build/screens/conversation_new/components/avatar_selection_row.js.map +1 -0
- package/build/screens/conversation_new/components/gender_filter_toggle.d.ts.map +1 -1
- package/build/screens/conversation_new/components/gender_filter_toggle.js +3 -9
- package/build/screens/conversation_new/components/gender_filter_toggle.js.map +1 -1
- package/build/screens/conversation_new/components/groups_form.d.ts +3 -1
- package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
- package/build/screens/conversation_new/components/groups_form.js +22 -8
- package/build/screens/conversation_new/components/groups_form.js.map +1 -1
- package/build/screens/conversation_new/components/services_form.d.ts +3 -1
- package/build/screens/conversation_new/components/services_form.d.ts.map +1 -1
- package/build/screens/conversation_new/components/services_form.js +22 -8
- package/build/screens/conversation_new/components/services_form.js.map +1 -1
- package/build/screens/conversation_new/conversation_new_screen.d.ts +2 -0
- package/build/screens/conversation_new/conversation_new_screen.d.ts.map +1 -1
- package/build/screens/conversation_new/conversation_new_screen.js +3 -3
- package/build/screens/conversation_new/conversation_new_screen.js.map +1 -1
- package/build/screens/team_conversation_screen.d.ts.map +1 -1
- package/build/screens/team_conversation_screen.js +1 -1
- package/build/screens/team_conversation_screen.js.map +1 -1
- package/build/types/resources/chat_configuration_resource.d.ts +8 -0
- package/build/types/resources/chat_configuration_resource.d.ts.map +1 -0
- package/build/types/resources/chat_configuration_resource.js +2 -0
- package/build/types/resources/chat_configuration_resource.js.map +1 -0
- package/build/utils/auth_events.d.ts +7 -0
- package/build/utils/auth_events.d.ts.map +1 -0
- package/build/utils/auth_events.js +17 -0
- package/build/utils/auth_events.js.map +1 -0
- package/build/utils/native_adapters/configuration.d.ts +3 -0
- package/build/utils/native_adapters/configuration.d.ts.map +1 -1
- package/build/utils/native_adapters/configuration.js +8 -0
- package/build/utils/native_adapters/configuration.js.map +1 -1
- package/build/utils/native_adapters/document_picker.d.ts +21 -0
- package/build/utils/native_adapters/document_picker.d.ts.map +1 -0
- package/build/utils/native_adapters/document_picker.js +7 -0
- package/build/utils/native_adapters/document_picker.js.map +1 -0
- package/build/utils/native_adapters/image_picker.d.ts +7 -1
- package/build/utils/native_adapters/image_picker.d.ts.map +1 -1
- package/build/utils/native_adapters/image_picker.js.map +1 -1
- package/build/utils/native_adapters/index.d.ts +1 -0
- package/build/utils/native_adapters/index.d.ts.map +1 -1
- package/build/utils/native_adapters/index.js +1 -0
- package/build/utils/native_adapters/index.js.map +1 -1
- package/build/utils/request/get_chat_configuration.d.ts +10 -0
- package/build/utils/request/get_chat_configuration.d.ts.map +1 -0
- package/build/utils/request/get_chat_configuration.js +21 -0
- package/build/utils/request/get_chat_configuration.js.map +1 -0
- package/package.json +4 -3
- package/src/__tests__/hooks/use_attachment_uploader.test.tsx +219 -0
- package/src/__tests__/hooks/use_chat_configuration.test.tsx +80 -0
- package/src/__tests__/utils/native_adapters/configuration.ts +25 -1
- package/src/components/conversation/message_form.tsx +39 -1
- package/src/components/display/emoji_avatar.tsx +7 -2
- package/src/components/display/icon_avatar.tsx +7 -2
- package/src/components/display/utils/avatar_gradient_colors.ts +10 -3
- package/src/components/page/error_boundary.tsx +16 -9
- package/src/components/primitive/avatar_primitive.tsx +11 -2
- package/src/contexts/api_provider.tsx +3 -0
- package/src/hooks/attachments/fallback_chat_configuration.ts +61 -0
- package/src/hooks/groups/use_groups_conversation_create.ts +2 -1
- package/src/hooks/services/use_find_or_create_services_conversation.ts +7 -7
- package/src/hooks/use_attachment_uploader.ts +39 -15
- package/src/hooks/use_chat_configuration.ts +54 -0
- package/src/hooks/use_conversation_avatar_update.ts +163 -0
- package/src/hooks/use_features.ts +1 -0
- package/src/navigation/index.tsx +13 -0
- package/src/screens/avatar_picker/__tests__/avatar_picker_state.test.ts +157 -0
- package/src/screens/avatar_picker/avatar_picker_screen.tsx +312 -0
- package/src/screens/avatar_picker/avatar_picker_state.ts +141 -0
- package/src/screens/avatar_picker/avatar_preview.tsx +46 -0
- package/src/screens/avatar_picker/color_picker.tsx +91 -0
- package/src/screens/avatar_picker/constants.ts +53 -0
- package/src/screens/avatar_picker/emoji_tab.tsx +76 -0
- package/src/screens/avatar_picker/icon_grid.tsx +81 -0
- package/src/screens/avatar_picker/upload_tab.tsx +62 -0
- package/src/screens/conversation_details_screen.tsx +60 -1
- package/src/screens/conversation_new/components/avatar_selection_row.tsx +82 -0
- package/src/screens/conversation_new/components/gender_filter_toggle.tsx +3 -9
- package/src/screens/conversation_new/components/groups_form.tsx +33 -6
- package/src/screens/conversation_new/components/services_form.tsx +37 -6
- package/src/screens/conversation_new/conversation_new_screen.tsx +17 -3
- package/src/screens/team_conversation_screen.tsx +2 -1
- package/src/types/resources/chat_configuration_resource.ts +11 -0
- package/src/utils/auth_events.ts +21 -0
- package/src/utils/native_adapters/configuration.ts +10 -0
- package/src/utils/native_adapters/document_picker.ts +26 -0
- package/src/utils/native_adapters/image_picker.ts +8 -1
- package/src/utils/native_adapters/index.ts +1 -0
- package/src/utils/request/get_chat_configuration.ts +23 -0
- package/build/hooks/attachments/supported_extensions.d.ts +0 -2
- package/build/hooks/attachments/supported_extensions.d.ts.map +0 -1
- package/build/hooks/attachments/supported_extensions.js +0 -48
- package/build/hooks/attachments/supported_extensions.js.map +0 -1
- package/src/hooks/attachments/supported_extensions.ts +0 -47
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
|
+
import { StyleSheet, View } from 'react-native'
|
|
3
|
+
import { EmojiKeyboard, type EmojiType } from 'rn-emoji-keyboard'
|
|
4
|
+
import { useTheme } from '../../hooks'
|
|
5
|
+
|
|
6
|
+
interface EmojiTabProps {
|
|
7
|
+
onEmojiSelect: (emoji: string) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function EmojiTab({ onEmojiSelect }: EmojiTabProps) {
|
|
11
|
+
const styles = useStyles()
|
|
12
|
+
const { colors } = useTheme()
|
|
13
|
+
|
|
14
|
+
const handleEmojiSelected = useCallback(
|
|
15
|
+
(emojiObject: EmojiType) => {
|
|
16
|
+
onEmojiSelect(emojiObject.emoji)
|
|
17
|
+
},
|
|
18
|
+
[onEmojiSelect]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const emojiTheme = {
|
|
22
|
+
container: colors.fillColorNeutral100Inverted,
|
|
23
|
+
header: colors.textColorDefaultSecondary,
|
|
24
|
+
knob: colors.fillColorNeutral040,
|
|
25
|
+
skinTonesContainer: colors.fillColorNeutral060,
|
|
26
|
+
category: {
|
|
27
|
+
icon: colors.textColorDefaultSecondary,
|
|
28
|
+
iconActive: colors.interaction,
|
|
29
|
+
container: colors.fillColorNeutral100Inverted,
|
|
30
|
+
containerActive: colors.fillColorNeutral080,
|
|
31
|
+
},
|
|
32
|
+
search: {
|
|
33
|
+
background: colors.fillColorNeutral080,
|
|
34
|
+
text: colors.textColorDefaultPrimary,
|
|
35
|
+
placeholder: colors.textColorDefaultSecondary,
|
|
36
|
+
icon: colors.textColorDefaultSecondary,
|
|
37
|
+
},
|
|
38
|
+
emoji: {
|
|
39
|
+
selected: colors.fillColorNeutral080,
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View style={styles.container}>
|
|
45
|
+
<EmojiKeyboard
|
|
46
|
+
categoryPosition="top"
|
|
47
|
+
onEmojiSelected={handleEmojiSelected}
|
|
48
|
+
enableSearchBar
|
|
49
|
+
enableRecentlyUsed
|
|
50
|
+
theme={emojiTheme}
|
|
51
|
+
styles={{
|
|
52
|
+
container: { borderRadius: 0 },
|
|
53
|
+
category: {
|
|
54
|
+
container: {
|
|
55
|
+
borderRadius: 0,
|
|
56
|
+
marginTop: -6,
|
|
57
|
+
borderBottomWidth: 1,
|
|
58
|
+
borderBottomColor: colors.borderColorDefaultDim,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
</View>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const useStyles = () => {
|
|
68
|
+
const { colors } = useTheme()
|
|
69
|
+
|
|
70
|
+
return StyleSheet.create({
|
|
71
|
+
container: {
|
|
72
|
+
flex: 1,
|
|
73
|
+
backgroundColor: colors.fillColorNeutral100Inverted,
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { IconName } from '@fortawesome/fontawesome-svg-core'
|
|
2
|
+
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'
|
|
3
|
+
import { PlatformPressable } from '@react-navigation/elements'
|
|
4
|
+
import React, { useCallback } from 'react'
|
|
5
|
+
import { FlatList, StyleSheet, useWindowDimensions } from 'react-native'
|
|
6
|
+
import { useTheme } from '../../hooks'
|
|
7
|
+
import { AVATAR_ICON_KEYS, GRID_COLUMNS } from './constants'
|
|
8
|
+
|
|
9
|
+
interface IconGridProps {
|
|
10
|
+
selectedIconKey: string | null
|
|
11
|
+
onIconSelect: (iconKey: string) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function IconGrid({ selectedIconKey, onIconSelect }: IconGridProps) {
|
|
15
|
+
const styles = useStyles()
|
|
16
|
+
|
|
17
|
+
const renderItem = useCallback(
|
|
18
|
+
({ item }: { item: string }) => {
|
|
19
|
+
const isSelected = item === selectedIconKey
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<PlatformPressable
|
|
23
|
+
onPress={() => onIconSelect(item)}
|
|
24
|
+
style={[styles.cell, isSelected && styles.cellSelected]}
|
|
25
|
+
accessibilityRole="button"
|
|
26
|
+
accessibilityLabel={`${item.replace(/-/g, ' ')} icon`}
|
|
27
|
+
accessibilityState={{ selected: isSelected }}
|
|
28
|
+
>
|
|
29
|
+
<FontAwesomeIcon icon={['fas', item as IconName]} size={20} color="white" />
|
|
30
|
+
</PlatformPressable>
|
|
31
|
+
)
|
|
32
|
+
},
|
|
33
|
+
[selectedIconKey, onIconSelect, styles.cell, styles.cellSelected]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<FlatList
|
|
38
|
+
data={AVATAR_ICON_KEYS}
|
|
39
|
+
numColumns={GRID_COLUMNS}
|
|
40
|
+
keyExtractor={item => item}
|
|
41
|
+
renderItem={renderItem}
|
|
42
|
+
contentContainerStyle={styles.grid}
|
|
43
|
+
columnWrapperStyle={styles.row}
|
|
44
|
+
style={styles.list}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const PADDING = 16
|
|
50
|
+
const GAP = 8
|
|
51
|
+
|
|
52
|
+
const useStyles = () => {
|
|
53
|
+
const { colors } = useTheme()
|
|
54
|
+
const { width: screenWidth } = useWindowDimensions()
|
|
55
|
+
const cellSize = Math.floor((screenWidth - PADDING * 2 - (GRID_COLUMNS - 1) * GAP) / GRID_COLUMNS)
|
|
56
|
+
|
|
57
|
+
return StyleSheet.create({
|
|
58
|
+
list: {
|
|
59
|
+
flex: 1,
|
|
60
|
+
},
|
|
61
|
+
grid: {
|
|
62
|
+
padding: PADDING,
|
|
63
|
+
},
|
|
64
|
+
row: {
|
|
65
|
+
gap: GAP,
|
|
66
|
+
marginBottom: GAP,
|
|
67
|
+
},
|
|
68
|
+
cell: {
|
|
69
|
+
width: cellSize,
|
|
70
|
+
height: cellSize,
|
|
71
|
+
borderRadius: cellSize / 2,
|
|
72
|
+
backgroundColor: colors.fillColorNeutral040,
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
justifyContent: 'center',
|
|
75
|
+
},
|
|
76
|
+
cellSelected: {
|
|
77
|
+
borderWidth: 3,
|
|
78
|
+
borderColor: colors.interaction,
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
|
+
import { Alert, StyleSheet, View } from 'react-native'
|
|
3
|
+
import { Button } from '../../components'
|
|
4
|
+
import { useTheme } from '../../hooks'
|
|
5
|
+
import { ImagePicker } from '../../utils/native_adapters'
|
|
6
|
+
import type { ImagePickerAsset } from '../../utils/native_adapters/image_picker'
|
|
7
|
+
|
|
8
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB
|
|
9
|
+
|
|
10
|
+
interface UploadTabProps {
|
|
11
|
+
imagePreviewUri: string | null
|
|
12
|
+
onImageSelect: (asset: ImagePickerAsset) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function UploadTab({ imagePreviewUri, onImageSelect }: UploadTabProps) {
|
|
16
|
+
const styles = useStyles()
|
|
17
|
+
|
|
18
|
+
const pickImage = useCallback(async () => {
|
|
19
|
+
const result = await ImagePicker.openImageLibraryAsync({
|
|
20
|
+
mediaTypes: ['images'],
|
|
21
|
+
allowsEditing: true,
|
|
22
|
+
allowsMultipleSelection: false,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
if (result.canceled || !result.assets?.[0]) return
|
|
26
|
+
|
|
27
|
+
const asset = result.assets[0]
|
|
28
|
+
|
|
29
|
+
if (asset.fileSize && asset.fileSize > MAX_FILE_SIZE) {
|
|
30
|
+
Alert.alert('Image too large', 'Please choose an image under 10MB.')
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
onImageSelect(asset)
|
|
35
|
+
}, [onImageSelect])
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<View style={styles.container}>
|
|
39
|
+
<Button
|
|
40
|
+
title={imagePreviewUri ? 'Change photo' : 'Choose photo'}
|
|
41
|
+
iconNameLeft="general.image"
|
|
42
|
+
onPress={pickImage}
|
|
43
|
+
variant="outline"
|
|
44
|
+
appearance="interaction"
|
|
45
|
+
size="md"
|
|
46
|
+
/>
|
|
47
|
+
</View>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const useStyles = () => {
|
|
52
|
+
const { colors } = useTheme()
|
|
53
|
+
|
|
54
|
+
return StyleSheet.create({
|
|
55
|
+
container: {
|
|
56
|
+
flex: 1,
|
|
57
|
+
alignItems: 'center',
|
|
58
|
+
justifyContent: 'center',
|
|
59
|
+
backgroundColor: colors.fillColorNeutral100Inverted,
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from 'react-native'
|
|
27
27
|
import {
|
|
28
28
|
Badge,
|
|
29
|
+
Button,
|
|
29
30
|
ChildNotice,
|
|
30
31
|
Heading,
|
|
31
32
|
Icon,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
TextButton,
|
|
36
37
|
type TextStyle,
|
|
37
38
|
} from '../components'
|
|
39
|
+
import { ConversationAvatar } from '../components/display/conversation_avatar'
|
|
38
40
|
import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
|
|
39
41
|
import { ButtonAppearanceUnion } from '../components/display/utils/button_colors'
|
|
40
42
|
import { useSuspensePaginator, useTheme } from '../hooks'
|
|
@@ -46,7 +48,7 @@ import {
|
|
|
46
48
|
useConversationUpdate,
|
|
47
49
|
} from '../hooks/use_conversation'
|
|
48
50
|
import { availableFeatures, useFeatures } from '../hooks/use_features'
|
|
49
|
-
import { MemberResource, isDefined } from '../types'
|
|
51
|
+
import { type ConversationResource, MemberResource, isDefined } from '../types'
|
|
50
52
|
import { GroupResource } from '../types/resources/group_resource'
|
|
51
53
|
import { genderDisplayLabel } from '../utils/gender_display_label'
|
|
52
54
|
import { tokens } from '../vendor/tapestry/tokens'
|
|
@@ -102,6 +104,7 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
|
|
|
102
104
|
const { mutate: deleteConversation } = useConversationDelete(route.params)
|
|
103
105
|
const { featureEnabled } = useFeatures()
|
|
104
106
|
const granularNotificationsEnabled = featureEnabled(availableFeatures.granular_notifications_ui)
|
|
107
|
+
const customAvatarsEnabled = featureEnabled(availableFeatures.custom_conversation_avatars)
|
|
105
108
|
const showGenderFilter =
|
|
106
109
|
featureEnabled(availableFeatures.gender_specific_conversations) && !!conversation.genderOption
|
|
107
110
|
const genderLabel = conversation.genderOption ? genderDisplayLabel(conversation.genderOption) : ''
|
|
@@ -234,6 +237,25 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
|
|
|
234
237
|
}, [HeaderRight, HeaderTitle, navigation])
|
|
235
238
|
|
|
236
239
|
const listData = [
|
|
240
|
+
{
|
|
241
|
+
type: canUpdate && customAvatarsEnabled ? SectionTypes.view : SectionTypes.hidden,
|
|
242
|
+
data: {
|
|
243
|
+
children: (
|
|
244
|
+
<AvatarCard
|
|
245
|
+
conversation={conversation}
|
|
246
|
+
onPress={() => navigation.navigate('AvatarPicker', { conversation_id })}
|
|
247
|
+
/>
|
|
248
|
+
),
|
|
249
|
+
},
|
|
250
|
+
sectionOuterStyle: styles.sectionOuterAvatarCard,
|
|
251
|
+
sectionInnerStyle: styles.sectionInnerAvatarCard,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: SectionTypes.header,
|
|
255
|
+
data: { title: 'Basic info' },
|
|
256
|
+
showBottomBorder: true,
|
|
257
|
+
sectionInnerStyle: styles.sectionInnerHeaderWithBottomBorder,
|
|
258
|
+
},
|
|
237
259
|
{
|
|
238
260
|
type: SectionTypes.view,
|
|
239
261
|
data: {
|
|
@@ -595,6 +617,30 @@ function NavigableSettingRow({ title, subtitle, onPress }: NavigableSettingRowPr
|
|
|
595
617
|
)
|
|
596
618
|
}
|
|
597
619
|
|
|
620
|
+
function AvatarCard({
|
|
621
|
+
conversation,
|
|
622
|
+
onPress,
|
|
623
|
+
}: {
|
|
624
|
+
conversation: ConversationResource
|
|
625
|
+
onPress: () => void
|
|
626
|
+
}) {
|
|
627
|
+
const styles = useStyles()
|
|
628
|
+
|
|
629
|
+
return (
|
|
630
|
+
<View style={styles.avatarCard}>
|
|
631
|
+
<ConversationAvatar conversation={conversation} size="2xl" />
|
|
632
|
+
<Button
|
|
633
|
+
title="Update avatar"
|
|
634
|
+
iconNameLeft="general.pencil"
|
|
635
|
+
onPress={onPress}
|
|
636
|
+
variant="outline"
|
|
637
|
+
appearance="interaction"
|
|
638
|
+
size="sm"
|
|
639
|
+
/>
|
|
640
|
+
</View>
|
|
641
|
+
)
|
|
642
|
+
}
|
|
643
|
+
|
|
598
644
|
function TeamsGroup({ teams }: { teams: GroupResource[] }) {
|
|
599
645
|
const styles = useStyles()
|
|
600
646
|
|
|
@@ -725,6 +771,19 @@ const useStyles = ({ isStart, isEnd }: { isStart?: boolean; isEnd?: boolean } =
|
|
|
725
771
|
navigableSettingChevron: {
|
|
726
772
|
color: colors.iconColorDefaultDisabled,
|
|
727
773
|
},
|
|
774
|
+
avatarCard: {
|
|
775
|
+
alignItems: 'center',
|
|
776
|
+
gap: 16,
|
|
777
|
+
},
|
|
778
|
+
sectionOuterAvatarCard: {
|
|
779
|
+
paddingLeft: 0,
|
|
780
|
+
},
|
|
781
|
+
sectionInnerAvatarCard: {
|
|
782
|
+
paddingTop: 24,
|
|
783
|
+
paddingBottom: 24,
|
|
784
|
+
paddingHorizontal: 16,
|
|
785
|
+
alignItems: 'center',
|
|
786
|
+
},
|
|
728
787
|
teamGroup: {
|
|
729
788
|
flexDirection: 'row',
|
|
730
789
|
gap: 4,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useNavigation, useRoute } from '@react-navigation/native'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Pressable, StyleSheet, View } from 'react-native'
|
|
4
|
+
import { Icon, Text } from '../../../components'
|
|
5
|
+
import { EmojiAvatar } from '../../../components/display/emoji_avatar'
|
|
6
|
+
import { IconAvatar } from '../../../components/display/icon_avatar'
|
|
7
|
+
import AvatarPrimitive from '../../../components/primitive/avatar_primitive'
|
|
8
|
+
import { useTheme } from '../../../hooks'
|
|
9
|
+
import type { AvatarUpdatePayload } from '../../../hooks/use_conversation_avatar_update'
|
|
10
|
+
|
|
11
|
+
interface AvatarSelectionRowProps {
|
|
12
|
+
avatarSelection?: AvatarUpdatePayload
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function AvatarSelectionRow({ avatarSelection }: AvatarSelectionRowProps) {
|
|
16
|
+
const styles = useStyles()
|
|
17
|
+
const navigation = useNavigation()
|
|
18
|
+
const route = useRoute()
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Pressable
|
|
22
|
+
style={styles.avatarSection}
|
|
23
|
+
onPress={() =>
|
|
24
|
+
navigation.navigate('AvatarPicker', {
|
|
25
|
+
source_params: route.params as Record<string, unknown>,
|
|
26
|
+
...(avatarSelection && { avatar_selection: avatarSelection }),
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
>
|
|
30
|
+
<Text style={styles.avatarLabel}>Customize avatar</Text>
|
|
31
|
+
<View style={styles.avatarSectionTrailing}>
|
|
32
|
+
<AvatarSelectionPreview avatarSelection={avatarSelection} />
|
|
33
|
+
<Icon name="general.rightChevron" size={12} />
|
|
34
|
+
</View>
|
|
35
|
+
</Pressable>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface AvatarSelectionPreviewProps {
|
|
40
|
+
avatarSelection?: AvatarUpdatePayload
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function AvatarSelectionPreview({ avatarSelection }: AvatarSelectionPreviewProps) {
|
|
44
|
+
if (!avatarSelection || avatarSelection.kind === 'clear') return null
|
|
45
|
+
|
|
46
|
+
switch (avatarSelection.kind) {
|
|
47
|
+
case 'icon':
|
|
48
|
+
return <IconAvatar iconKey={avatarSelection.key} color={avatarSelection.color} size="md" />
|
|
49
|
+
case 'emoji':
|
|
50
|
+
return <EmojiAvatar emoji={avatarSelection.key} color={avatarSelection.color} size="md" />
|
|
51
|
+
case 'image':
|
|
52
|
+
return (
|
|
53
|
+
<AvatarPrimitive.Root size="md">
|
|
54
|
+
<AvatarPrimitive.Mask>
|
|
55
|
+
<AvatarPrimitive.Image sourceUri={avatarSelection.imageAsset.uri} />
|
|
56
|
+
</AvatarPrimitive.Mask>
|
|
57
|
+
</AvatarPrimitive.Root>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const useStyles = () => {
|
|
63
|
+
const { colors } = useTheme()
|
|
64
|
+
|
|
65
|
+
return StyleSheet.create({
|
|
66
|
+
avatarSection: {
|
|
67
|
+
padding: 16,
|
|
68
|
+
flexDirection: 'row',
|
|
69
|
+
alignItems: 'center',
|
|
70
|
+
justifyContent: 'space-between',
|
|
71
|
+
},
|
|
72
|
+
avatarSectionTrailing: {
|
|
73
|
+
flexDirection: 'row',
|
|
74
|
+
alignItems: 'center',
|
|
75
|
+
gap: 12,
|
|
76
|
+
},
|
|
77
|
+
avatarLabel: {
|
|
78
|
+
fontSize: 18,
|
|
79
|
+
color: colors.textColorDefaultPrimary,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { ActivityIndicator, StyleSheet, View } from 'react-native'
|
|
3
|
-
import { Heading, Switch, Text
|
|
3
|
+
import { Heading, Switch, Text } from '../../../components'
|
|
4
4
|
import { useTheme } from '../../../hooks'
|
|
5
5
|
import { genderDisplayLabel } from '../../../utils/gender_display_label'
|
|
6
6
|
|
|
@@ -26,10 +26,7 @@ function FilterableGenderContent({ genderValue, enabled, onToggle }: FilterableG
|
|
|
26
26
|
/>
|
|
27
27
|
</View>
|
|
28
28
|
<Text style={{ color: colors.textColorDefaultSecondary }}>
|
|
29
|
-
Filter limited to your profile's set gender.
|
|
30
|
-
<TextInlineButton nativeID="gender-filter-learn-more" onPress={() => {}}>
|
|
31
|
-
Learn more.
|
|
32
|
-
</TextInlineButton>
|
|
29
|
+
Filter limited to your profile's set gender.
|
|
33
30
|
</Text>
|
|
34
31
|
</>
|
|
35
32
|
)
|
|
@@ -45,10 +42,7 @@ function NoGenderContent({ isFetching }: { isFetching: boolean }) {
|
|
|
45
42
|
<ActivityIndicator size="small" />
|
|
46
43
|
) : (
|
|
47
44
|
<Text style={{ color: colors.textColorDefaultSecondary }}>
|
|
48
|
-
Set a gender in your Church Center profile to enable gender filtering.
|
|
49
|
-
<TextInlineButton nativeID="gender-filter-learn-more" onPress={() => {}}>
|
|
50
|
-
Learn more.
|
|
51
|
-
</TextInlineButton>
|
|
45
|
+
Set a gender in your Church Center profile to enable gender filtering.
|
|
52
46
|
</Text>
|
|
53
47
|
)}
|
|
54
48
|
</>
|
|
@@ -4,30 +4,42 @@ import { Platform, Pressable, StyleSheet, TextInput, View } from 'react-native'
|
|
|
4
4
|
import { Banner, ChildNotice, Heading, Text } from '../../../components'
|
|
5
5
|
import { ActionButton } from '../../../components/display/action_button'
|
|
6
6
|
import { KeyboardView } from '../../../components/display/keyboard_view'
|
|
7
|
-
import { useCurrentPerson, useSuspenseGet, useTheme } from '../../../hooks'
|
|
7
|
+
import { useApiClient, useCurrentPerson, useSuspenseGet, useTheme } from '../../../hooks'
|
|
8
8
|
import {
|
|
9
9
|
GroupMembersForNewConversationResult,
|
|
10
10
|
useGroupMembersForNewConversation,
|
|
11
11
|
} from '../../../hooks/groups/use_group_members_for_new_conversation'
|
|
12
12
|
import { useGroupsConversationCreate } from '../../../hooks/groups/use_groups_conversation_create'
|
|
13
|
+
import {
|
|
14
|
+
type AvatarUpdatePayload,
|
|
15
|
+
patchConversationAvatar,
|
|
16
|
+
} from '../../../hooks/use_conversation_avatar_update'
|
|
17
|
+
import { availableFeatures, useFeatures } from '../../../hooks/use_features'
|
|
13
18
|
import { useMyGender } from '../../../hooks/use_my_gender'
|
|
19
|
+
import { useUploadClient } from '../../../hooks/use_upload_client'
|
|
14
20
|
import { GroupsGroupResource } from '../../../types'
|
|
15
21
|
import { GraphId } from '../../../types/resources/group_resource'
|
|
16
22
|
import { pluralize } from '../../../utils'
|
|
17
23
|
import { genderDisplayLabel } from '../../../utils/gender_display_label'
|
|
18
24
|
import { Haptic } from '../../../utils/native_adapters'
|
|
25
|
+
import { AvatarSelectionRow } from './avatar_selection_row'
|
|
19
26
|
import { Divider, FormList } from './form_list'
|
|
20
27
|
import { GenderFilterToggle } from './gender_filter_toggle'
|
|
21
28
|
|
|
22
29
|
type GroupsFormProps = {
|
|
23
30
|
groupId: number
|
|
24
31
|
chat_group_graph_id?: GraphId
|
|
32
|
+
avatarSelection?: AvatarUpdatePayload
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
export const GroupsForm = ({ groupId, chat_group_graph_id }: GroupsFormProps) => {
|
|
35
|
+
export const GroupsForm = ({ groupId, chat_group_graph_id, avatarSelection }: GroupsFormProps) => {
|
|
28
36
|
const navigation = useNavigation()
|
|
37
|
+
const apiClient = useApiClient()
|
|
38
|
+
const uploadClient = useUploadClient()
|
|
29
39
|
const [title, setTitle] = useState('')
|
|
30
40
|
const [genderFilterEnabled, setGenderFilterEnabled] = useState(false)
|
|
41
|
+
const { featureEnabled } = useFeatures()
|
|
42
|
+
const customAvatarsEnabled = featureEnabled(availableFeatures.custom_conversation_avatars)
|
|
31
43
|
const {
|
|
32
44
|
isFeatureEnabled: genderFilterAvailable,
|
|
33
45
|
genderId,
|
|
@@ -67,9 +79,8 @@ export const GroupsForm = ({ groupId, chat_group_graph_id }: GroupsFormProps) =>
|
|
|
67
79
|
}
|
|
68
80
|
}
|
|
69
81
|
|
|
70
|
-
const
|
|
82
|
+
const handleCreateSuccess = useCallback(
|
|
71
83
|
(conversationId: number) => {
|
|
72
|
-
// navigate to the conversation screen
|
|
73
84
|
navigation.dispatch(
|
|
74
85
|
StackActions.popTo('Conversation', {
|
|
75
86
|
conversation_id: conversationId,
|
|
@@ -77,15 +88,19 @@ export const GroupsForm = ({ groupId, chat_group_graph_id }: GroupsFormProps) =>
|
|
|
77
88
|
})
|
|
78
89
|
)
|
|
79
90
|
Haptic.notificationSuccess()
|
|
91
|
+
|
|
92
|
+
if (avatarSelection && avatarSelection.kind !== 'clear') {
|
|
93
|
+
patchConversationAvatar(apiClient, uploadClient, conversationId, avatarSelection)
|
|
94
|
+
}
|
|
80
95
|
},
|
|
81
|
-
[chat_group_graph_id, navigation]
|
|
96
|
+
[apiClient, avatarSelection, chat_group_graph_id, navigation, uploadClient]
|
|
82
97
|
)
|
|
83
98
|
|
|
84
99
|
const { mutate: handleSave, isPending } = useGroupsConversationCreate({
|
|
85
100
|
groupId,
|
|
86
101
|
title,
|
|
87
102
|
genderId: activeGenderId,
|
|
88
|
-
onSuccess:
|
|
103
|
+
onSuccess: handleCreateSuccess,
|
|
89
104
|
})
|
|
90
105
|
|
|
91
106
|
return (
|
|
@@ -106,6 +121,8 @@ export const GroupsForm = ({ groupId, chat_group_graph_id }: GroupsFormProps) =>
|
|
|
106
121
|
onGenderToggle={handleGenderToggle}
|
|
107
122
|
groupMemberships={groupMemberships}
|
|
108
123
|
filteredMemberCount={groupMemberships.adultMembers.length}
|
|
124
|
+
avatarSelection={avatarSelection}
|
|
125
|
+
customAvatarsEnabled={customAvatarsEnabled}
|
|
109
126
|
/>
|
|
110
127
|
}
|
|
111
128
|
/>
|
|
@@ -130,6 +147,8 @@ interface FormContentProps {
|
|
|
130
147
|
onGenderToggle: (enabled: boolean) => void
|
|
131
148
|
groupMemberships: GroupMembersForNewConversationResult
|
|
132
149
|
filteredMemberCount: number
|
|
150
|
+
avatarSelection?: AvatarUpdatePayload
|
|
151
|
+
customAvatarsEnabled: boolean
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
function FormContent({
|
|
@@ -143,6 +162,8 @@ function FormContent({
|
|
|
143
162
|
onGenderToggle,
|
|
144
163
|
groupMemberships,
|
|
145
164
|
filteredMemberCount,
|
|
165
|
+
avatarSelection,
|
|
166
|
+
customAvatarsEnabled,
|
|
146
167
|
}: FormContentProps) {
|
|
147
168
|
const styles = useStyles()
|
|
148
169
|
const inputRef = useRef<TextInput>(null)
|
|
@@ -185,6 +206,12 @@ function FormContent({
|
|
|
185
206
|
/>
|
|
186
207
|
</Pressable>
|
|
187
208
|
<Divider />
|
|
209
|
+
{customAvatarsEnabled && (
|
|
210
|
+
<>
|
|
211
|
+
<AvatarSelectionRow avatarSelection={avatarSelection} />
|
|
212
|
+
<Divider />
|
|
213
|
+
</>
|
|
214
|
+
)}
|
|
188
215
|
{genderFilterAvailable && (
|
|
189
216
|
<>
|
|
190
217
|
<GenderFilterToggle
|
|
@@ -4,14 +4,22 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
|
|
4
4
|
import { StyleSheet, View } from 'react-native'
|
|
5
5
|
import { Badge, Banner, ChildNotice, Heading, Switch, TextButton } from '../../../components'
|
|
6
6
|
import { ActionButton } from '../../../components/display/action_button'
|
|
7
|
+
import { useApiClient } from '../../../hooks'
|
|
7
8
|
import { useFindOrCreateServicesConversation } from '../../../hooks/services/use_find_or_create_services_conversation'
|
|
8
9
|
import { useServicesTeams } from '../../../hooks/services/use_services_team'
|
|
9
10
|
import { useTeamMembersForNewConversation } from '../../../hooks/services/use_team_members_for_new_conversation'
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type AvatarUpdatePayload,
|
|
13
|
+
patchConversationAvatar,
|
|
14
|
+
} from '../../../hooks/use_conversation_avatar_update'
|
|
15
|
+
import { availableFeatures, useFeatures } from '../../../hooks/use_features'
|
|
16
|
+
import { useUploadClient } from '../../../hooks/use_upload_client'
|
|
17
|
+
import { MemberResource } from '../../../types'
|
|
11
18
|
import { pluralize } from '../../../utils'
|
|
12
19
|
import { Haptic } from '../../../utils/native_adapters'
|
|
13
20
|
import { tokens } from '../../../vendor/tapestry/tokens'
|
|
14
21
|
import { TeamFilterTypes } from '../../conversation_filter_recipients/types'
|
|
22
|
+
import { AvatarSelectionRow } from './avatar_selection_row'
|
|
15
23
|
import { FilterByPlan } from './filter_by_plan'
|
|
16
24
|
import { Divider, FormList } from './form_list'
|
|
17
25
|
|
|
@@ -19,14 +27,20 @@ type ServicesFormProps = {
|
|
|
19
27
|
initialTeamIds?: number[]
|
|
20
28
|
initialPlanId?: number
|
|
21
29
|
teamFilterType?: TeamFilterTypes
|
|
30
|
+
avatarSelection?: AvatarUpdatePayload
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
export const ServicesForm = ({
|
|
25
34
|
initialTeamIds,
|
|
26
35
|
initialPlanId,
|
|
27
36
|
teamFilterType,
|
|
37
|
+
avatarSelection,
|
|
28
38
|
}: ServicesFormProps) => {
|
|
29
39
|
const styles = useStyles()
|
|
40
|
+
const apiClient = useApiClient()
|
|
41
|
+
const uploadClient = useUploadClient()
|
|
42
|
+
const { featureEnabled } = useFeatures()
|
|
43
|
+
const customAvatarsEnabled = featureEnabled(availableFeatures.custom_conversation_avatars)
|
|
30
44
|
const [selectedPlanId, setSelectedPlanId] = useState<number | undefined>(initialPlanId)
|
|
31
45
|
const initialState = useMemo(() => uniq(initialTeamIds) || [], [initialTeamIds]) // Uniq here because services can send duplicates in the teams_i_lead response.
|
|
32
46
|
const [selectedTeamIds, setSelectedTeamIds] = useState<number[]>(initialState)
|
|
@@ -61,17 +75,19 @@ export const ServicesForm = ({
|
|
|
61
75
|
} = useFindOrCreateServicesConversation({
|
|
62
76
|
teamIds: selectedTeamIds,
|
|
63
77
|
planId: filerByPlan ? selectedPlanId : undefined,
|
|
64
|
-
onSuccess: (conversation
|
|
65
|
-
// exit from the create stack
|
|
78
|
+
onSuccess: (conversation, { created }) => {
|
|
66
79
|
navigation.getParent()?.goBack()
|
|
67
|
-
// navigate to the conversation screen
|
|
68
80
|
navigation.dispatch(
|
|
69
81
|
StackActions.push('Conversation', {
|
|
70
82
|
conversation_id: conversation.id,
|
|
71
83
|
})
|
|
72
84
|
)
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
if (created) {
|
|
86
|
+
Haptic.notificationSuccess()
|
|
87
|
+
if (avatarSelection && avatarSelection.kind !== 'clear') {
|
|
88
|
+
patchConversationAvatar(apiClient, uploadClient, conversation.id, avatarSelection)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
75
91
|
},
|
|
76
92
|
})
|
|
77
93
|
|
|
@@ -90,6 +106,9 @@ export const ServicesForm = ({
|
|
|
90
106
|
members={members}
|
|
91
107
|
isMemberError={isMemberError}
|
|
92
108
|
teamFilterType={teamFilterType}
|
|
109
|
+
avatarSelection={avatarSelection}
|
|
110
|
+
selectionHasConversation={selectionHasConversation}
|
|
111
|
+
customAvatarsEnabled={customAvatarsEnabled}
|
|
93
112
|
/>
|
|
94
113
|
}
|
|
95
114
|
/>
|
|
@@ -114,6 +133,9 @@ interface FormContentProps {
|
|
|
114
133
|
members: MemberResource[]
|
|
115
134
|
isMemberError: boolean
|
|
116
135
|
teamFilterType?: TeamFilterTypes
|
|
136
|
+
avatarSelection?: AvatarUpdatePayload
|
|
137
|
+
selectionHasConversation: boolean
|
|
138
|
+
customAvatarsEnabled: boolean
|
|
117
139
|
}
|
|
118
140
|
|
|
119
141
|
function FormContent({
|
|
@@ -126,6 +148,9 @@ function FormContent({
|
|
|
126
148
|
members,
|
|
127
149
|
isMemberError,
|
|
128
150
|
teamFilterType,
|
|
151
|
+
avatarSelection,
|
|
152
|
+
selectionHasConversation,
|
|
153
|
+
customAvatarsEnabled,
|
|
129
154
|
}: FormContentProps) {
|
|
130
155
|
const navigation = useNavigation()
|
|
131
156
|
const servicesTeams = useServicesTeams()
|
|
@@ -205,6 +230,12 @@ function FormContent({
|
|
|
205
230
|
)}
|
|
206
231
|
</View>
|
|
207
232
|
<Divider />
|
|
233
|
+
{!selectionHasConversation && customAvatarsEnabled && (
|
|
234
|
+
<>
|
|
235
|
+
<AvatarSelectionRow avatarSelection={avatarSelection} />
|
|
236
|
+
<Divider />
|
|
237
|
+
</>
|
|
238
|
+
)}
|
|
208
239
|
<View style={styles.memberSection}>
|
|
209
240
|
<Heading variant="h3">{pluralize(memberCount, 'member')} selected</Heading>
|
|
210
241
|
{hasChildren && (
|