@planningcenter/chat-react-native 3.32.1-rc.1 → 3.33.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 (171) 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/primitive/avatar_primitive.d.ts +3 -1
  15. package/build/components/primitive/avatar_primitive.d.ts.map +1 -1
  16. package/build/components/primitive/avatar_primitive.js +10 -2
  17. package/build/components/primitive/avatar_primitive.js.map +1 -1
  18. package/build/hooks/attachments/fallback_chat_configuration.d.ts +4 -0
  19. package/build/hooks/attachments/fallback_chat_configuration.d.ts.map +1 -0
  20. package/build/hooks/attachments/fallback_chat_configuration.js +59 -0
  21. package/build/hooks/attachments/fallback_chat_configuration.js.map +1 -0
  22. package/build/hooks/groups/use_groups_conversation_create.d.ts.map +1 -1
  23. package/build/hooks/groups/use_groups_conversation_create.js +1 -1
  24. package/build/hooks/groups/use_groups_conversation_create.js.map +1 -1
  25. package/build/hooks/services/use_find_or_create_services_conversation.d.ts +43 -11
  26. package/build/hooks/services/use_find_or_create_services_conversation.d.ts.map +1 -1
  27. package/build/hooks/services/use_find_or_create_services_conversation.js +5 -5
  28. package/build/hooks/services/use_find_or_create_services_conversation.js.map +1 -1
  29. package/build/hooks/use_attachment_uploader.d.ts.map +1 -1
  30. package/build/hooks/use_attachment_uploader.js +39 -14
  31. package/build/hooks/use_attachment_uploader.js.map +1 -1
  32. package/build/hooks/use_chat_configuration.d.ts +6 -0
  33. package/build/hooks/use_chat_configuration.d.ts.map +1 -0
  34. package/build/hooks/use_chat_configuration.js +41 -0
  35. package/build/hooks/use_chat_configuration.js.map +1 -0
  36. package/build/hooks/use_conversation_avatar_update.d.ts +26 -0
  37. package/build/hooks/use_conversation_avatar_update.d.ts.map +1 -0
  38. package/build/hooks/use_conversation_avatar_update.js +130 -0
  39. package/build/hooks/use_conversation_avatar_update.js.map +1 -0
  40. package/build/hooks/use_features.d.ts +1 -0
  41. package/build/hooks/use_features.d.ts.map +1 -1
  42. package/build/hooks/use_features.js +1 -0
  43. package/build/hooks/use_features.js.map +1 -1
  44. package/build/navigation/index.d.ts +16 -0
  45. package/build/navigation/index.d.ts.map +1 -1
  46. package/build/navigation/index.js +9 -0
  47. package/build/navigation/index.js.map +1 -1
  48. package/build/screens/avatar_picker/avatar_picker_screen.d.ts +12 -0
  49. package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -0
  50. package/build/screens/avatar_picker/avatar_picker_screen.js +193 -0
  51. package/build/screens/avatar_picker/avatar_picker_screen.js.map +1 -0
  52. package/build/screens/avatar_picker/avatar_picker_state.d.ts +38 -0
  53. package/build/screens/avatar_picker/avatar_picker_state.d.ts.map +1 -0
  54. package/build/screens/avatar_picker/avatar_picker_state.js +101 -0
  55. package/build/screens/avatar_picker/avatar_picker_state.js.map +1 -0
  56. package/build/screens/avatar_picker/avatar_preview.d.ts +9 -0
  57. package/build/screens/avatar_picker/avatar_preview.d.ts.map +1 -0
  58. package/build/screens/avatar_picker/avatar_preview.js +39 -0
  59. package/build/screens/avatar_picker/avatar_preview.js.map +1 -0
  60. package/build/screens/avatar_picker/color_picker.d.ts +9 -0
  61. package/build/screens/avatar_picker/color_picker.d.ts.map +1 -0
  62. package/build/screens/avatar_picker/color_picker.js +53 -0
  63. package/build/screens/avatar_picker/color_picker.js.map +1 -0
  64. package/build/screens/avatar_picker/constants.d.ts +3 -0
  65. package/build/screens/avatar_picker/constants.d.ts.map +1 -0
  66. package/build/screens/avatar_picker/constants.js +53 -0
  67. package/build/screens/avatar_picker/constants.js.map +1 -0
  68. package/build/screens/avatar_picker/emoji_tab.d.ts +7 -0
  69. package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -0
  70. package/build/screens/avatar_picker/emoji_tab.js +55 -0
  71. package/build/screens/avatar_picker/emoji_tab.js.map +1 -0
  72. package/build/screens/avatar_picker/icon_grid.d.ts +8 -0
  73. package/build/screens/avatar_picker/icon_grid.d.ts.map +1 -0
  74. package/build/screens/avatar_picker/icon_grid.js +48 -0
  75. package/build/screens/avatar_picker/icon_grid.js.map +1 -0
  76. package/build/screens/avatar_picker/upload_tab.d.ts +9 -0
  77. package/build/screens/avatar_picker/upload_tab.d.ts.map +1 -0
  78. package/build/screens/avatar_picker/upload_tab.js +39 -0
  79. package/build/screens/avatar_picker/upload_tab.js.map +1 -0
  80. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  81. package/build/screens/conversation_details_screen.js +37 -1
  82. package/build/screens/conversation_details_screen.js.map +1 -1
  83. package/build/screens/conversation_new/components/avatar_selection_row.d.ts +12 -0
  84. package/build/screens/conversation_new/components/avatar_selection_row.d.ts.map +1 -0
  85. package/build/screens/conversation_new/components/avatar_selection_row.js +60 -0
  86. package/build/screens/conversation_new/components/avatar_selection_row.js.map +1 -0
  87. package/build/screens/conversation_new/components/gender_filter_toggle.d.ts.map +1 -1
  88. package/build/screens/conversation_new/components/gender_filter_toggle.js +3 -9
  89. package/build/screens/conversation_new/components/gender_filter_toggle.js.map +1 -1
  90. package/build/screens/conversation_new/components/groups_form.d.ts +3 -1
  91. package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
  92. package/build/screens/conversation_new/components/groups_form.js +22 -8
  93. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  94. package/build/screens/conversation_new/components/services_form.d.ts +3 -1
  95. package/build/screens/conversation_new/components/services_form.d.ts.map +1 -1
  96. package/build/screens/conversation_new/components/services_form.js +22 -8
  97. package/build/screens/conversation_new/components/services_form.js.map +1 -1
  98. package/build/screens/conversation_new/conversation_new_screen.d.ts +2 -0
  99. package/build/screens/conversation_new/conversation_new_screen.d.ts.map +1 -1
  100. package/build/screens/conversation_new/conversation_new_screen.js +3 -3
  101. package/build/screens/conversation_new/conversation_new_screen.js.map +1 -1
  102. package/build/screens/team_conversation_screen.d.ts.map +1 -1
  103. package/build/screens/team_conversation_screen.js +1 -1
  104. package/build/screens/team_conversation_screen.js.map +1 -1
  105. package/build/types/resources/chat_configuration_resource.d.ts +8 -0
  106. package/build/types/resources/chat_configuration_resource.d.ts.map +1 -0
  107. package/build/types/resources/chat_configuration_resource.js +2 -0
  108. package/build/types/resources/chat_configuration_resource.js.map +1 -0
  109. package/build/utils/native_adapters/configuration.d.ts +3 -0
  110. package/build/utils/native_adapters/configuration.d.ts.map +1 -1
  111. package/build/utils/native_adapters/configuration.js +8 -0
  112. package/build/utils/native_adapters/configuration.js.map +1 -1
  113. package/build/utils/native_adapters/document_picker.d.ts +21 -0
  114. package/build/utils/native_adapters/document_picker.d.ts.map +1 -0
  115. package/build/utils/native_adapters/document_picker.js +7 -0
  116. package/build/utils/native_adapters/document_picker.js.map +1 -0
  117. package/build/utils/native_adapters/image_picker.d.ts +7 -1
  118. package/build/utils/native_adapters/image_picker.d.ts.map +1 -1
  119. package/build/utils/native_adapters/image_picker.js.map +1 -1
  120. package/build/utils/native_adapters/index.d.ts +1 -0
  121. package/build/utils/native_adapters/index.d.ts.map +1 -1
  122. package/build/utils/native_adapters/index.js +1 -0
  123. package/build/utils/native_adapters/index.js.map +1 -1
  124. package/build/utils/request/get_chat_configuration.d.ts +10 -0
  125. package/build/utils/request/get_chat_configuration.d.ts.map +1 -0
  126. package/build/utils/request/get_chat_configuration.js +21 -0
  127. package/build/utils/request/get_chat_configuration.js.map +1 -0
  128. package/package.json +4 -3
  129. package/src/__tests__/hooks/use_attachment_uploader.test.tsx +219 -0
  130. package/src/__tests__/hooks/use_chat_configuration.test.tsx +80 -0
  131. package/src/__tests__/utils/native_adapters/configuration.ts +25 -1
  132. package/src/components/conversation/message_form.tsx +39 -1
  133. package/src/components/display/emoji_avatar.tsx +7 -2
  134. package/src/components/display/icon_avatar.tsx +7 -2
  135. package/src/components/display/utils/avatar_gradient_colors.ts +10 -3
  136. package/src/components/primitive/avatar_primitive.tsx +11 -2
  137. package/src/hooks/attachments/fallback_chat_configuration.ts +61 -0
  138. package/src/hooks/groups/use_groups_conversation_create.ts +2 -1
  139. package/src/hooks/services/use_find_or_create_services_conversation.ts +7 -7
  140. package/src/hooks/use_attachment_uploader.ts +39 -15
  141. package/src/hooks/use_chat_configuration.ts +54 -0
  142. package/src/hooks/use_conversation_avatar_update.ts +163 -0
  143. package/src/hooks/use_features.ts +1 -0
  144. package/src/navigation/index.tsx +13 -0
  145. package/src/screens/avatar_picker/__tests__/avatar_picker_state.test.ts +157 -0
  146. package/src/screens/avatar_picker/avatar_picker_screen.tsx +312 -0
  147. package/src/screens/avatar_picker/avatar_picker_state.ts +141 -0
  148. package/src/screens/avatar_picker/avatar_preview.tsx +46 -0
  149. package/src/screens/avatar_picker/color_picker.tsx +91 -0
  150. package/src/screens/avatar_picker/constants.ts +53 -0
  151. package/src/screens/avatar_picker/emoji_tab.tsx +76 -0
  152. package/src/screens/avatar_picker/icon_grid.tsx +81 -0
  153. package/src/screens/avatar_picker/upload_tab.tsx +62 -0
  154. package/src/screens/conversation_details_screen.tsx +60 -1
  155. package/src/screens/conversation_new/components/avatar_selection_row.tsx +82 -0
  156. package/src/screens/conversation_new/components/gender_filter_toggle.tsx +3 -9
  157. package/src/screens/conversation_new/components/groups_form.tsx +33 -6
  158. package/src/screens/conversation_new/components/services_form.tsx +37 -6
  159. package/src/screens/conversation_new/conversation_new_screen.tsx +17 -3
  160. package/src/screens/team_conversation_screen.tsx +2 -1
  161. package/src/types/resources/chat_configuration_resource.ts +11 -0
  162. package/src/utils/native_adapters/configuration.ts +10 -0
  163. package/src/utils/native_adapters/document_picker.ts +26 -0
  164. package/src/utils/native_adapters/image_picker.ts +8 -1
  165. package/src/utils/native_adapters/index.ts +1 -0
  166. package/src/utils/request/get_chat_configuration.ts +23 -0
  167. package/build/hooks/attachments/supported_extensions.d.ts +0 -2
  168. package/build/hooks/attachments/supported_extensions.d.ts.map +0 -1
  169. package/build/hooks/attachments/supported_extensions.js +0 -48
  170. package/build/hooks/attachments/supported_extensions.js.map +0 -1
  171. package/src/hooks/attachments/supported_extensions.ts +0 -47
@@ -1 +1 @@
1
- {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/configuration.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage'\nimport { Linking as RNLinking } from 'react-native'\nimport { AudioAdapter } from './audio'\nimport { ClipboardAdapter } from './clipboard'\nimport { HapticAdapter } from './haptic'\nimport { ImagePickerAdapter } from './image_picker'\nimport { LinkingAdapter } from './linking'\nimport { LogAdapter } from './log'\nimport { StorageAdapter } from './storage_adapter'\nimport { VideoAdapter } from './video'\n\ntype ChatConfigurations = {\n clipboard: ClipboardAdapter\n audio: AudioAdapter\n video: VideoAdapter\n imagePicker: ImagePickerAdapter\n log?: LogAdapter\n linking?: LinkingAdapter\n haptic?: HapticAdapter\n}\n\nexport class ChatAdapters {\n static configure(configurations: ChatConfigurations) {\n Clipboard = configurations.clipboard\n Audio = configurations.audio\n Video = configurations.video\n ImagePicker = configurations.imagePicker\n Log = configurations.log || new LogAdapter()\n Linking = configurations.linking || new LinkingAdapter(RNLinking)\n Haptic = configurations.haptic || new HapticAdapter()\n }\n}\n\nconst methodMissing = () => {\n console.warn('ChatAdapters.configure() must be called before using any adapters')\n}\n\nexport let Clipboard: ClipboardAdapter = new ClipboardAdapter({\n getStringAsync: async () => {\n methodMissing()\n return ''\n },\n setStringAsync: async (_: string) => methodMissing(),\n})\n\nexport let Audio: AudioAdapter = new AudioAdapter({\n useAudio: (_: string) => {\n methodMissing()\n return {} as any\n },\n})\n\nexport let Video: VideoAdapter = new VideoAdapter({\n Player: Object.assign(\n () => {\n methodMissing()\n return null\n },\n { $$typeof: Symbol.for('react.forward_ref') }\n ),\n})\n\nexport let ImagePicker: ImagePickerAdapter = new ImagePickerAdapter({\n openCameraAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n openImageLibraryAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n})\n\nexport let Log: LogAdapter = new LogAdapter()\n\nexport let Linking: LinkingAdapter = new LinkingAdapter(RNLinking)\n\nexport let Haptic: HapticAdapter = new HapticAdapter()\n\nexport let Storage = new StorageAdapter({\n getItem: AsyncStorage.getItem,\n setItem: AsyncStorage.setItem,\n removeItem: AsyncStorage.removeItem,\n})\n"]}
1
+ {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/configuration.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage'\nimport { Linking as RNLinking } from 'react-native'\nimport { AudioAdapter } from './audio'\nimport { ClipboardAdapter } from './clipboard'\nimport { DocumentPickerAdapter } from './document_picker'\nimport { HapticAdapter } from './haptic'\nimport { ImagePickerAdapter } from './image_picker'\nimport { LinkingAdapter } from './linking'\nimport { LogAdapter } from './log'\nimport { StorageAdapter } from './storage_adapter'\nimport { VideoAdapter } from './video'\n\ntype ChatConfigurations = {\n clipboard: ClipboardAdapter\n audio: AudioAdapter\n video: VideoAdapter\n imagePicker: ImagePickerAdapter\n documentPicker: DocumentPickerAdapter\n log?: LogAdapter\n linking?: LinkingAdapter\n haptic?: HapticAdapter\n}\n\nexport class ChatAdapters {\n static configure(configurations: ChatConfigurations) {\n Clipboard = configurations.clipboard\n Audio = configurations.audio\n Video = configurations.video\n ImagePicker = configurations.imagePicker\n DocumentPicker = configurations.documentPicker\n Log = configurations.log || new LogAdapter()\n Linking = configurations.linking || new LinkingAdapter(RNLinking)\n Haptic = configurations.haptic || new HapticAdapter()\n }\n}\n\nconst methodMissing = () => {\n console.warn('ChatAdapters.configure() must be called before using any adapters')\n}\n\nexport let Clipboard: ClipboardAdapter = new ClipboardAdapter({\n getStringAsync: async () => {\n methodMissing()\n return ''\n },\n setStringAsync: async (_: string) => methodMissing(),\n})\n\nexport let Audio: AudioAdapter = new AudioAdapter({\n useAudio: (_: string) => {\n methodMissing()\n return {} as any\n },\n})\n\nexport let Video: VideoAdapter = new VideoAdapter({\n Player: Object.assign(\n () => {\n methodMissing()\n return null\n },\n { $$typeof: Symbol.for('react.forward_ref') }\n ),\n})\n\nexport let ImagePicker: ImagePickerAdapter = new ImagePickerAdapter({\n openCameraAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n openImageLibraryAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n})\n\nexport let DocumentPicker: DocumentPickerAdapter = new DocumentPickerAdapter({\n openAsync: async () => {\n methodMissing()\n return { canceled: true, assets: null }\n },\n})\n\nexport let Log: LogAdapter = new LogAdapter()\n\nexport let Linking: LinkingAdapter = new LinkingAdapter(RNLinking)\n\nexport let Haptic: HapticAdapter = new HapticAdapter()\n\nexport let Storage = new StorageAdapter({\n getItem: AsyncStorage.getItem,\n setItem: AsyncStorage.setItem,\n removeItem: AsyncStorage.removeItem,\n})\n"]}
@@ -0,0 +1,21 @@
1
+ export type DocumentPickerAsset = {
2
+ uri: string;
3
+ name: string;
4
+ size?: number;
5
+ mimeType?: string;
6
+ };
7
+ type DocumentPickerSuccessResult = {
8
+ canceled: false;
9
+ assets: DocumentPickerAsset[];
10
+ };
11
+ type DocumentPickerCanceledResult = {
12
+ canceled: true;
13
+ assets: null;
14
+ };
15
+ export type DocumentPickerResult = DocumentPickerSuccessResult | DocumentPickerCanceledResult;
16
+ export declare class DocumentPickerAdapter {
17
+ openAsync: () => Promise<DocumentPickerResult>;
18
+ constructor(methods: DocumentPickerAdapter);
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=document_picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document_picker.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/document_picker.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,2BAA2B,GAAG;IACjC,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,mBAAmB,EAAE,CAAA;CAC9B,CAAA;AAED,KAAK,4BAA4B,GAAG;IAClC,QAAQ,EAAE,IAAI,CAAA;IACd,MAAM,EAAE,IAAI,CAAA;CACb,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,2BAA2B,GAAG,4BAA4B,CAAA;AAE7F,qBAAa,qBAAqB;IAChC,SAAS,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAA;gBAElC,OAAO,EAAE,qBAAqB;CAG3C"}
@@ -0,0 +1,7 @@
1
+ export class DocumentPickerAdapter {
2
+ openAsync;
3
+ constructor(methods) {
4
+ this.openAsync = methods.openAsync;
5
+ }
6
+ }
7
+ //# sourceMappingURL=document_picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document_picker.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/document_picker.ts"],"names":[],"mappings":"AAmBA,MAAM,OAAO,qBAAqB;IAChC,SAAS,CAAqC;IAE9C,YAAY,OAA8B;QACxC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;IACpC,CAAC;CACF","sourcesContent":["export type DocumentPickerAsset = {\n uri: string\n name: string\n size?: number\n mimeType?: string\n}\n\ntype DocumentPickerSuccessResult = {\n canceled: false\n assets: DocumentPickerAsset[]\n}\n\ntype DocumentPickerCanceledResult = {\n canceled: true\n assets: null\n}\n\nexport type DocumentPickerResult = DocumentPickerSuccessResult | DocumentPickerCanceledResult\n\nexport class DocumentPickerAdapter {\n openAsync: () => Promise<DocumentPickerResult>\n\n constructor(methods: DocumentPickerAdapter) {\n this.openAsync = methods.openAsync\n }\n}\n"]}
@@ -16,9 +16,15 @@ type ImagePickerCanceledResult = {
16
16
  assets: null;
17
17
  };
18
18
  export type ImagePickerResult = ImagePickerSuccessResult | ImagePickerCanceledResult;
19
+ export interface ImagePickerOptions {
20
+ selectionLimit?: number;
21
+ allowsEditing?: boolean;
22
+ allowsMultipleSelection?: boolean;
23
+ mediaTypes?: Array<'images' | 'videos' | 'livePhotos'>;
24
+ }
19
25
  export declare class ImagePickerAdapter {
20
26
  openCameraAsync: () => Promise<ImagePickerResult>;
21
- openImageLibraryAsync: () => Promise<ImagePickerResult>;
27
+ openImageLibraryAsync: (options?: ImagePickerOptions) => Promise<ImagePickerResult>;
22
28
  constructor(methods: ImagePickerAdapter);
23
29
  }
24
30
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"image_picker.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/image_picker.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAC3B,CAAA;AAED,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,IAAI,CAAA;IACd,MAAM,EAAE,IAAI,CAAA;CACb,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,wBAAwB,GAAG,yBAAyB,CAAA;AAEpF,qBAAa,kBAAkB;IAC7B,eAAe,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACjD,qBAAqB,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;gBAE3C,OAAO,EAAE,kBAAkB;CAIxC"}
1
+ {"version":3,"file":"image_picker.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/image_picker.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAC3B,CAAA;AAED,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,IAAI,CAAA;IACd,MAAM,EAAE,IAAI,CAAA;CACb,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,wBAAwB,GAAG,yBAAyB,CAAA;AAEpF,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,UAAU,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC,CAAA;CACvD;AAED,qBAAa,kBAAkB;IAC7B,eAAe,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACjD,qBAAqB,EAAE,CAAC,OAAO,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;gBAEvE,OAAO,EAAE,kBAAkB;CAIxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"image_picker.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/image_picker.ts"],"names":[],"mappings":"AAsBA,MAAM,OAAO,kBAAkB;IAC7B,eAAe,CAAkC;IACjD,qBAAqB,CAAkC;IAEvD,YAAY,OAA2B;QACrC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAA;QAC9C,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAA;IAC5D,CAAC;CACF","sourcesContent":["export type ImagePickerAsset = {\n uri: string\n assetId?: string | null\n width: number\n height: number\n mimeType?: string\n fileName?: string | null\n fileSize?: number\n}\n\ntype ImagePickerSuccessResult = {\n canceled: false\n assets: ImagePickerAsset[]\n}\n\ntype ImagePickerCanceledResult = {\n canceled: true\n assets: null\n}\n\nexport type ImagePickerResult = ImagePickerSuccessResult | ImagePickerCanceledResult\n\nexport class ImagePickerAdapter {\n openCameraAsync: () => Promise<ImagePickerResult>\n openImageLibraryAsync: () => Promise<ImagePickerResult>\n\n constructor(methods: ImagePickerAdapter) {\n this.openCameraAsync = methods.openCameraAsync\n this.openImageLibraryAsync = methods.openImageLibraryAsync\n }\n}\n"]}
1
+ {"version":3,"file":"image_picker.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/image_picker.ts"],"names":[],"mappings":"AA6BA,MAAM,OAAO,kBAAkB;IAC7B,eAAe,CAAkC;IACjD,qBAAqB,CAA8D;IAEnF,YAAY,OAA2B;QACrC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAA;QAC9C,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAA;IAC5D,CAAC;CACF","sourcesContent":["export type ImagePickerAsset = {\n uri: string\n assetId?: string | null\n width: number\n height: number\n mimeType?: string\n fileName?: string | null\n fileSize?: number\n}\n\ntype ImagePickerSuccessResult = {\n canceled: false\n assets: ImagePickerAsset[]\n}\n\ntype ImagePickerCanceledResult = {\n canceled: true\n assets: null\n}\n\nexport type ImagePickerResult = ImagePickerSuccessResult | ImagePickerCanceledResult\n\nexport interface ImagePickerOptions {\n selectionLimit?: number\n allowsEditing?: boolean\n allowsMultipleSelection?: boolean\n mediaTypes?: Array<'images' | 'videos' | 'livePhotos'>\n}\n\nexport class ImagePickerAdapter {\n openCameraAsync: () => Promise<ImagePickerResult>\n openImageLibraryAsync: (options?: ImagePickerOptions) => Promise<ImagePickerResult>\n\n constructor(methods: ImagePickerAdapter) {\n this.openCameraAsync = methods.openCameraAsync\n this.openImageLibraryAsync = methods.openImageLibraryAsync\n }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  export * from './audio';
2
2
  export * from './clipboard';
3
3
  export * from './configuration';
4
+ export * from './document_picker';
4
5
  export * from './image_picker';
5
6
  export * from './linking';
6
7
  export * from './log';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  export * from './audio';
2
2
  export * from './clipboard';
3
3
  export * from './configuration';
4
+ export * from './document_picker';
4
5
  export * from './image_picker';
5
6
  export * from './linking';
6
7
  export * from './log';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA","sourcesContent":["export * from './audio'\nexport * from './clipboard'\nexport * from './configuration'\nexport * from './image_picker'\nexport * from './linking'\nexport * from './log'\nexport * from './video'\nexport * from './haptic'\nexport * from './storage_adapter'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/native_adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA","sourcesContent":["export * from './audio'\nexport * from './clipboard'\nexport * from './configuration'\nexport * from './document_picker'\nexport * from './image_picker'\nexport * from './linking'\nexport * from './log'\nexport * from './video'\nexport * from './haptic'\nexport * from './storage_adapter'\n"]}
@@ -0,0 +1,10 @@
1
+ export declare const getChatConfigurationRequestArgs: () => {
2
+ url: string;
3
+ data: {
4
+ fields: {
5
+ ChatConfiguration: string[];
6
+ };
7
+ };
8
+ };
9
+ export declare const getChatConfigurationQueryKey: () => import("../../hooks/use_suspense_api").RequestQueryKey;
10
+ //# sourceMappingURL=get_chat_configuration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_chat_configuration.d.ts","sourceRoot":"","sources":["../../../src/utils/request/get_chat_configuration.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,+BAA+B;;;;;;;CAe3C,CAAA;AAED,eAAO,MAAM,4BAA4B,8DAGxC,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { getRequestQueryKey } from '../../hooks/use_suspense_api';
2
+ export const getChatConfigurationRequestArgs = () => {
3
+ const url = '/me/chat_configuration';
4
+ return {
5
+ url,
6
+ data: {
7
+ fields: {
8
+ ChatConfiguration: [
9
+ 'allowed_file_extensions',
10
+ 'max_file_size_in_bytes',
11
+ 'max_attachments_per_message',
12
+ ],
13
+ },
14
+ },
15
+ };
16
+ };
17
+ export const getChatConfigurationQueryKey = () => {
18
+ const requestArgs = getChatConfigurationRequestArgs();
19
+ return getRequestQueryKey(requestArgs);
20
+ };
21
+ //# sourceMappingURL=get_chat_configuration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_chat_configuration.js","sourceRoot":"","sources":["../../../src/utils/request/get_chat_configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAEjE,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,EAAE;IAClD,MAAM,GAAG,GAAG,wBAAwB,CAAA;IAEpC,OAAO;QACL,GAAG;QACH,IAAI,EAAE;YACJ,MAAM,EAAE;gBACN,iBAAiB,EAAE;oBACjB,yBAAyB;oBACzB,wBAAwB;oBACxB,6BAA6B;iBAC9B;aACF;SACF;KACF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,EAAE;IAC/C,MAAM,WAAW,GAAG,+BAA+B,EAAE,CAAA;IACrD,OAAO,kBAAkB,CAAC,WAAW,CAAC,CAAA;AACxC,CAAC,CAAA","sourcesContent":["import { getRequestQueryKey } from '../../hooks/use_suspense_api'\n\nexport const getChatConfigurationRequestArgs = () => {\n const url = '/me/chat_configuration'\n\n return {\n url,\n data: {\n fields: {\n ChatConfiguration: [\n 'allowed_file_extensions',\n 'max_file_size_in_bytes',\n 'max_attachments_per_message',\n ],\n },\n },\n }\n}\n\nexport const getChatConfigurationQueryKey = () => {\n const requestArgs = getChatConfigurationRequestArgs()\n return getRequestQueryKey(requestArgs)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "3.32.1-rc.1",
3
+ "version": "3.33.0",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -26,7 +26,8 @@
26
26
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
27
27
  "@fortawesome/react-native-fontawesome": "^0.3.2",
28
28
  "lodash-inflection": "^1.5.0",
29
- "react-compiler-runtime": "^1.0.0"
29
+ "react-compiler-runtime": "^1.0.0",
30
+ "rn-emoji-keyboard": "^1.7.0"
30
31
  },
31
32
  "peerDependencies": {
32
33
  "@planningcenter/datetime-fmt": ">=2.0.0",
@@ -64,5 +65,5 @@
64
65
  "react-native-url-polyfill": "^2.0.0",
65
66
  "typescript": "<5.6.0"
66
67
  },
67
- "gitHead": "b56ae60405cfd6240e9645fed4fdecb245570a21"
68
+ "gitHead": "97036ad047060e6943f5d80b07618053382a7e77"
68
69
  }
@@ -0,0 +1,219 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2
+ import { renderHook, act } from '@testing-library/react-hooks'
3
+ import React from 'react'
4
+ import { useApiClient } from '../../hooks/use_api_client'
5
+ import { useAttachmentUploader } from '../../hooks/use_attachment_uploader'
6
+ import { useChatConfiguration } from '../../hooks/use_chat_configuration'
7
+ import { useUploadClient } from '../../hooks/use_upload_client'
8
+ import { FileAttachment } from '../../types/resources/denormalized_attachment_resource_for_create'
9
+
10
+ jest.mock('../../hooks/use_api_client')
11
+ jest.mock('../../hooks/use_upload_client')
12
+ jest.mock('../../hooks/use_chat_configuration')
13
+
14
+ const mockedUseApiClient = useApiClient as jest.MockedFunction<typeof useApiClient>
15
+ const mockedUseUploadClient = useUploadClient as jest.MockedFunction<typeof useUploadClient>
16
+ const mockedUseChatConfiguration = useChatConfiguration as jest.MockedFunction<
17
+ typeof useChatConfiguration
18
+ >
19
+
20
+ const createWrapper = () => {
21
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
22
+ return ({ children }: { children: React.ReactNode }) => (
23
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
24
+ )
25
+ }
26
+
27
+ const setChatConfiguration = (overrides: Partial<ReturnType<typeof useChatConfiguration>> = {}) => {
28
+ mockedUseChatConfiguration.mockReturnValue({
29
+ allowedFileExtensions: ['.pdf', '.jpg'],
30
+ maxFileSizeInBytes: 50 * 1024 * 1024,
31
+ maxAttachmentsPerMessage: 10,
32
+ ...overrides,
33
+ })
34
+ }
35
+
36
+ const renderUploader = (opts?: { draftAttachments?: FileAttachment[] }) =>
37
+ renderHook(() => useAttachmentUploader({ conversationId: 1, ...opts }), {
38
+ wrapper: createWrapper(),
39
+ })
40
+
41
+ const makeFile = (
42
+ overrides: Partial<
43
+ Parameters<ReturnType<typeof useAttachmentUploader>['handleFilesAttached']>[0][number]
44
+ > = {}
45
+ ) => ({
46
+ uri: 'file:///tmp/example.pdf',
47
+ name: 'example.pdf',
48
+ type: 'application/pdf',
49
+ size: 1024,
50
+ ...overrides,
51
+ })
52
+
53
+ const draftAttachment = (name: string): FileAttachment => ({
54
+ file: { uri: `file:///tmp/${name}`, name, type: 'application/pdf', size: 1024 },
55
+ status: 'success',
56
+ uploadedAt: 0,
57
+ id: `att-${name}`,
58
+ })
59
+
60
+ beforeEach(() => {
61
+ setChatConfiguration()
62
+ mockedUseApiClient.mockReturnValue({
63
+ chat: { post: jest.fn().mockResolvedValue({ data: { id: 'msg-attachment-1' } }) },
64
+ } as any)
65
+ mockedUseUploadClient.mockReturnValue({
66
+ uploadFile: jest.fn().mockResolvedValue({ id: 'uploaded-1' }),
67
+ } as any)
68
+ })
69
+
70
+ afterEach(() => {
71
+ jest.clearAllMocks()
72
+ })
73
+
74
+ describe('useAttachmentUploader', () => {
75
+ describe('extension validation', () => {
76
+ it('rejects a disallowed extension and lists it in the error', () => {
77
+ const { result } = renderUploader()
78
+
79
+ act(() => {
80
+ result.current.handleFilesAttached([makeFile({ name: 'evil.exe' })])
81
+ })
82
+
83
+ expect(result.current.errorMessage).toContain(
84
+ 'The following file types are not supported: exe'
85
+ )
86
+ expect(result.current.attachments).toHaveLength(0)
87
+ })
88
+
89
+ it('accepts a matching extension regardless of casing', async () => {
90
+ const { result } = renderUploader()
91
+
92
+ await act(async () => {
93
+ result.current.handleFilesAttached([makeFile({ name: 'Shouting.PDF' })])
94
+ })
95
+
96
+ expect(result.current.errorMessage).toBeNull()
97
+ expect(result.current.attachments).toHaveLength(1)
98
+ })
99
+ })
100
+
101
+ describe('file size validation', () => {
102
+ it('rejects files that exceed maxFileSizeInBytes', () => {
103
+ setChatConfiguration({ maxFileSizeInBytes: 50 * 1024 * 1024 })
104
+ const { result } = renderUploader()
105
+
106
+ act(() => {
107
+ result.current.handleFilesAttached([makeFile({ size: 50 * 1024 * 1024 + 1 })])
108
+ })
109
+
110
+ expect(result.current.errorMessage).toBe('File size exceeds 50 MB')
111
+ expect(result.current.attachments).toHaveLength(0)
112
+ })
113
+
114
+ it('formats an integer MB limit without a trailing decimal', () => {
115
+ setChatConfiguration({ maxFileSizeInBytes: 25 * 1024 * 1024 })
116
+ const { result } = renderUploader()
117
+
118
+ act(() => {
119
+ result.current.handleFilesAttached([makeFile({ size: 25 * 1024 * 1024 + 1 })])
120
+ })
121
+
122
+ expect(result.current.errorMessage).toBe('File size exceeds 25 MB')
123
+ })
124
+
125
+ it('formats a fractional MB limit to one decimal', () => {
126
+ // 50.5 MB — server could in principle return a non-multiple-of-MB cap.
127
+ setChatConfiguration({ maxFileSizeInBytes: Math.round(50.5 * 1024 * 1024) })
128
+ const { result } = renderUploader()
129
+
130
+ act(() => {
131
+ result.current.handleFilesAttached([makeFile({ size: 51 * 1024 * 1024 })])
132
+ })
133
+
134
+ expect(result.current.errorMessage).toBe('File size exceeds 50.5 MB')
135
+ })
136
+ })
137
+
138
+ describe('attachment count validation', () => {
139
+ it('rejects a batch that would push past maxAttachmentsPerMessage', () => {
140
+ setChatConfiguration({ maxAttachmentsPerMessage: 2 })
141
+ const { result } = renderUploader()
142
+
143
+ act(() => {
144
+ result.current.handleFilesAttached([
145
+ makeFile({ uri: 'file:///a.pdf', name: 'a.pdf' }),
146
+ makeFile({ uri: 'file:///b.pdf', name: 'b.pdf' }),
147
+ makeFile({ uri: 'file:///c.pdf', name: 'c.pdf' }),
148
+ ])
149
+ })
150
+
151
+ expect(result.current.errorMessage).toBe("You can't attach more than 2 files at once.")
152
+ expect(result.current.attachments).toHaveLength(0)
153
+ })
154
+ })
155
+
156
+ describe('remainingAttachable', () => {
157
+ it('reports how many more files can be attached', () => {
158
+ setChatConfiguration({ maxAttachmentsPerMessage: 5 })
159
+ const { result } = renderUploader({
160
+ draftAttachments: [draftAttachment('a.pdf'), draftAttachment('b.pdf')],
161
+ })
162
+
163
+ expect(result.current.remainingAttachable).toBe(3)
164
+ })
165
+
166
+ it('clamps to 0 when the draft already exceeds the server limit', () => {
167
+ // Simulates a saved draft whose attachments outnumber a since-lowered
168
+ // server cap — the value must never go negative.
169
+ setChatConfiguration({ maxAttachmentsPerMessage: 1 })
170
+ const { result } = renderUploader({
171
+ draftAttachments: [draftAttachment('a.pdf'), draftAttachment('b.pdf')],
172
+ })
173
+
174
+ expect(result.current.remainingAttachable).toBe(0)
175
+ })
176
+ })
177
+
178
+ describe('upload errors', () => {
179
+ const rejectingResponse = (body: unknown, status = 500) => {
180
+ const response = { status, clone: () => response, json: async () => body }
181
+ return response as unknown as Response
182
+ }
183
+
184
+ it('shows a generic error message when the upload fails', async () => {
185
+ mockedUseApiClient.mockReturnValue({
186
+ chat: { post: jest.fn().mockRejectedValue(rejectingResponse({})) },
187
+ } as any)
188
+
189
+ const { result } = renderUploader()
190
+
191
+ await act(async () => {
192
+ result.current.handleFilesAttached([makeFile()])
193
+ })
194
+ await act(async () => {
195
+ await Promise.resolve()
196
+ })
197
+
198
+ expect(result.current.errorMessage).toBe('This file could not be uploaded.')
199
+ })
200
+
201
+ it('does not overwrite the error message when the upload is flagged', async () => {
202
+ mockedUseUploadClient.mockReturnValue({
203
+ uploadFile: jest.fn().mockRejectedValue({ code: 'image_flagged' }),
204
+ } as any)
205
+
206
+ const { result } = renderUploader()
207
+
208
+ await act(async () => {
209
+ result.current.handleFilesAttached([makeFile()])
210
+ })
211
+ await act(async () => {
212
+ await Promise.resolve()
213
+ })
214
+
215
+ expect(result.current.errorMessage).toBeNull()
216
+ expect(result.current.flaggedAttachmentCount).toBe(1)
217
+ })
218
+ })
219
+ })
@@ -0,0 +1,80 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2
+ import { renderHook, act } from '@testing-library/react-hooks'
3
+ import React, { Suspense } from 'react'
4
+ import {
5
+ FALLBACK_ALLOWED_FILE_EXTENSIONS,
6
+ FALLBACK_MAX_ATTACHMENTS_PER_MESSAGE,
7
+ FALLBACK_MAX_FILE_SIZE_IN_BYTES,
8
+ } from '../../hooks/attachments/fallback_chat_configuration'
9
+ import * as useApiClientModule from '../../hooks/use_api_client'
10
+ import { useChatConfiguration } from '../../hooks/use_chat_configuration'
11
+
12
+ const createWrapper = () => {
13
+ const queryClient = new QueryClient({
14
+ defaultOptions: { queries: { retry: false } },
15
+ })
16
+
17
+ return ({ children }: { children: React.ReactNode }) => (
18
+ <QueryClientProvider client={queryClient}>
19
+ <Suspense fallback={null}>{children}</Suspense>
20
+ </QueryClientProvider>
21
+ )
22
+ }
23
+
24
+ const waitForQuery = async () => {
25
+ await act(async () => {
26
+ await Promise.resolve()
27
+ await Promise.resolve()
28
+ await Promise.resolve()
29
+ })
30
+ }
31
+
32
+ const mockApiClient = (getImpl: () => Promise<unknown>) => {
33
+ jest.spyOn(useApiClientModule, 'useApiClient').mockReturnValue({
34
+ chat: { get: jest.fn(getImpl) },
35
+ } as any)
36
+ }
37
+
38
+ describe('useChatConfiguration', () => {
39
+ afterEach(() => {
40
+ jest.restoreAllMocks()
41
+ })
42
+
43
+ it('returns server-provided values when the API succeeds', async () => {
44
+ mockApiClient(() =>
45
+ Promise.resolve({
46
+ data: {
47
+ type: 'ChatConfiguration',
48
+ id: 'current',
49
+ allowedFileExtensions: ['.pdf', '.jpg'],
50
+ maxFileSizeInBytes: 1000,
51
+ maxAttachmentsPerMessage: 3,
52
+ },
53
+ links: {},
54
+ meta: {},
55
+ })
56
+ )
57
+
58
+ const { result } = renderHook(() => useChatConfiguration(), { wrapper: createWrapper() })
59
+ await waitForQuery()
60
+
61
+ expect(result.current).toEqual({
62
+ allowedFileExtensions: ['.pdf', '.jpg'],
63
+ maxFileSizeInBytes: 1000,
64
+ maxAttachmentsPerMessage: 3,
65
+ })
66
+ })
67
+
68
+ it('returns fallback values when the API rejects', async () => {
69
+ mockApiClient(() => Promise.reject(new Error('boom')))
70
+
71
+ const { result } = renderHook(() => useChatConfiguration(), { wrapper: createWrapper() })
72
+ await waitForQuery()
73
+
74
+ expect(result.current).toEqual({
75
+ allowedFileExtensions: FALLBACK_ALLOWED_FILE_EXTENSIONS,
76
+ maxFileSizeInBytes: FALLBACK_MAX_FILE_SIZE_IN_BYTES,
77
+ maxAttachmentsPerMessage: FALLBACK_MAX_ATTACHMENTS_PER_MESSAGE,
78
+ })
79
+ })
80
+ })
@@ -4,11 +4,17 @@ import {
4
4
  AudioAdapter,
5
5
  Clipboard,
6
6
  ClipboardAdapter,
7
+ DocumentPickerAdapter,
7
8
  ImagePickerAdapter,
8
9
  LinkingAdapter,
9
10
  VideoAdapter,
10
11
  } from '../../../utils/native_adapters'
11
- import { ChatAdapters, Linking, Haptic } from '../../../utils/native_adapters/configuration'
12
+ import {
13
+ ChatAdapters,
14
+ DocumentPicker,
15
+ Linking,
16
+ Haptic,
17
+ } from '../../../utils/native_adapters/configuration'
12
18
  import { HapticAdapter } from '../../../utils/native_adapters/haptic'
13
19
  import { VideoPlayerHandle, VideoPlayerProps } from '../../../utils/native_adapters/video'
14
20
 
@@ -104,6 +110,24 @@ describe('ChatAdapters', () => {
104
110
  })
105
111
  })
106
112
 
113
+ describe('document picker adapter', () => {
114
+ it('uses the configured adapter when provided', () => {
115
+ const documentPicker = new DocumentPickerAdapter({
116
+ openAsync: jest.fn(async () => ({ canceled: true, assets: null })),
117
+ })
118
+
119
+ ChatAdapters.configure({
120
+ clipboard,
121
+ audio,
122
+ video,
123
+ imagePicker,
124
+ documentPicker,
125
+ })
126
+
127
+ expect(DocumentPicker).toEqual(documentPicker)
128
+ })
129
+ })
130
+
107
131
  describe('haptic adapter', () => {
108
132
  it('should configure the haptic adapter', () => {
109
133
  ChatAdapters.configure({
@@ -36,7 +36,13 @@ import {
36
36
  platformFontWeightMedium,
37
37
  platformPressedOpacityStyle,
38
38
  } from '../../utils'
39
- import { Haptic, ImagePicker, ImagePickerResult } from '../../utils/native_adapters'
39
+ import {
40
+ DocumentPicker,
41
+ DocumentPickerResult,
42
+ Haptic,
43
+ ImagePicker,
44
+ ImagePickerResult,
45
+ } from '../../utils/native_adapters'
40
46
  import { tokens } from '../../vendor/tapestry/tokens'
41
47
  import { Button } from '../display/button'
42
48
  import BannerPrimitive from '../primitive/banner_primitive'
@@ -522,6 +528,21 @@ function MessageFormAttachmentPicker() {
522
528
  attachmentUploader?.handleFilesAttached(filteredAssets)
523
529
  }
524
530
 
531
+ function uploadDocumentPickerResult(result: DocumentPickerResult) {
532
+ if (result.canceled) return
533
+
534
+ const filteredAssets = result.assets
535
+ .filter(asset => asset.size != null && asset.uri)
536
+ .map(asset => ({
537
+ uri: asset.uri,
538
+ name: asset.name,
539
+ type: asset.mimeType || 'application/octet-stream',
540
+ size: asset.size as number,
541
+ }))
542
+
543
+ attachmentUploader?.handleFilesAttached(filteredAssets)
544
+ }
545
+
525
546
  const openCamera = async () => {
526
547
  setIsOpen(false)
527
548
  let result = await ImagePicker.openCameraAsync()
@@ -538,6 +559,14 @@ function MessageFormAttachmentPicker() {
538
559
  }
539
560
  }
540
561
 
562
+ const pickFile = async () => {
563
+ setIsOpen(false)
564
+ let result = await DocumentPicker.openAsync()
565
+ if (!result.canceled) {
566
+ uploadDocumentPickerResult(result)
567
+ }
568
+ }
569
+
541
570
  if (usingGiphy || currentlyEditingMessage) {
542
571
  return null
543
572
  }
@@ -564,6 +593,15 @@ function MessageFormAttachmentPicker() {
564
593
  onPress={pickImage}
565
594
  style={styles.attachmentPickerButton}
566
595
  />
596
+ <IconButton
597
+ accessibilityLabel="Attach a file"
598
+ accessibilityHint="Opens your files to attach documents"
599
+ size="lg"
600
+ appearance="neutral"
601
+ name="general.blankFile"
602
+ onPress={pickFile}
603
+ style={styles.attachmentPickerButton}
604
+ />
567
605
  </View>
568
606
  )}
569
607
  <IconButton
@@ -1,14 +1,19 @@
1
1
  import React from 'react'
2
2
  import { StyleSheet, Text, View } from 'react-native'
3
3
  import LinearGradient from 'react-native-linear-gradient'
4
- import AvatarPrimitive, { type AvatarRootProps } from '../primitive/avatar_primitive'
4
+ import AvatarPrimitive, {
5
+ type AvatarRootProps,
6
+ type AvatarSize,
7
+ } from '../primitive/avatar_primitive'
5
8
  import { getAvatarGradientProps } from './utils/avatar_gradient_colors'
6
9
 
7
- const EMOJI_SIZE: Record<string, number> = {
10
+ const EMOJI_SIZE: Record<AvatarSize, number> = {
8
11
  xs: 8,
9
12
  sm: 10,
10
13
  md: 14,
11
14
  lg: 20,
15
+ xl: 28,
16
+ '2xl': 40,
12
17
  }
13
18
 
14
19
  interface EmojiAvatarProps {
@@ -7,14 +7,19 @@ import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'
7
7
  import React from 'react'
8
8
  import { StyleSheet, Text, View } from 'react-native'
9
9
  import LinearGradient from 'react-native-linear-gradient'
10
- import AvatarPrimitive, { type AvatarRootProps } from '../primitive/avatar_primitive'
10
+ import AvatarPrimitive, {
11
+ type AvatarRootProps,
12
+ type AvatarSize,
13
+ } from '../primitive/avatar_primitive'
11
14
  import { getAvatarGradientProps } from './utils/avatar_gradient_colors'
12
15
 
13
- const ICON_SIZE: Record<string, number> = {
16
+ const ICON_SIZE: Record<AvatarSize, number> = {
14
17
  xs: 10,
15
18
  sm: 12,
16
19
  md: 16,
17
20
  lg: 20,
21
+ xl: 28,
22
+ '2xl': 40,
18
23
  }
19
24
 
20
25
  interface IconAvatarProps {