@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.
Files changed (184) hide show
  1. package/build/components/conversation/message_form.d.ts.map +1 -1
  2. package/build/components/conversation/message_form.js +22 -1
  3. package/build/components/conversation/message_form.js.map +1 -1
  4. package/build/components/display/emoji_avatar.d.ts.map +1 -1
  5. package/build/components/display/emoji_avatar.js +2 -0
  6. package/build/components/display/emoji_avatar.js.map +1 -1
  7. package/build/components/display/icon_avatar.d.ts.map +1 -1
  8. package/build/components/display/icon_avatar.js +2 -0
  9. package/build/components/display/icon_avatar.js.map +1 -1
  10. package/build/components/display/utils/avatar_gradient_colors.d.ts +3 -0
  11. package/build/components/display/utils/avatar_gradient_colors.d.ts.map +1 -1
  12. package/build/components/display/utils/avatar_gradient_colors.js +8 -3
  13. package/build/components/display/utils/avatar_gradient_colors.js.map +1 -1
  14. package/build/components/page/error_boundary.d.ts.map +1 -1
  15. package/build/components/page/error_boundary.js +13 -10
  16. package/build/components/page/error_boundary.js.map +1 -1
  17. package/build/components/primitive/avatar_primitive.d.ts +3 -1
  18. package/build/components/primitive/avatar_primitive.d.ts.map +1 -1
  19. package/build/components/primitive/avatar_primitive.js +10 -2
  20. package/build/components/primitive/avatar_primitive.js.map +1 -1
  21. package/build/contexts/api_provider.d.ts.map +1 -1
  22. package/build/contexts/api_provider.js +2 -0
  23. package/build/contexts/api_provider.js.map +1 -1
  24. package/build/hooks/attachments/fallback_chat_configuration.d.ts +4 -0
  25. package/build/hooks/attachments/fallback_chat_configuration.d.ts.map +1 -0
  26. package/build/hooks/attachments/fallback_chat_configuration.js +59 -0
  27. package/build/hooks/attachments/fallback_chat_configuration.js.map +1 -0
  28. package/build/hooks/groups/use_groups_conversation_create.d.ts.map +1 -1
  29. package/build/hooks/groups/use_groups_conversation_create.js +1 -1
  30. package/build/hooks/groups/use_groups_conversation_create.js.map +1 -1
  31. package/build/hooks/services/use_find_or_create_services_conversation.d.ts +43 -11
  32. package/build/hooks/services/use_find_or_create_services_conversation.d.ts.map +1 -1
  33. package/build/hooks/services/use_find_or_create_services_conversation.js +5 -5
  34. package/build/hooks/services/use_find_or_create_services_conversation.js.map +1 -1
  35. package/build/hooks/use_attachment_uploader.d.ts.map +1 -1
  36. package/build/hooks/use_attachment_uploader.js +39 -14
  37. package/build/hooks/use_attachment_uploader.js.map +1 -1
  38. package/build/hooks/use_chat_configuration.d.ts +6 -0
  39. package/build/hooks/use_chat_configuration.d.ts.map +1 -0
  40. package/build/hooks/use_chat_configuration.js +41 -0
  41. package/build/hooks/use_chat_configuration.js.map +1 -0
  42. package/build/hooks/use_conversation_avatar_update.d.ts +26 -0
  43. package/build/hooks/use_conversation_avatar_update.d.ts.map +1 -0
  44. package/build/hooks/use_conversation_avatar_update.js +130 -0
  45. package/build/hooks/use_conversation_avatar_update.js.map +1 -0
  46. package/build/hooks/use_features.d.ts +1 -0
  47. package/build/hooks/use_features.d.ts.map +1 -1
  48. package/build/hooks/use_features.js +1 -0
  49. package/build/hooks/use_features.js.map +1 -1
  50. package/build/navigation/index.d.ts +16 -0
  51. package/build/navigation/index.d.ts.map +1 -1
  52. package/build/navigation/index.js +9 -0
  53. package/build/navigation/index.js.map +1 -1
  54. package/build/screens/avatar_picker/avatar_picker_screen.d.ts +12 -0
  55. package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -0
  56. package/build/screens/avatar_picker/avatar_picker_screen.js +193 -0
  57. package/build/screens/avatar_picker/avatar_picker_screen.js.map +1 -0
  58. package/build/screens/avatar_picker/avatar_picker_state.d.ts +38 -0
  59. package/build/screens/avatar_picker/avatar_picker_state.d.ts.map +1 -0
  60. package/build/screens/avatar_picker/avatar_picker_state.js +101 -0
  61. package/build/screens/avatar_picker/avatar_picker_state.js.map +1 -0
  62. package/build/screens/avatar_picker/avatar_preview.d.ts +9 -0
  63. package/build/screens/avatar_picker/avatar_preview.d.ts.map +1 -0
  64. package/build/screens/avatar_picker/avatar_preview.js +39 -0
  65. package/build/screens/avatar_picker/avatar_preview.js.map +1 -0
  66. package/build/screens/avatar_picker/color_picker.d.ts +9 -0
  67. package/build/screens/avatar_picker/color_picker.d.ts.map +1 -0
  68. package/build/screens/avatar_picker/color_picker.js +53 -0
  69. package/build/screens/avatar_picker/color_picker.js.map +1 -0
  70. package/build/screens/avatar_picker/constants.d.ts +3 -0
  71. package/build/screens/avatar_picker/constants.d.ts.map +1 -0
  72. package/build/screens/avatar_picker/constants.js +53 -0
  73. package/build/screens/avatar_picker/constants.js.map +1 -0
  74. package/build/screens/avatar_picker/emoji_tab.d.ts +7 -0
  75. package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -0
  76. package/build/screens/avatar_picker/emoji_tab.js +55 -0
  77. package/build/screens/avatar_picker/emoji_tab.js.map +1 -0
  78. package/build/screens/avatar_picker/icon_grid.d.ts +8 -0
  79. package/build/screens/avatar_picker/icon_grid.d.ts.map +1 -0
  80. package/build/screens/avatar_picker/icon_grid.js +48 -0
  81. package/build/screens/avatar_picker/icon_grid.js.map +1 -0
  82. package/build/screens/avatar_picker/upload_tab.d.ts +9 -0
  83. package/build/screens/avatar_picker/upload_tab.d.ts.map +1 -0
  84. package/build/screens/avatar_picker/upload_tab.js +39 -0
  85. package/build/screens/avatar_picker/upload_tab.js.map +1 -0
  86. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  87. package/build/screens/conversation_details_screen.js +37 -1
  88. package/build/screens/conversation_details_screen.js.map +1 -1
  89. package/build/screens/conversation_new/components/avatar_selection_row.d.ts +12 -0
  90. package/build/screens/conversation_new/components/avatar_selection_row.d.ts.map +1 -0
  91. package/build/screens/conversation_new/components/avatar_selection_row.js +60 -0
  92. package/build/screens/conversation_new/components/avatar_selection_row.js.map +1 -0
  93. package/build/screens/conversation_new/components/gender_filter_toggle.d.ts.map +1 -1
  94. package/build/screens/conversation_new/components/gender_filter_toggle.js +3 -9
  95. package/build/screens/conversation_new/components/gender_filter_toggle.js.map +1 -1
  96. package/build/screens/conversation_new/components/groups_form.d.ts +3 -1
  97. package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
  98. package/build/screens/conversation_new/components/groups_form.js +22 -8
  99. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  100. package/build/screens/conversation_new/components/services_form.d.ts +3 -1
  101. package/build/screens/conversation_new/components/services_form.d.ts.map +1 -1
  102. package/build/screens/conversation_new/components/services_form.js +22 -8
  103. package/build/screens/conversation_new/components/services_form.js.map +1 -1
  104. package/build/screens/conversation_new/conversation_new_screen.d.ts +2 -0
  105. package/build/screens/conversation_new/conversation_new_screen.d.ts.map +1 -1
  106. package/build/screens/conversation_new/conversation_new_screen.js +3 -3
  107. package/build/screens/conversation_new/conversation_new_screen.js.map +1 -1
  108. package/build/screens/team_conversation_screen.d.ts.map +1 -1
  109. package/build/screens/team_conversation_screen.js +1 -1
  110. package/build/screens/team_conversation_screen.js.map +1 -1
  111. package/build/types/resources/chat_configuration_resource.d.ts +8 -0
  112. package/build/types/resources/chat_configuration_resource.d.ts.map +1 -0
  113. package/build/types/resources/chat_configuration_resource.js +2 -0
  114. package/build/types/resources/chat_configuration_resource.js.map +1 -0
  115. package/build/utils/auth_events.d.ts +7 -0
  116. package/build/utils/auth_events.d.ts.map +1 -0
  117. package/build/utils/auth_events.js +17 -0
  118. package/build/utils/auth_events.js.map +1 -0
  119. package/build/utils/native_adapters/configuration.d.ts +3 -0
  120. package/build/utils/native_adapters/configuration.d.ts.map +1 -1
  121. package/build/utils/native_adapters/configuration.js +8 -0
  122. package/build/utils/native_adapters/configuration.js.map +1 -1
  123. package/build/utils/native_adapters/document_picker.d.ts +21 -0
  124. package/build/utils/native_adapters/document_picker.d.ts.map +1 -0
  125. package/build/utils/native_adapters/document_picker.js +7 -0
  126. package/build/utils/native_adapters/document_picker.js.map +1 -0
  127. package/build/utils/native_adapters/image_picker.d.ts +7 -1
  128. package/build/utils/native_adapters/image_picker.d.ts.map +1 -1
  129. package/build/utils/native_adapters/image_picker.js.map +1 -1
  130. package/build/utils/native_adapters/index.d.ts +1 -0
  131. package/build/utils/native_adapters/index.d.ts.map +1 -1
  132. package/build/utils/native_adapters/index.js +1 -0
  133. package/build/utils/native_adapters/index.js.map +1 -1
  134. package/build/utils/request/get_chat_configuration.d.ts +10 -0
  135. package/build/utils/request/get_chat_configuration.d.ts.map +1 -0
  136. package/build/utils/request/get_chat_configuration.js +21 -0
  137. package/build/utils/request/get_chat_configuration.js.map +1 -0
  138. package/package.json +4 -3
  139. package/src/__tests__/hooks/use_attachment_uploader.test.tsx +219 -0
  140. package/src/__tests__/hooks/use_chat_configuration.test.tsx +80 -0
  141. package/src/__tests__/utils/native_adapters/configuration.ts +25 -1
  142. package/src/components/conversation/message_form.tsx +39 -1
  143. package/src/components/display/emoji_avatar.tsx +7 -2
  144. package/src/components/display/icon_avatar.tsx +7 -2
  145. package/src/components/display/utils/avatar_gradient_colors.ts +10 -3
  146. package/src/components/page/error_boundary.tsx +16 -9
  147. package/src/components/primitive/avatar_primitive.tsx +11 -2
  148. package/src/contexts/api_provider.tsx +3 -0
  149. package/src/hooks/attachments/fallback_chat_configuration.ts +61 -0
  150. package/src/hooks/groups/use_groups_conversation_create.ts +2 -1
  151. package/src/hooks/services/use_find_or_create_services_conversation.ts +7 -7
  152. package/src/hooks/use_attachment_uploader.ts +39 -15
  153. package/src/hooks/use_chat_configuration.ts +54 -0
  154. package/src/hooks/use_conversation_avatar_update.ts +163 -0
  155. package/src/hooks/use_features.ts +1 -0
  156. package/src/navigation/index.tsx +13 -0
  157. package/src/screens/avatar_picker/__tests__/avatar_picker_state.test.ts +157 -0
  158. package/src/screens/avatar_picker/avatar_picker_screen.tsx +312 -0
  159. package/src/screens/avatar_picker/avatar_picker_state.ts +141 -0
  160. package/src/screens/avatar_picker/avatar_preview.tsx +46 -0
  161. package/src/screens/avatar_picker/color_picker.tsx +91 -0
  162. package/src/screens/avatar_picker/constants.ts +53 -0
  163. package/src/screens/avatar_picker/emoji_tab.tsx +76 -0
  164. package/src/screens/avatar_picker/icon_grid.tsx +81 -0
  165. package/src/screens/avatar_picker/upload_tab.tsx +62 -0
  166. package/src/screens/conversation_details_screen.tsx +60 -1
  167. package/src/screens/conversation_new/components/avatar_selection_row.tsx +82 -0
  168. package/src/screens/conversation_new/components/gender_filter_toggle.tsx +3 -9
  169. package/src/screens/conversation_new/components/groups_form.tsx +33 -6
  170. package/src/screens/conversation_new/components/services_form.tsx +37 -6
  171. package/src/screens/conversation_new/conversation_new_screen.tsx +17 -3
  172. package/src/screens/team_conversation_screen.tsx +2 -1
  173. package/src/types/resources/chat_configuration_resource.ts +11 -0
  174. package/src/utils/auth_events.ts +21 -0
  175. package/src/utils/native_adapters/configuration.ts +10 -0
  176. package/src/utils/native_adapters/document_picker.ts +26 -0
  177. package/src/utils/native_adapters/image_picker.ts +8 -1
  178. package/src/utils/native_adapters/index.ts +1 -0
  179. package/src/utils/request/get_chat_configuration.ts +23 -0
  180. package/build/hooks/attachments/supported_extensions.d.ts +0 -2
  181. package/build/hooks/attachments/supported_extensions.d.ts.map +0 -1
  182. package/build/hooks/attachments/supported_extensions.js +0 -48
  183. package/build/hooks/attachments/supported_extensions.js.map +0 -1
  184. package/src/hooks/attachments/supported_extensions.ts +0 -47
@@ -0,0 +1,53 @@
1
+ import { PlatformPressable } from '@react-navigation/elements';
2
+ import React, { useCallback } from 'react';
3
+ import { FlatList, StyleSheet, useWindowDimensions } from 'react-native';
4
+ import LinearGradient from 'react-native-linear-gradient';
5
+ import { COLOR_KEYS, getAvatarGradientProps, } from '../../components/display/utils/avatar_gradient_colors';
6
+ import { useTheme } from '../../hooks';
7
+ import { GRID_COLUMNS } from './constants';
8
+ export function ColorPicker({ selectedColor, onColorSelect }) {
9
+ const styles = useStyles();
10
+ const renderItem = useCallback(({ item }) => {
11
+ const gradientProps = getAvatarGradientProps(item);
12
+ const isSelected = item === selectedColor;
13
+ return (<PlatformPressable onPress={() => onColorSelect(item)} style={[styles.swatchOuter, isSelected && styles.swatchSelected]} accessibilityRole="button" accessibilityLabel={`${item.replace(/-/g, ' ')} color`} accessibilityState={{ selected: isSelected }}>
14
+ <LinearGradient {...gradientProps} style={styles.swatch}/>
15
+ </PlatformPressable>);
16
+ }, [selectedColor, onColorSelect, styles.swatch, styles.swatchOuter, styles.swatchSelected]);
17
+ return (<FlatList data={COLOR_KEYS} numColumns={GRID_COLUMNS} keyExtractor={item => item} renderItem={renderItem} contentContainerStyle={styles.grid} columnWrapperStyle={styles.row} scrollEnabled={false}/>);
18
+ }
19
+ const PADDING = 16;
20
+ const GAP = 8;
21
+ const useStyles = () => {
22
+ const { colors } = useTheme();
23
+ const { width: screenWidth } = useWindowDimensions();
24
+ const swatchSize = Math.floor((screenWidth - PADDING * 2 - (GRID_COLUMNS - 1) * GAP) / GRID_COLUMNS);
25
+ const innerSize = swatchSize - 6;
26
+ return StyleSheet.create({
27
+ grid: {
28
+ paddingHorizontal: PADDING,
29
+ paddingVertical: 12,
30
+ },
31
+ row: {
32
+ gap: GAP,
33
+ marginBottom: GAP,
34
+ },
35
+ swatchOuter: {
36
+ width: swatchSize,
37
+ height: swatchSize,
38
+ borderRadius: swatchSize / 2,
39
+ alignItems: 'center',
40
+ justifyContent: 'center',
41
+ },
42
+ swatchSelected: {
43
+ borderWidth: 3,
44
+ borderColor: colors.interaction,
45
+ },
46
+ swatch: {
47
+ width: innerSize,
48
+ height: innerSize,
49
+ borderRadius: innerSize / 2,
50
+ },
51
+ });
52
+ };
53
+ //# sourceMappingURL=color_picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color_picker.js","sourceRoot":"","sources":["../../../src/screens/avatar_picker/color_picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport React, { useCallback } from 'react'\nimport { FlatList, StyleSheet, useWindowDimensions } from 'react-native'\nimport LinearGradient from 'react-native-linear-gradient'\nimport {\n COLOR_KEYS,\n type CustomAvatarColorKey,\n getAvatarGradientProps,\n} from '../../components/display/utils/avatar_gradient_colors'\nimport { useTheme } from '../../hooks'\nimport { GRID_COLUMNS } from './constants'\n\ninterface ColorPickerProps {\n selectedColor: string\n onColorSelect: (color: CustomAvatarColorKey) => void\n}\n\nexport function ColorPicker({ selectedColor, onColorSelect }: ColorPickerProps) {\n const styles = useStyles()\n\n const renderItem = useCallback(\n ({ item }: { item: CustomAvatarColorKey }) => {\n const gradientProps = getAvatarGradientProps(item)\n const isSelected = item === selectedColor\n\n return (\n <PlatformPressable\n onPress={() => onColorSelect(item)}\n style={[styles.swatchOuter, isSelected && styles.swatchSelected]}\n accessibilityRole=\"button\"\n accessibilityLabel={`${item.replace(/-/g, ' ')} color`}\n accessibilityState={{ selected: isSelected }}\n >\n <LinearGradient {...gradientProps} style={styles.swatch} />\n </PlatformPressable>\n )\n },\n [selectedColor, onColorSelect, styles.swatch, styles.swatchOuter, styles.swatchSelected]\n )\n\n return (\n <FlatList\n data={COLOR_KEYS}\n numColumns={GRID_COLUMNS}\n keyExtractor={item => item}\n renderItem={renderItem}\n contentContainerStyle={styles.grid}\n columnWrapperStyle={styles.row}\n scrollEnabled={false}\n />\n )\n}\n\nconst PADDING = 16\nconst GAP = 8\n\nconst useStyles = () => {\n const { colors } = useTheme()\n const { width: screenWidth } = useWindowDimensions()\n const swatchSize = Math.floor(\n (screenWidth - PADDING * 2 - (GRID_COLUMNS - 1) * GAP) / GRID_COLUMNS\n )\n const innerSize = swatchSize - 6\n\n return StyleSheet.create({\n grid: {\n paddingHorizontal: PADDING,\n paddingVertical: 12,\n },\n row: {\n gap: GAP,\n marginBottom: GAP,\n },\n swatchOuter: {\n width: swatchSize,\n height: swatchSize,\n borderRadius: swatchSize / 2,\n alignItems: 'center',\n justifyContent: 'center',\n },\n swatchSelected: {\n borderWidth: 3,\n borderColor: colors.interaction,\n },\n swatch: {\n width: innerSize,\n height: innerSize,\n borderRadius: innerSize / 2,\n },\n })\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const AVATAR_ICON_KEYS: string[];
2
+ export declare const GRID_COLUMNS = 6;
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/screens/avatar_picker/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,EAAE,MAAM,EAkDpC,CAAA;AAED,eAAO,MAAM,YAAY,IAAI,CAAA"}
@@ -0,0 +1,53 @@
1
+ export const AVATAR_ICON_KEYS = [
2
+ // Church
3
+ 'book-bible',
4
+ 'church',
5
+ 'cross',
6
+ 'dove',
7
+ 'hands-heart',
8
+ 'person-rays',
9
+ 'praying-hands',
10
+ 'podium',
11
+ 'droplet',
12
+ 'person-drowning',
13
+ 'lighthouse',
14
+ // Services
15
+ 'guitar',
16
+ 'guitar-electric',
17
+ 'violin',
18
+ 'calendar-heart',
19
+ 'hand-heart',
20
+ 'hand-wave',
21
+ 'baby',
22
+ 'children',
23
+ 'mug-hot',
24
+ 'video-camera',
25
+ 'music',
26
+ 'piano-keyboard',
27
+ 'drum',
28
+ 'user-graduate',
29
+ 'comments-question',
30
+ 'presentation',
31
+ 'microphone-stand',
32
+ 'id-badge',
33
+ 'book-user',
34
+ 'hand-holding-heart',
35
+ 'guitars',
36
+ 'amp-guitar',
37
+ 'school-flag',
38
+ 'shield-check',
39
+ 'projector',
40
+ // Groups
41
+ 'people',
42
+ 'people-group',
43
+ 'person-dress',
44
+ 'person',
45
+ 'seedling',
46
+ 'comments-alt',
47
+ 'globe',
48
+ 'book-open-reader',
49
+ 'running',
50
+ 'leaf',
51
+ ];
52
+ export const GRID_COLUMNS = 6;
53
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/screens/avatar_picker/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,OAAO;IACP,MAAM;IACN,aAAa;IACb,aAAa;IACb,eAAe;IACf,QAAQ;IACR,SAAS;IACT,iBAAiB;IACjB,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,iBAAiB;IACjB,QAAQ;IACR,gBAAgB;IAChB,YAAY;IACZ,WAAW;IACX,MAAM;IACN,UAAU;IACV,SAAS;IACT,cAAc;IACd,OAAO;IACP,gBAAgB;IAChB,MAAM;IACN,eAAe;IACf,mBAAmB;IACnB,cAAc;IACd,kBAAkB;IAClB,UAAU;IACV,WAAW;IACX,oBAAoB;IACpB,SAAS;IACT,YAAY;IACZ,aAAa;IACb,cAAc;IACd,WAAW;IACX,SAAS;IACT,QAAQ;IACR,cAAc;IACd,cAAc;IACd,QAAQ;IACR,UAAU;IACV,cAAc;IACd,OAAO;IACP,kBAAkB;IAClB,SAAS;IACT,MAAM;CACP,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAA","sourcesContent":["export const AVATAR_ICON_KEYS: string[] = [\n // Church\n 'book-bible',\n 'church',\n 'cross',\n 'dove',\n 'hands-heart',\n 'person-rays',\n 'praying-hands',\n 'podium',\n 'droplet',\n 'person-drowning',\n 'lighthouse',\n // Services\n 'guitar',\n 'guitar-electric',\n 'violin',\n 'calendar-heart',\n 'hand-heart',\n 'hand-wave',\n 'baby',\n 'children',\n 'mug-hot',\n 'video-camera',\n 'music',\n 'piano-keyboard',\n 'drum',\n 'user-graduate',\n 'comments-question',\n 'presentation',\n 'microphone-stand',\n 'id-badge',\n 'book-user',\n 'hand-holding-heart',\n 'guitars',\n 'amp-guitar',\n 'school-flag',\n 'shield-check',\n 'projector',\n // Groups\n 'people',\n 'people-group',\n 'person-dress',\n 'person',\n 'seedling',\n 'comments-alt',\n 'globe',\n 'book-open-reader',\n 'running',\n 'leaf',\n]\n\nexport const GRID_COLUMNS = 6\n"]}
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface EmojiTabProps {
3
+ onEmojiSelect: (emoji: string) => void;
4
+ }
5
+ export declare function EmojiTab({ onEmojiSelect }: EmojiTabProps): React.JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=emoji_tab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emoji_tab.d.ts","sourceRoot":"","sources":["../../../src/screens/avatar_picker/emoji_tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAA;AAK1C,UAAU,aAAa;IACrB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC;AAED,wBAAgB,QAAQ,CAAC,EAAE,aAAa,EAAE,EAAE,aAAa,qBAuDxD"}
@@ -0,0 +1,55 @@
1
+ import React, { useCallback } from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { EmojiKeyboard } from 'rn-emoji-keyboard';
4
+ import { useTheme } from '../../hooks';
5
+ export function EmojiTab({ onEmojiSelect }) {
6
+ const styles = useStyles();
7
+ const { colors } = useTheme();
8
+ const handleEmojiSelected = useCallback((emojiObject) => {
9
+ onEmojiSelect(emojiObject.emoji);
10
+ }, [onEmojiSelect]);
11
+ const emojiTheme = {
12
+ container: colors.fillColorNeutral100Inverted,
13
+ header: colors.textColorDefaultSecondary,
14
+ knob: colors.fillColorNeutral040,
15
+ skinTonesContainer: colors.fillColorNeutral060,
16
+ category: {
17
+ icon: colors.textColorDefaultSecondary,
18
+ iconActive: colors.interaction,
19
+ container: colors.fillColorNeutral100Inverted,
20
+ containerActive: colors.fillColorNeutral080,
21
+ },
22
+ search: {
23
+ background: colors.fillColorNeutral080,
24
+ text: colors.textColorDefaultPrimary,
25
+ placeholder: colors.textColorDefaultSecondary,
26
+ icon: colors.textColorDefaultSecondary,
27
+ },
28
+ emoji: {
29
+ selected: colors.fillColorNeutral080,
30
+ },
31
+ };
32
+ return (<View style={styles.container}>
33
+ <EmojiKeyboard categoryPosition="top" onEmojiSelected={handleEmojiSelected} enableSearchBar enableRecentlyUsed theme={emojiTheme} styles={{
34
+ container: { borderRadius: 0 },
35
+ category: {
36
+ container: {
37
+ borderRadius: 0,
38
+ marginTop: -6,
39
+ borderBottomWidth: 1,
40
+ borderBottomColor: colors.borderColorDefaultDim,
41
+ },
42
+ },
43
+ }}/>
44
+ </View>);
45
+ }
46
+ const useStyles = () => {
47
+ const { colors } = useTheme();
48
+ return StyleSheet.create({
49
+ container: {
50
+ flex: 1,
51
+ backgroundColor: colors.fillColorNeutral100Inverted,
52
+ },
53
+ });
54
+ };
55
+ //# sourceMappingURL=emoji_tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emoji_tab.js","sourceRoot":"","sources":["../../../src/screens/avatar_picker/emoji_tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAkB,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAMtC,MAAM,UAAU,QAAQ,CAAC,EAAE,aAAa,EAAiB;IACvD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE7B,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,WAAsB,EAAE,EAAE;QACzB,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAA;IAED,MAAM,UAAU,GAAG;QACjB,SAAS,EAAE,MAAM,CAAC,2BAA2B;QAC7C,MAAM,EAAE,MAAM,CAAC,yBAAyB;QACxC,IAAI,EAAE,MAAM,CAAC,mBAAmB;QAChC,kBAAkB,EAAE,MAAM,CAAC,mBAAmB;QAC9C,QAAQ,EAAE;YACR,IAAI,EAAE,MAAM,CAAC,yBAAyB;YACtC,UAAU,EAAE,MAAM,CAAC,WAAW;YAC9B,SAAS,EAAE,MAAM,CAAC,2BAA2B;YAC7C,eAAe,EAAE,MAAM,CAAC,mBAAmB;SAC5C;QACD,MAAM,EAAE;YACN,UAAU,EAAE,MAAM,CAAC,mBAAmB;YACtC,IAAI,EAAE,MAAM,CAAC,uBAAuB;YACpC,WAAW,EAAE,MAAM,CAAC,yBAAyB;YAC7C,IAAI,EAAE,MAAM,CAAC,yBAAyB;SACvC;QACD,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM,CAAC,mBAAmB;SACrC;KACF,CAAA;IAED,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAC5B;MAAA,CAAC,aAAa,CACZ,gBAAgB,CAAC,KAAK,CACtB,eAAe,CAAC,CAAC,mBAAmB,CAAC,CACrC,eAAe,CACf,kBAAkB,CAClB,KAAK,CAAC,CAAC,UAAU,CAAC,CAClB,MAAM,CAAC,CAAC;YACN,SAAS,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE;YAC9B,QAAQ,EAAE;gBACR,SAAS,EAAE;oBACT,YAAY,EAAE,CAAC;oBACf,SAAS,EAAE,CAAC,CAAC;oBACb,iBAAiB,EAAE,CAAC;oBACpB,iBAAiB,EAAE,MAAM,CAAC,qBAAqB;iBAChD;aACF;SACF,CAAC,EAEN;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE7B,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE;YACT,IAAI,EAAE,CAAC;YACP,eAAe,EAAE,MAAM,CAAC,2BAA2B;SACpD;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import React, { useCallback } from 'react'\nimport { StyleSheet, View } from 'react-native'\nimport { EmojiKeyboard, type EmojiType } from 'rn-emoji-keyboard'\nimport { useTheme } from '../../hooks'\n\ninterface EmojiTabProps {\n onEmojiSelect: (emoji: string) => void\n}\n\nexport function EmojiTab({ onEmojiSelect }: EmojiTabProps) {\n const styles = useStyles()\n const { colors } = useTheme()\n\n const handleEmojiSelected = useCallback(\n (emojiObject: EmojiType) => {\n onEmojiSelect(emojiObject.emoji)\n },\n [onEmojiSelect]\n )\n\n const emojiTheme = {\n container: colors.fillColorNeutral100Inverted,\n header: colors.textColorDefaultSecondary,\n knob: colors.fillColorNeutral040,\n skinTonesContainer: colors.fillColorNeutral060,\n category: {\n icon: colors.textColorDefaultSecondary,\n iconActive: colors.interaction,\n container: colors.fillColorNeutral100Inverted,\n containerActive: colors.fillColorNeutral080,\n },\n search: {\n background: colors.fillColorNeutral080,\n text: colors.textColorDefaultPrimary,\n placeholder: colors.textColorDefaultSecondary,\n icon: colors.textColorDefaultSecondary,\n },\n emoji: {\n selected: colors.fillColorNeutral080,\n },\n }\n\n return (\n <View style={styles.container}>\n <EmojiKeyboard\n categoryPosition=\"top\"\n onEmojiSelected={handleEmojiSelected}\n enableSearchBar\n enableRecentlyUsed\n theme={emojiTheme}\n styles={{\n container: { borderRadius: 0 },\n category: {\n container: {\n borderRadius: 0,\n marginTop: -6,\n borderBottomWidth: 1,\n borderBottomColor: colors.borderColorDefaultDim,\n },\n },\n }}\n />\n </View>\n )\n}\n\nconst useStyles = () => {\n const { colors } = useTheme()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n backgroundColor: colors.fillColorNeutral100Inverted,\n },\n })\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface IconGridProps {
3
+ selectedIconKey: string | null;
4
+ onIconSelect: (iconKey: string) => void;
5
+ }
6
+ export declare function IconGrid({ selectedIconKey, onIconSelect }: IconGridProps): React.JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=icon_grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon_grid.d.ts","sourceRoot":"","sources":["../../../src/screens/avatar_picker/icon_grid.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAsB,MAAM,OAAO,CAAA;AAK1C,UAAU,aAAa;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,wBAAgB,QAAQ,CAAC,EAAE,eAAe,EAAE,YAAY,EAAE,EAAE,aAAa,qBAiCxE"}
@@ -0,0 +1,48 @@
1
+ import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
2
+ import { PlatformPressable } from '@react-navigation/elements';
3
+ import React, { useCallback } from 'react';
4
+ import { FlatList, StyleSheet, useWindowDimensions } from 'react-native';
5
+ import { useTheme } from '../../hooks';
6
+ import { AVATAR_ICON_KEYS, GRID_COLUMNS } from './constants';
7
+ export function IconGrid({ selectedIconKey, onIconSelect }) {
8
+ const styles = useStyles();
9
+ const renderItem = useCallback(({ item }) => {
10
+ const isSelected = item === selectedIconKey;
11
+ return (<PlatformPressable onPress={() => onIconSelect(item)} style={[styles.cell, isSelected && styles.cellSelected]} accessibilityRole="button" accessibilityLabel={`${item.replace(/-/g, ' ')} icon`} accessibilityState={{ selected: isSelected }}>
12
+ <FontAwesomeIcon icon={['fas', item]} size={20} color="white"/>
13
+ </PlatformPressable>);
14
+ }, [selectedIconKey, onIconSelect, styles.cell, styles.cellSelected]);
15
+ return (<FlatList data={AVATAR_ICON_KEYS} numColumns={GRID_COLUMNS} keyExtractor={item => item} renderItem={renderItem} contentContainerStyle={styles.grid} columnWrapperStyle={styles.row} style={styles.list}/>);
16
+ }
17
+ const PADDING = 16;
18
+ const GAP = 8;
19
+ const useStyles = () => {
20
+ const { colors } = useTheme();
21
+ const { width: screenWidth } = useWindowDimensions();
22
+ const cellSize = Math.floor((screenWidth - PADDING * 2 - (GRID_COLUMNS - 1) * GAP) / GRID_COLUMNS);
23
+ return StyleSheet.create({
24
+ list: {
25
+ flex: 1,
26
+ },
27
+ grid: {
28
+ padding: PADDING,
29
+ },
30
+ row: {
31
+ gap: GAP,
32
+ marginBottom: GAP,
33
+ },
34
+ cell: {
35
+ width: cellSize,
36
+ height: cellSize,
37
+ borderRadius: cellSize / 2,
38
+ backgroundColor: colors.fillColorNeutral040,
39
+ alignItems: 'center',
40
+ justifyContent: 'center',
41
+ },
42
+ cellSelected: {
43
+ borderWidth: 3,
44
+ borderColor: colors.interaction,
45
+ },
46
+ });
47
+ };
48
+ //# sourceMappingURL=icon_grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon_grid.js","sourceRoot":"","sources":["../../../src/screens/avatar_picker/icon_grid.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { IconName } from '@fortawesome/fontawesome-svg-core'\nimport { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'\nimport { PlatformPressable } from '@react-navigation/elements'\nimport React, { useCallback } from 'react'\nimport { FlatList, StyleSheet, useWindowDimensions } from 'react-native'\nimport { useTheme } from '../../hooks'\nimport { AVATAR_ICON_KEYS, GRID_COLUMNS } from './constants'\n\ninterface IconGridProps {\n selectedIconKey: string | null\n onIconSelect: (iconKey: string) => void\n}\n\nexport function IconGrid({ selectedIconKey, onIconSelect }: IconGridProps) {\n const styles = useStyles()\n\n const renderItem = useCallback(\n ({ item }: { item: string }) => {\n const isSelected = item === selectedIconKey\n\n return (\n <PlatformPressable\n onPress={() => onIconSelect(item)}\n style={[styles.cell, isSelected && styles.cellSelected]}\n accessibilityRole=\"button\"\n accessibilityLabel={`${item.replace(/-/g, ' ')} icon`}\n accessibilityState={{ selected: isSelected }}\n >\n <FontAwesomeIcon icon={['fas', item as IconName]} size={20} color=\"white\" />\n </PlatformPressable>\n )\n },\n [selectedIconKey, onIconSelect, styles.cell, styles.cellSelected]\n )\n\n return (\n <FlatList\n data={AVATAR_ICON_KEYS}\n numColumns={GRID_COLUMNS}\n keyExtractor={item => item}\n renderItem={renderItem}\n contentContainerStyle={styles.grid}\n columnWrapperStyle={styles.row}\n style={styles.list}\n />\n )\n}\n\nconst PADDING = 16\nconst GAP = 8\n\nconst useStyles = () => {\n const { colors } = useTheme()\n const { width: screenWidth } = useWindowDimensions()\n const cellSize = Math.floor((screenWidth - PADDING * 2 - (GRID_COLUMNS - 1) * GAP) / GRID_COLUMNS)\n\n return StyleSheet.create({\n list: {\n flex: 1,\n },\n grid: {\n padding: PADDING,\n },\n row: {\n gap: GAP,\n marginBottom: GAP,\n },\n cell: {\n width: cellSize,\n height: cellSize,\n borderRadius: cellSize / 2,\n backgroundColor: colors.fillColorNeutral040,\n alignItems: 'center',\n justifyContent: 'center',\n },\n cellSelected: {\n borderWidth: 3,\n borderColor: colors.interaction,\n },\n })\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { ImagePickerAsset } from '../../utils/native_adapters/image_picker';
3
+ interface UploadTabProps {
4
+ imagePreviewUri: string | null;
5
+ onImageSelect: (asset: ImagePickerAsset) => void;
6
+ }
7
+ export declare function UploadTab({ imagePreviewUri, onImageSelect }: UploadTabProps): React.JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=upload_tab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload_tab.d.ts","sourceRoot":"","sources":["../../../src/screens/avatar_picker/upload_tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAA;AAK1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAA;AAIhF,UAAU,cAAc;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;CACjD;AAED,wBAAgB,SAAS,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,EAAE,cAAc,qBAkC3E"}
@@ -0,0 +1,39 @@
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
+ const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
7
+ export function UploadTab({ imagePreviewUri, onImageSelect }) {
8
+ const styles = useStyles();
9
+ const pickImage = useCallback(async () => {
10
+ const result = await ImagePicker.openImageLibraryAsync({
11
+ mediaTypes: ['images'],
12
+ allowsEditing: true,
13
+ allowsMultipleSelection: false,
14
+ });
15
+ if (result.canceled || !result.assets?.[0])
16
+ return;
17
+ const asset = result.assets[0];
18
+ if (asset.fileSize && asset.fileSize > MAX_FILE_SIZE) {
19
+ Alert.alert('Image too large', 'Please choose an image under 10MB.');
20
+ return;
21
+ }
22
+ onImageSelect(asset);
23
+ }, [onImageSelect]);
24
+ return (<View style={styles.container}>
25
+ <Button title={imagePreviewUri ? 'Change photo' : 'Choose photo'} iconNameLeft="general.image" onPress={pickImage} variant="outline" appearance="interaction" size="md"/>
26
+ </View>);
27
+ }
28
+ const useStyles = () => {
29
+ const { colors } = useTheme();
30
+ return StyleSheet.create({
31
+ container: {
32
+ flex: 1,
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ backgroundColor: colors.fillColorNeutral100Inverted,
36
+ },
37
+ });
38
+ };
39
+ //# sourceMappingURL=upload_tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload_tab.js","sourceRoot":"","sources":["../../../src/screens/avatar_picker/upload_tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAGzD,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA,CAAC,OAAO;AAO9C,MAAM,UAAU,SAAS,CAAC,EAAE,eAAe,EAAE,aAAa,EAAkB;IAC1E,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACvC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,qBAAqB,CAAC;YACrD,UAAU,EAAE,CAAC,QAAQ,CAAC;YACtB,aAAa,EAAE,IAAI;YACnB,uBAAuB,EAAE,KAAK;SAC/B,CAAC,CAAA;QAEF,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAAE,OAAM;QAElD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAE9B,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,GAAG,aAAa,EAAE,CAAC;YACrD,KAAK,CAAC,KAAK,CAAC,iBAAiB,EAAE,oCAAoC,CAAC,CAAA;YACpE,OAAM;QACR,CAAC;QAED,aAAa,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAA;IAEnB,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAC5B;MAAA,CAAC,MAAM,CACL,KAAK,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CACzD,YAAY,CAAC,eAAe,CAC5B,OAAO,CAAC,CAAC,SAAS,CAAC,CACnB,OAAO,CAAC,SAAS,CACjB,UAAU,CAAC,aAAa,CACxB,IAAI,CAAC,IAAI,EAEb;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE7B,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE;YACT,IAAI,EAAE,CAAC;YACP,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,eAAe,EAAE,MAAM,CAAC,2BAA2B;SACpD;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import React, { useCallback } from 'react'\nimport { Alert, StyleSheet, View } from 'react-native'\nimport { Button } from '../../components'\nimport { useTheme } from '../../hooks'\nimport { ImagePicker } from '../../utils/native_adapters'\nimport type { ImagePickerAsset } from '../../utils/native_adapters/image_picker'\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB\n\ninterface UploadTabProps {\n imagePreviewUri: string | null\n onImageSelect: (asset: ImagePickerAsset) => void\n}\n\nexport function UploadTab({ imagePreviewUri, onImageSelect }: UploadTabProps) {\n const styles = useStyles()\n\n const pickImage = useCallback(async () => {\n const result = await ImagePicker.openImageLibraryAsync({\n mediaTypes: ['images'],\n allowsEditing: true,\n allowsMultipleSelection: false,\n })\n\n if (result.canceled || !result.assets?.[0]) return\n\n const asset = result.assets[0]\n\n if (asset.fileSize && asset.fileSize > MAX_FILE_SIZE) {\n Alert.alert('Image too large', 'Please choose an image under 10MB.')\n return\n }\n\n onImageSelect(asset)\n }, [onImageSelect])\n\n return (\n <View style={styles.container}>\n <Button\n title={imagePreviewUri ? 'Change photo' : 'Choose photo'}\n iconNameLeft=\"general.image\"\n onPress={pickImage}\n variant=\"outline\"\n appearance=\"interaction\"\n size=\"md\"\n />\n </View>\n )\n}\n\nconst useStyles = () => {\n const { colors } = useTheme()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: colors.fillColorNeutral100Inverted,\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_details_screen.d.ts","sourceRoot":"","sources":["../../src/screens/conversation_details_screen.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAgB,iBAAiB,EAAiB,MAAM,0BAA0B,CAAA;AACzF,OAAO,KAQN,MAAM,OAAO,CAAA;AA0Ed,MAAM,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;IAC7D,eAAe,EAAE,MAAM,CAAA;CACxB,CAAC,CAAA;AAEF,wBAAgB,yBAAyB,CAAC,EAAE,KAAK,EAAE,EAAE,8BAA8B,qBA2VlF"}
1
+ {"version":3,"file":"conversation_details_screen.d.ts","sourceRoot":"","sources":["../../src/screens/conversation_details_screen.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAgB,iBAAiB,EAAiB,MAAM,0BAA0B,CAAA;AACzF,OAAO,KAQN,MAAM,OAAO,CAAA;AA4Ed,MAAM,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;IAC7D,eAAe,EAAE,MAAM,CAAA;CACxB,CAAC,CAAA;AAEF,wBAAgB,yBAAyB,CAAC,EAAE,KAAK,EAAE,EAAE,8BAA8B,qBA+WlF"}
@@ -2,7 +2,8 @@ import { HeaderTitle as ElementsHeaderTitle, PlatformPressable, } from '@react-n
2
2
  import { StackActions, useNavigation } from '@react-navigation/native';
3
3
  import React, { useCallback, useEffect, useRef, useState, } from 'react';
4
4
  import { Alert, FlatList, Platform, Pressable, StyleSheet, TextInput, View, } from 'react-native';
5
- import { Badge, ChildNotice, Heading, Icon, Person, Switch, Text, TextButton, } from '../components';
5
+ import { Badge, Button, ChildNotice, Heading, Icon, Person, Switch, Text, TextButton, } from '../components';
6
+ import { ConversationAvatar } from '../components/display/conversation_avatar';
6
7
  import { HeaderTextButton } from '../components/display/platform_modal_header_buttons';
7
8
  import { useSuspensePaginator, useTheme } from '../hooks';
8
9
  import { useConversation, useConversationDelete, useConversationDisableReplies, useConversationMute, useConversationUpdate, } from '../hooks/use_conversation';
@@ -34,6 +35,7 @@ export function ConversationDetailsScreen({ route }) {
34
35
  const { mutate: deleteConversation } = useConversationDelete(route.params);
35
36
  const { featureEnabled } = useFeatures();
36
37
  const granularNotificationsEnabled = featureEnabled(availableFeatures.granular_notifications_ui);
38
+ const customAvatarsEnabled = featureEnabled(availableFeatures.custom_conversation_avatars);
37
39
  const showGenderFilter = featureEnabled(availableFeatures.gender_specific_conversations) && !!conversation.genderOption;
38
40
  const genderLabel = conversation.genderOption ? genderDisplayLabel(conversation.genderOption) : '';
39
41
  const { muted, setMuted } = useConversationMute({ conversation_id });
@@ -123,6 +125,20 @@ export function ConversationDetailsScreen({ route }) {
123
125
  });
124
126
  }, [HeaderRight, HeaderTitle, navigation]);
125
127
  const listData = [
128
+ {
129
+ type: canUpdate && customAvatarsEnabled ? SectionTypes.view : SectionTypes.hidden,
130
+ data: {
131
+ children: (<AvatarCard conversation={conversation} onPress={() => navigation.navigate('AvatarPicker', { conversation_id })}/>),
132
+ },
133
+ sectionOuterStyle: styles.sectionOuterAvatarCard,
134
+ sectionInnerStyle: styles.sectionInnerAvatarCard,
135
+ },
136
+ {
137
+ type: SectionTypes.header,
138
+ data: { title: 'Basic info' },
139
+ showBottomBorder: true,
140
+ sectionInnerStyle: styles.sectionInnerHeaderWithBottomBorder,
141
+ },
126
142
  {
127
143
  type: SectionTypes.view,
128
144
  data: {
@@ -315,6 +331,13 @@ function NavigableSettingRow({ title, subtitle, onPress }) {
315
331
  </View>
316
332
  </PlatformPressable>);
317
333
  }
334
+ function AvatarCard({ conversation, onPress, }) {
335
+ const styles = useStyles();
336
+ return (<View style={styles.avatarCard}>
337
+ <ConversationAvatar conversation={conversation} size="2xl"/>
338
+ <Button title="Update avatar" iconNameLeft="general.pencil" onPress={onPress} variant="outline" appearance="interaction" size="sm"/>
339
+ </View>);
340
+ }
318
341
  function TeamsGroup({ teams }) {
319
342
  const styles = useStyles();
320
343
  if (teams.length === 0)
@@ -437,6 +460,19 @@ const useStyles = ({ isStart, isEnd } = {}) => {
437
460
  navigableSettingChevron: {
438
461
  color: colors.iconColorDefaultDisabled,
439
462
  },
463
+ avatarCard: {
464
+ alignItems: 'center',
465
+ gap: 16,
466
+ },
467
+ sectionOuterAvatarCard: {
468
+ paddingLeft: 0,
469
+ },
470
+ sectionInnerAvatarCard: {
471
+ paddingTop: 24,
472
+ paddingBottom: 24,
473
+ paddingHorizontal: 16,
474
+ alignItems: 'center',
475
+ },
440
476
  teamGroup: {
441
477
  flexDirection: 'row',
442
478
  gap: 4,