@planningcenter/chat-react-native 3.2.0-rc.26 → 3.2.0-rc.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/build/components/conversation/message_form/message_form_attachment_image.d.ts +13 -0
  2. package/build/components/conversation/message_form/message_form_attachment_image.d.ts.map +1 -0
  3. package/build/components/conversation/message_form/message_form_attachment_image.js +78 -0
  4. package/build/components/conversation/message_form/message_form_attachment_image.js.map +1 -0
  5. package/build/components/conversation/message_form.d.ts.map +1 -1
  6. package/build/components/conversation/message_form.js +128 -16
  7. package/build/components/conversation/message_form.js.map +1 -1
  8. package/build/hooks/attachments/supported_extensions.d.ts +2 -0
  9. package/build/hooks/attachments/supported_extensions.d.ts.map +1 -0
  10. package/build/hooks/attachments/supported_extensions.js +48 -0
  11. package/build/hooks/attachments/supported_extensions.js.map +1 -0
  12. package/build/hooks/use_attachment_uploader.d.ts +26 -0
  13. package/build/hooks/use_attachment_uploader.d.ts.map +1 -0
  14. package/build/hooks/use_attachment_uploader.js +111 -0
  15. package/build/hooks/use_attachment_uploader.js.map +1 -0
  16. package/build/hooks/use_upload_client.d.ts +28 -0
  17. package/build/hooks/use_upload_client.d.ts.map +1 -0
  18. package/build/hooks/use_upload_client.js +32 -0
  19. package/build/hooks/use_upload_client.js.map +1 -0
  20. package/build/screens/conversation_screen.js +1 -1
  21. package/build/screens/conversation_screen.js.map +1 -1
  22. package/build/utils/native_adapters/configuration.d.ts +4 -1
  23. package/build/utils/native_adapters/configuration.d.ts.map +1 -1
  24. package/build/utils/native_adapters/configuration.js +13 -1
  25. package/build/utils/native_adapters/configuration.js.map +1 -1
  26. package/build/utils/native_adapters/image_picker.d.ts +25 -0
  27. package/build/utils/native_adapters/image_picker.d.ts.map +1 -0
  28. package/build/utils/native_adapters/image_picker.js +9 -0
  29. package/build/utils/native_adapters/image_picker.js.map +1 -0
  30. package/build/utils/native_adapters/index.d.ts +1 -0
  31. package/build/utils/native_adapters/index.d.ts.map +1 -1
  32. package/build/utils/native_adapters/index.js +1 -0
  33. package/build/utils/native_adapters/index.js.map +1 -1
  34. package/build/utils/upload_uri.d.ts +23 -0
  35. package/build/utils/upload_uri.d.ts.map +1 -0
  36. package/build/utils/upload_uri.js +60 -0
  37. package/build/utils/upload_uri.js.map +1 -0
  38. package/package.json +2 -2
  39. package/src/components/conversation/message_form/message_form_attachment_image.tsx +121 -0
  40. package/src/components/conversation/message_form.tsx +197 -31
  41. package/src/hooks/attachments/supported_extensions.ts +47 -0
  42. package/src/hooks/use_attachment_uploader.ts +179 -0
  43. package/src/hooks/use_upload_client.ts +67 -0
  44. package/src/screens/conversation_screen.tsx +1 -1
  45. package/src/utils/native_adapters/configuration.ts +15 -1
  46. package/src/utils/native_adapters/image_picker.ts +31 -0
  47. package/src/utils/native_adapters/index.ts +1 -0
  48. package/src/utils/upload_uri.ts +69 -0
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { FileAttachment } from '../../../hooks/use_attachment_uploader';
3
+ interface Props {
4
+ uri: string;
5
+ alt: string;
6
+ status: FileAttachment['status'];
7
+ width?: number;
8
+ height?: number;
9
+ removeAttachment: () => void;
10
+ }
11
+ export declare function MessageFormAttachmentImage({ uri, alt, status, removeAttachment }: Props): React.JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=message_form_attachment_image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message_form_attachment_image.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation/message_form/message_form_attachment_image.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAA;AAIvE,UAAU,KAAK;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,wBAAgB,0BAA0B,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,KAAK,qBA8CvF"}
@@ -0,0 +1,78 @@
1
+ import React from 'react';
2
+ import { Image as ReactNativeImage, StyleSheet, View, } from 'react-native';
3
+ import { useTheme } from '../../../hooks';
4
+ import { Icon, IconButton, Spinner } from '../../display';
5
+ export function MessageFormAttachmentImage({ uri, alt, status, removeAttachment }) {
6
+ function opacity() {
7
+ if (status === 'uploading') {
8
+ return 0.5;
9
+ }
10
+ return 1;
11
+ }
12
+ const styles = useStyles({
13
+ width: 50,
14
+ height: 50,
15
+ borderRadius: 8,
16
+ });
17
+ const loading = status === 'uploading';
18
+ const error = status === 'error';
19
+ const ready = status === 'success';
20
+ return (<View accessible={Boolean(alt)} accessibilityRole="image" accessibilityState={{ busy: loading }}>
21
+ <ReactNativeImage source={{ uri }} style={[styles.image, { opacity: opacity() }]} alt={alt}/>
22
+ {ready && (<IconButton name="general.x" accessibilityLabel="Remove Attachment" size="md" style={styles.removeAttachmentIcon} onPress={removeAttachment}/>)}
23
+ {loading && (<View style={[styles.loadingBackground]}>
24
+ <Spinner size={24}/>
25
+ </View>)}
26
+ {error && (<View style={styles.errorBackground}>
27
+ <View style={styles.errorIconBackground}>
28
+ <Icon name="churchCenter.exclamationCircle" size={18} color="red"/>
29
+ </View>
30
+ </View>)}
31
+ </View>);
32
+ }
33
+ const useStyles = ({ width, height, borderRadius }) => {
34
+ const { colors } = useTheme();
35
+ return StyleSheet.create({
36
+ image: {
37
+ backgroundColor: colors.fillColorNeutral070,
38
+ width,
39
+ height,
40
+ borderRadius,
41
+ },
42
+ removeAttachmentIcon: {
43
+ position: 'absolute',
44
+ top: 4,
45
+ right: 4,
46
+ backgroundColor: 'rgba(255, 255, 255, 0.5)',
47
+ borderRadius: 24,
48
+ width: 24,
49
+ height: 24,
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
52
+ },
53
+ loadingBackground: {
54
+ position: 'absolute',
55
+ top: 0,
56
+ left: 0,
57
+ borderRadius,
58
+ width,
59
+ height,
60
+ },
61
+ errorBackground: {
62
+ position: 'absolute',
63
+ top: 0,
64
+ left: 0,
65
+ borderRadius,
66
+ width,
67
+ height,
68
+ justifyContent: 'center',
69
+ alignItems: 'center',
70
+ },
71
+ errorIconBackground: {
72
+ backgroundColor: 'white',
73
+ padding: 2,
74
+ borderRadius: 50,
75
+ },
76
+ });
77
+ };
78
+ //# sourceMappingURL=message_form_attachment_image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message_form_attachment_image.js","sourceRoot":"","sources":["../../../../src/components/conversation/message_form/message_form_attachment_image.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAGL,KAAK,IAAI,gBAAgB,EACzB,UAAU,EACV,IAAI,GACL,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAWzD,MAAM,UAAU,0BAA0B,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAS;IACtF,SAAS,OAAO;QACd,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,OAAO,CAAC,CAAA;IACV,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC;QACvB,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,EAAE;QACV,YAAY,EAAE,CAAC;KAChB,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,MAAM,KAAK,WAAW,CAAA;IACtC,MAAM,KAAK,GAAG,MAAM,KAAK,OAAO,CAAA;IAChC,MAAM,KAAK,GAAG,MAAM,KAAK,SAAS,CAAA;IAElC,OAAO,CACL,CAAC,IAAI,CACH,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CACzB,iBAAiB,CAAC,OAAO,CACzB,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAEtC;MAAA,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAC3F;MAAA,CAAC,KAAK,IAAI,CACR,CAAC,UAAU,CACT,IAAI,CAAC,WAAW,CAChB,kBAAkB,CAAC,mBAAmB,CACtC,IAAI,CAAC,IAAI,CACT,KAAK,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC,CACnC,OAAO,CAAC,CAAC,gBAAgB,CAAC,EAC1B,CACH,CACD;MAAA,CAAC,OAAO,IAAI,CACV,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CACtC;UAAA,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EACpB;QAAA,EAAE,IAAI,CAAC,CACR,CACD;MAAA,CAAC,KAAK,IAAI,CACR,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAClC;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,CACtC;YAAA,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EACnE;UAAA,EAAE,IAAI,CACR;QAAA,EAAE,IAAI,CAAC,CACR,CACH;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AAQD,MAAM,SAAS,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAU,EAAE,EAAE;IAC5D,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE7B,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,KAAK,EAAE;YACL,eAAe,EAAE,MAAM,CAAC,mBAAmB;YAC3C,KAAK;YACL,MAAM;YACN,YAAY;SACb;QACD,oBAAoB,EAAE;YACpB,QAAQ,EAAE,UAAU;YACpB,GAAG,EAAE,CAAC;YACN,KAAK,EAAE,CAAC;YACR,eAAe,EAAE,0BAA0B;YAC3C,YAAY,EAAE,EAAE;YAChB,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;YACV,cAAc,EAAE,QAAQ;YACxB,UAAU,EAAE,QAAQ;SACrB;QACD,iBAAiB,EAAE;YACjB,QAAQ,EAAE,UAAU;YACpB,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,CAAC;YACP,YAAY;YACZ,KAAK;YACL,MAAM;SACP;QACD,eAAe,EAAE;YACf,QAAQ,EAAE,UAAU;YACpB,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,CAAC;YACP,YAAY;YACZ,KAAK;YACL,MAAM;YACN,cAAc,EAAE,QAAQ;YACxB,UAAU,EAAE,QAAQ;SACrB;QACD,mBAAmB,EAAE;YACnB,eAAe,EAAE,OAAO;YACxB,OAAO,EAAE,CAAC;YACV,YAAY,EAAE,EAAE;SACjB;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import React from 'react'\nimport {\n AnimatableNumericValue,\n DimensionValue,\n Image as ReactNativeImage,\n StyleSheet,\n View,\n} from 'react-native'\nimport { FileAttachment } from '../../../hooks/use_attachment_uploader'\nimport { useTheme } from '../../../hooks'\nimport { Icon, IconButton, Spinner } from '../../display'\n\ninterface Props {\n uri: string\n alt: string\n status: FileAttachment['status']\n width?: number\n height?: number\n removeAttachment: () => void\n}\n\nexport function MessageFormAttachmentImage({ uri, alt, status, removeAttachment }: Props) {\n function opacity() {\n if (status === 'uploading') {\n return 0.5\n }\n return 1\n }\n const styles = useStyles({\n width: 50,\n height: 50,\n borderRadius: 8,\n })\n const loading = status === 'uploading'\n const error = status === 'error'\n const ready = status === 'success'\n\n return (\n <View\n accessible={Boolean(alt)}\n accessibilityRole=\"image\"\n accessibilityState={{ busy: loading }}\n >\n <ReactNativeImage source={{ uri }} style={[styles.image, { opacity: opacity() }]} alt={alt} />\n {ready && (\n <IconButton\n name=\"general.x\"\n accessibilityLabel=\"Remove Attachment\"\n size=\"md\"\n style={styles.removeAttachmentIcon}\n onPress={removeAttachment}\n />\n )}\n {loading && (\n <View style={[styles.loadingBackground]}>\n <Spinner size={24} />\n </View>\n )}\n {error && (\n <View style={styles.errorBackground}>\n <View style={styles.errorIconBackground}>\n <Icon name=\"churchCenter.exclamationCircle\" size={18} color=\"red\" />\n </View>\n </View>\n )}\n </View>\n )\n}\n\ninterface Styles {\n width: DimensionValue\n height: DimensionValue\n borderRadius: AnimatableNumericValue | string\n}\n\nconst useStyles = ({ width, height, borderRadius }: Styles) => {\n const { colors } = useTheme()\n\n return StyleSheet.create({\n image: {\n backgroundColor: colors.fillColorNeutral070,\n width,\n height,\n borderRadius,\n },\n removeAttachmentIcon: {\n position: 'absolute',\n top: 4,\n right: 4,\n backgroundColor: 'rgba(255, 255, 255, 0.5)',\n borderRadius: 24,\n width: 24,\n height: 24,\n justifyContent: 'center',\n alignItems: 'center',\n },\n loadingBackground: {\n position: 'absolute',\n top: 0,\n left: 0,\n borderRadius,\n width,\n height,\n },\n errorBackground: {\n position: 'absolute',\n top: 0,\n left: 0,\n borderRadius,\n width,\n height,\n justifyContent: 'center',\n alignItems: 'center',\n },\n errorIconBackground: {\n backgroundColor: 'white',\n padding: 2,\n borderRadius: 50,\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"message_form.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/message_form.tsx"],"names":[],"mappings":"AACA,OAAO,KAA0C,MAAM,OAAO,CAAA;AAC9D,OAAO,EAA+B,SAAS,EAAE,MAAM,cAAc,CAAA;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAKlD,eAAO,MAAM,WAAW;;;;;;CAMvB,CAAA;AAED,UAAU,qBAAsB,SAAQ,SAAS;IAC/C,YAAY,EAAE,oBAAoB,CAAA;CACnC;AAYD,iBAAS,eAAe,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,qBAAqB,qBAsEzE;AAED,iBAAS,gBAAgB,sBAqBxB;AAED,iBAAS,oBAAoB,sBAe5B;AAED,iBAAS,2BAA2B,6BAenC;AAED,iBAAS,mBAAmB,6BA4B3B"}
1
+ {"version":3,"file":"message_form.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/message_form.tsx"],"names":[],"mappings":"AACA,OAAO,KAAuD,MAAM,OAAO,CAAA;AAC3E,OAAO,EAA+B,SAAS,EAAE,MAAM,cAAc,CAAA;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAYlD,eAAO,MAAM,WAAW;;;;;;CAMvB,CAAA;AAED,UAAU,qBAAsB,SAAQ,SAAS;IAC/C,YAAY,EAAE,oBAAoB,CAAA;CACnC;AAqBD,iBAAS,eAAe,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,qBAAqB,qBAkGzE;AAiCD,iBAAS,gBAAgB,sBA4BxB;AAED,iBAAS,oBAAoB,sBAe5B;AAED,iBAAS,2BAA2B,6BA8EnC;AAED,iBAAS,mBAAmB,6BA4B3B"}
@@ -1,10 +1,13 @@
1
1
  import { useNavigation, useTheme as useNavigationTheme, useRoute } from '@react-navigation/native';
2
- import React, { useContext, useEffect, useState } from 'react';
2
+ import React, { useCallback, useContext, useEffect, useState } from 'react';
3
3
  import { StyleSheet, TextInput, View } from 'react-native';
4
4
  import { IconButton, Text } from '../../components';
5
5
  import { useTheme } from '../../hooks';
6
6
  import { useMessageCreate } from '../../hooks/use_message_create';
7
7
  import { ChatContext } from '../../contexts/chat_context';
8
+ import { ImagePicker } from '../../utils/native_adapters';
9
+ import { useAttachmentUploader } from '../../hooks/use_attachment_uploader';
10
+ import { MessageFormAttachmentImage } from './message_form/message_form_attachment_image';
8
11
  export const MessageForm = {
9
12
  Root: MessageFormRoot,
10
13
  TextInput: MessageFormInput,
@@ -29,7 +32,19 @@ function MessageFormRoot({ conversation, children }) {
29
32
  const [usingGiphy, setUsingGiphy] = useState(false);
30
33
  const navigation = useNavigation();
31
34
  const route = useRoute();
32
- const { status, isPending, reset, mutate } = useMessageCreate({ conversationId: conversation.id });
35
+ const { status, isPending, reset: resetMutation, mutate, } = useMessageCreate({
36
+ conversationId: conversation.id,
37
+ });
38
+ const attachmentUploader = useAttachmentUploader({
39
+ conversationId: conversation.id,
40
+ });
41
+ const resetAttachmentUploader = attachmentUploader.reset;
42
+ const reset = useCallback(() => {
43
+ resetAttachmentUploader();
44
+ resetMutation();
45
+ setText('');
46
+ setUsingGiphy(false);
47
+ }, [resetAttachmentUploader, resetMutation]);
33
48
  useEffect(() => {
34
49
  if (canGiphy && !usingGiphy && text.startsWith('/giphy ')) {
35
50
  setUsingGiphy(true);
@@ -39,23 +54,25 @@ function MessageFormRoot({ conversation, children }) {
39
54
  useEffect(() => {
40
55
  switch (status) {
41
56
  case 'success':
42
- setText('');
43
57
  reset();
44
58
  break;
45
59
  }
46
60
  }, [reset, status]);
47
61
  useEffect(() => {
48
62
  if (route.params.clear_input) {
49
- setText('');
50
- setUsingGiphy(false);
63
+ reset();
51
64
  navigation.setParams({ ...route.params, clear_input: false });
52
65
  }
53
- }, [navigation, route.params]);
66
+ }, [reset, navigation, route.params]);
54
67
  const canSubmit = (() => {
55
68
  if (isPending)
56
69
  return false;
70
+ if (attachmentUploader?.pendingUploads)
71
+ return false;
57
72
  if (text.length > 0)
58
73
  return true;
74
+ if (attachmentUploader?.attachments?.length)
75
+ return true;
59
76
  return false;
60
77
  })();
61
78
  const disabled = !canSubmit;
@@ -70,7 +87,14 @@ function MessageFormRoot({ conversation, children }) {
70
87
  });
71
88
  }
72
89
  else {
73
- mutate({ text });
90
+ let attachmentsForSubmit = [];
91
+ if (attachmentUploader?.attachmentIds) {
92
+ attachmentsForSubmit = attachmentUploader.attachmentIds.map((id) => ({
93
+ type: 'MessageAttachment',
94
+ id,
95
+ }));
96
+ }
97
+ mutate({ text, attachments: attachmentsForSubmit });
74
98
  }
75
99
  };
76
100
  return (<MessageFormContext.Provider value={{
@@ -81,19 +105,42 @@ function MessageFormRoot({ conversation, children }) {
81
105
  canGiphy,
82
106
  usingGiphy,
83
107
  setUsingGiphy,
108
+ attachmentUploader,
84
109
  }}>
85
110
  <View style={styles.textInputContainer}>{children}</View>
86
111
  </MessageFormContext.Provider>);
87
112
  }
113
+ function MessageFormAttachments() {
114
+ const styles = useMessageFormStyles();
115
+ const { attachmentUploader } = React.useContext(MessageFormContext);
116
+ const numberOfAttachments = attachmentUploader?.attachments?.length || 0;
117
+ const attachments = attachmentUploader?.attachments || [];
118
+ if (numberOfAttachments === 0) {
119
+ return null;
120
+ }
121
+ return (<View style={styles.messageFormAttachments}>
122
+ {attachments.map(attachment => {
123
+ return (<MessageFormAttachmentImage key={attachment.file.uri} uri={attachment.file.uri} alt={attachment.file.name} status={attachment.status} width={attachment.file.width} height={attachment.file.height} removeAttachment={() => {
124
+ attachmentUploader?.removeAttachment(attachment);
125
+ }}/>);
126
+ })}
127
+ </View>);
128
+ }
88
129
  function MessageFormInput() {
89
130
  const styles = useMessageFormStyles();
90
- const { text, setText, onSubmit, usingGiphy } = React.useContext(MessageFormContext);
91
- return (<View style={styles.textInput}>
92
- {usingGiphy ? (<View style={styles.giphyBadge}>
93
- <Text>/Giphy</Text>
94
- </View>) : null}
131
+ const { text, setText, onSubmit, usingGiphy, attachmentUploader } = React.useContext(MessageFormContext);
132
+ const attachmentError = attachmentUploader?.errorMessage;
133
+ return (<View style={styles.textInputBoundary}>
134
+ <MessageFormAttachments />
135
+ <View style={styles.textInput}>
136
+ {usingGiphy ? (<View style={styles.giphyBadge}>
137
+ <Text>/Giphy</Text>
138
+ </View>) : null}
95
139
 
96
- <TextInput aria-disabled={true} placeholder="Send a message" onChangeText={setText} value={text} onSubmitEditing={onSubmit}/>
140
+ <TextInput aria-disabled={true} placeholder="Send a message" onChangeText={setText} value={text} onSubmitEditing={onSubmit}/>
141
+ </View>
142
+
143
+ {attachmentError ? <Text style={styles.inputErrorMessage}>{attachmentError}</Text> : null}
97
144
  </View>);
98
145
  }
99
146
  function MessageFormSubmitBtn() {
@@ -102,11 +149,55 @@ function MessageFormSubmitBtn() {
102
149
  return (<IconButton disabled={disabled} accessibilityLabel={usingGiphy ? 'Search Giphy' : 'Send message'} size="md" appearance="neutral" style={styles.textInputSend} name={usingGiphy ? 'general.search' : 'general.upArrow'} onPress={onSubmit}/>);
103
150
  }
104
151
  function MessageFormAttachmentPicker() {
105
- const { usingGiphy } = React.useContext(MessageFormContext);
152
+ const styles = useMessageFormStyles();
153
+ const { usingGiphy, attachmentUploader } = React.useContext(MessageFormContext);
154
+ const [isOpen, setIsOpen] = useState(false);
155
+ function uploadImagePickerResult(result) {
156
+ if (result.canceled) {
157
+ return;
158
+ }
159
+ const filteredAssets = result.assets
160
+ .filter(asset => {
161
+ return asset.fileSize && asset.fileName && asset.mimeType;
162
+ })
163
+ .map(asset => {
164
+ return {
165
+ uri: asset.uri,
166
+ name: asset.fileName,
167
+ type: asset.mimeType,
168
+ size: asset.fileSize,
169
+ height: asset.height,
170
+ width: asset.width,
171
+ };
172
+ });
173
+ attachmentUploader?.handleFilesAttached(filteredAssets);
174
+ }
175
+ const openCamera = async () => {
176
+ setIsOpen(false);
177
+ let result = await ImagePicker.openCameraAsync();
178
+ if (!result.canceled) {
179
+ uploadImagePickerResult(result);
180
+ }
181
+ };
182
+ const pickImage = async () => {
183
+ setIsOpen(false);
184
+ let result = await ImagePicker.openImageLibraryAsync();
185
+ if (!result.canceled) {
186
+ uploadImagePickerResult(result);
187
+ }
188
+ };
106
189
  if (usingGiphy) {
107
190
  return null;
108
191
  }
109
- return (<IconButton accessibilityLabel="Shazam" size="md" appearance="neutral" name={'general.paperclip'}/>);
192
+ return (
193
+ // TODO: Design Pass
194
+ <View style={styles.attachmentPicker}>
195
+ {isOpen && (<View style={styles.attachmentPickerButtons}>
196
+ <IconButton accessibilityLabel="Take a photo" size="md" appearance="neutral" name={'general.videoCamera'} onPress={openCamera}/>
197
+ <IconButton accessibilityLabel="Choose a photo" size="md" appearance="neutral" name={'churchCenter.photosIos'} onPress={pickImage}/>
198
+ </View>)}
199
+ <IconButton accessibilityLabel="File Menu" size="md" appearance="neutral" name={'general.outlinedPlusCircle'} onPress={() => setIsOpen(!isOpen)}/>
200
+ </View>);
110
201
  }
111
202
  function MessageFormCommands() {
112
203
  const { canGiphy, usingGiphy, setUsingGiphy } = React.useContext(MessageFormContext);
@@ -131,13 +222,16 @@ const useMessageFormStyles = () => {
131
222
  alignItems: 'center',
132
223
  gap: 12,
133
224
  },
134
- textInput: {
225
+ textInputBoundary: {
135
226
  borderRadius: 24,
136
227
  borderWidth: 1,
137
228
  padding: 12,
138
229
  paddingHorizontal: 20,
139
230
  borderColor: theme.colors.fillColorNeutral050Base,
140
231
  flex: 1,
232
+ gap: 12,
233
+ },
234
+ textInput: {
141
235
  flexDirection: 'row',
142
236
  gap: 12,
143
237
  },
@@ -152,6 +246,24 @@ const useMessageFormStyles = () => {
152
246
  height: 36,
153
247
  width: 36,
154
248
  },
249
+ attachmentPicker: {
250
+ position: 'relative',
251
+ },
252
+ attachmentPickerButtons: {
253
+ position: 'absolute',
254
+ left: 0,
255
+ bottom: 40,
256
+ zIndex: 10,
257
+ gap: 16,
258
+ },
259
+ messageFormAttachments: {
260
+ flexDirection: 'row',
261
+ gap: 8,
262
+ },
263
+ inputErrorMessage: {
264
+ color: theme.colors.statusErrorText,
265
+ fontSize: 14,
266
+ },
155
267
  });
156
268
  };
157
269
  //# sourceMappingURL=message_form.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"message_form.js","sourceRoot":"","sources":["../../../src/components/conversation/message_form.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useNavigation, useTheme as useNavigationTheme, useRoute } from '@react-navigation/native'\nimport React, { useContext, useEffect, useState } from 'react'\nimport { StyleSheet, TextInput, View, ViewProps } from 'react-native'\nimport { IconButton, Text } from '../../components'\nimport { useTheme } from '../../hooks'\nimport { ConversationResource } from '../../types'\nimport { useMessageCreate } from '../../hooks/use_message_create'\nimport { ConversationScreenProps } from '../../screens/conversation_screen'\nimport { ChatContext } from '../../contexts/chat_context'\n\nexport const MessageForm = {\n Root: MessageFormRoot,\n TextInput: MessageFormInput,\n SubmitButton: MessageFormSubmitBtn,\n AttachmentPicker: MessageFormAttachmentPicker,\n Commands: MessageFormCommands,\n}\n\ninterface MessagesFormRootProps extends ViewProps {\n conversation: ConversationResource\n}\n\nconst MessageFormContext = React.createContext({\n text: '',\n setText: (_text: string) => {},\n onSubmit: () => {},\n disabled: false,\n canGiphy: false,\n usingGiphy: false,\n setUsingGiphy: (_usingGiphy: boolean) => {},\n})\n\nfunction MessageFormRoot({ conversation, children }: MessagesFormRootProps) {\n const { giphyApiKey } = useContext(ChatContext)\n const canGiphy = !!giphyApiKey\n const styles = useMessageFormStyles()\n const [text, setText] = React.useState('')\n const [usingGiphy, setUsingGiphy] = useState(false)\n const navigation = useNavigation()\n const route = useRoute() as ConversationScreenProps['route']\n const { status, isPending, reset, mutate } = useMessageCreate({ conversationId: conversation.id })\n\n useEffect(() => {\n if (canGiphy && !usingGiphy && text.startsWith('/giphy ')) {\n setUsingGiphy(true)\n setText('')\n }\n }, [canGiphy, text, usingGiphy])\n\n useEffect(() => {\n switch (status) {\n case 'success':\n setText('')\n reset()\n break\n }\n }, [reset, status])\n\n useEffect(() => {\n if (route.params.clear_input) {\n setText('')\n setUsingGiphy(false)\n navigation.setParams({ ...route.params, clear_input: false })\n }\n }, [navigation, route.params])\n\n const canSubmit = (() => {\n if (isPending) return false\n if (text.length > 0) return true\n return false\n })()\n const disabled = !canSubmit\n\n const handleSubmit = () => {\n if (!canSubmit) return\n\n if (canGiphy && usingGiphy) {\n TextInput.State.blurTextInput(TextInput.State.currentlyFocusedInput())\n navigation.navigate('SendGiphy', {\n conversation_id: conversation.id,\n search_term: text,\n })\n } else {\n mutate({ text })\n }\n }\n\n return (\n <MessageFormContext.Provider\n value={{\n text,\n setText,\n onSubmit: handleSubmit,\n disabled,\n canGiphy,\n usingGiphy,\n setUsingGiphy,\n }}\n >\n <View style={styles.textInputContainer}>{children}</View>\n </MessageFormContext.Provider>\n )\n}\n\nfunction MessageFormInput() {\n const styles = useMessageFormStyles()\n const { text, setText, onSubmit, usingGiphy } = React.useContext(MessageFormContext)\n\n return (\n <View style={styles.textInput}>\n {usingGiphy ? (\n <View style={styles.giphyBadge}>\n <Text>/Giphy</Text>\n </View>\n ) : null}\n\n <TextInput\n aria-disabled={true}\n placeholder=\"Send a message\"\n onChangeText={setText}\n value={text}\n onSubmitEditing={onSubmit}\n />\n </View>\n )\n}\n\nfunction MessageFormSubmitBtn() {\n const styles = useMessageFormStyles()\n const { onSubmit, disabled, usingGiphy } = React.useContext(MessageFormContext)\n\n return (\n <IconButton\n disabled={disabled}\n accessibilityLabel={usingGiphy ? 'Search Giphy' : 'Send message'}\n size=\"md\"\n appearance=\"neutral\"\n style={styles.textInputSend}\n name={usingGiphy ? 'general.search' : 'general.upArrow'}\n onPress={onSubmit}\n />\n )\n}\n\nfunction MessageFormAttachmentPicker() {\n const { usingGiphy } = React.useContext(MessageFormContext)\n\n if (usingGiphy) {\n return null\n }\n\n return (\n <IconButton\n accessibilityLabel=\"Shazam\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.paperclip'}\n />\n )\n}\n\nfunction MessageFormCommands() {\n const { canGiphy, usingGiphy, setUsingGiphy } = React.useContext(MessageFormContext)\n\n if (!canGiphy) {\n return null\n }\n\n if (usingGiphy) {\n return (\n <IconButton\n accessibilityLabel=\"Exit Giphy Search\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.x'}\n onPress={() => setUsingGiphy(false)}\n />\n )\n }\n\n return (\n <IconButton\n accessibilityLabel=\"Search Giphy\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.bolt'}\n onPress={() => setUsingGiphy(true)}\n />\n )\n}\n\nconst useMessageFormStyles = () => {\n const theme = useTheme()\n const navigationTheme = useNavigationTheme()\n\n return StyleSheet.create({\n textInputContainer: {\n borderColor: theme.colors.fillColorNeutral050Base,\n borderTopWidth: 1,\n padding: 12,\n backgroundColor: navigationTheme.colors.card,\n flexDirection: 'row',\n alignItems: 'center',\n gap: 12,\n },\n textInput: {\n borderRadius: 24,\n borderWidth: 1,\n padding: 12,\n paddingHorizontal: 20,\n borderColor: theme.colors.fillColorNeutral050Base,\n flex: 1,\n flexDirection: 'row',\n gap: 12,\n },\n giphyBadge: {\n backgroundColor: theme.colors.fillColorNeutral050Base,\n borderRadius: 24,\n padding: 8,\n paddingHorizontal: 12,\n },\n textInputSend: {\n borderRadius: 24,\n height: 36,\n width: 36,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"message_form.js","sourceRoot":"","sources":["../../../src/components/conversation/message_form.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useNavigation, useTheme as useNavigationTheme, useRoute } from '@react-navigation/native'\nimport React, { useCallback, useContext, useEffect, useState } from 'react'\nimport { StyleSheet, TextInput, View, ViewProps } from 'react-native'\nimport { IconButton, Text } from '../../components'\nimport { useTheme } from '../../hooks'\nimport { ConversationResource } from '../../types'\nimport { useMessageCreate } from '../../hooks/use_message_create'\nimport { ConversationScreenProps } from '../../screens/conversation_screen'\nimport { ChatContext } from '../../contexts/chat_context'\nimport { ImagePicker, ImagePickerResult } from '../../utils/native_adapters'\nimport { useAttachmentUploader } from '../../hooks/use_attachment_uploader'\nimport {\n DenormalizedAttachmentResourceForCreate,\n DenormalizedMessageAttachmentResourceForCreate,\n} from '../../types/resources/denormalized_attachment_resource'\nimport { MessageFormAttachmentImage } from './message_form/message_form_attachment_image'\n\nexport const MessageForm = {\n Root: MessageFormRoot,\n TextInput: MessageFormInput,\n SubmitButton: MessageFormSubmitBtn,\n AttachmentPicker: MessageFormAttachmentPicker,\n Commands: MessageFormCommands,\n}\n\ninterface MessagesFormRootProps extends ViewProps {\n conversation: ConversationResource\n}\n\nconst MessageFormContext = React.createContext<{\n text: string\n setText: (text: string) => void\n onSubmit: () => void\n disabled: boolean\n canGiphy: boolean\n usingGiphy: boolean\n setUsingGiphy: (usingGiphy: boolean) => void\n attachmentUploader?: ReturnType<typeof useAttachmentUploader>\n}>({\n text: '',\n setText: (_text: string) => {},\n onSubmit: () => {},\n disabled: false,\n canGiphy: false,\n usingGiphy: false,\n setUsingGiphy: (_usingGiphy: boolean) => {},\n})\n\nfunction MessageFormRoot({ conversation, children }: MessagesFormRootProps) {\n const { giphyApiKey } = useContext(ChatContext)\n const canGiphy = !!giphyApiKey\n const styles = useMessageFormStyles()\n const [text, setText] = React.useState('')\n const [usingGiphy, setUsingGiphy] = useState(false)\n const navigation = useNavigation()\n const route = useRoute() as ConversationScreenProps['route']\n const {\n status,\n isPending,\n reset: resetMutation,\n mutate,\n } = useMessageCreate({\n conversationId: conversation.id,\n })\n const attachmentUploader = useAttachmentUploader({\n conversationId: conversation.id,\n })\n const resetAttachmentUploader = attachmentUploader.reset\n\n const reset = useCallback(() => {\n resetAttachmentUploader()\n resetMutation()\n setText('')\n setUsingGiphy(false)\n }, [resetAttachmentUploader, resetMutation])\n\n useEffect(() => {\n if (canGiphy && !usingGiphy && text.startsWith('/giphy ')) {\n setUsingGiphy(true)\n setText('')\n }\n }, [canGiphy, text, usingGiphy])\n\n useEffect(() => {\n switch (status) {\n case 'success':\n reset()\n break\n }\n }, [reset, status])\n\n useEffect(() => {\n if (route.params.clear_input) {\n reset()\n navigation.setParams({ ...route.params, clear_input: false })\n }\n }, [reset, navigation, route.params])\n\n const canSubmit = (() => {\n if (isPending) return false\n if (attachmentUploader?.pendingUploads) return false\n if (text.length > 0) return true\n if (attachmentUploader?.attachments?.length) return true\n return false\n })()\n const disabled = !canSubmit\n\n const handleSubmit = () => {\n if (!canSubmit) return\n\n if (canGiphy && usingGiphy) {\n TextInput.State.blurTextInput(TextInput.State.currentlyFocusedInput())\n navigation.navigate('SendGiphy', {\n conversation_id: conversation.id,\n search_term: text,\n })\n } else {\n let attachmentsForSubmit: DenormalizedAttachmentResourceForCreate[] = []\n if (attachmentUploader?.attachmentIds) {\n attachmentsForSubmit = attachmentUploader.attachmentIds.map(\n (id: string): DenormalizedMessageAttachmentResourceForCreate => ({\n type: 'MessageAttachment',\n id,\n })\n )\n }\n mutate({ text, attachments: attachmentsForSubmit })\n }\n }\n\n return (\n <MessageFormContext.Provider\n value={{\n text,\n setText,\n onSubmit: handleSubmit,\n disabled,\n canGiphy,\n usingGiphy,\n setUsingGiphy,\n attachmentUploader,\n }}\n >\n <View style={styles.textInputContainer}>{children}</View>\n </MessageFormContext.Provider>\n )\n}\n\nfunction MessageFormAttachments() {\n const styles = useMessageFormStyles()\n const { attachmentUploader } = React.useContext(MessageFormContext)\n const numberOfAttachments = attachmentUploader?.attachments?.length || 0\n const attachments = attachmentUploader?.attachments || []\n\n if (numberOfAttachments === 0) {\n return null\n }\n\n return (\n <View style={styles.messageFormAttachments}>\n {attachments.map(attachment => {\n return (\n <MessageFormAttachmentImage\n key={attachment.file.uri}\n uri={attachment.file.uri}\n alt={attachment.file.name}\n status={attachment.status}\n width={attachment.file.width}\n height={attachment.file.height}\n removeAttachment={() => {\n attachmentUploader?.removeAttachment(attachment)\n }}\n />\n )\n })}\n </View>\n )\n}\n\nfunction MessageFormInput() {\n const styles = useMessageFormStyles()\n const { text, setText, onSubmit, usingGiphy, attachmentUploader } =\n React.useContext(MessageFormContext)\n const attachmentError = attachmentUploader?.errorMessage\n\n return (\n <View style={styles.textInputBoundary}>\n <MessageFormAttachments />\n <View style={styles.textInput}>\n {usingGiphy ? (\n <View style={styles.giphyBadge}>\n <Text>/Giphy</Text>\n </View>\n ) : null}\n\n <TextInput\n aria-disabled={true}\n placeholder=\"Send a message\"\n onChangeText={setText}\n value={text}\n onSubmitEditing={onSubmit}\n />\n </View>\n\n {attachmentError ? <Text style={styles.inputErrorMessage}>{attachmentError}</Text> : null}\n </View>\n )\n}\n\nfunction MessageFormSubmitBtn() {\n const styles = useMessageFormStyles()\n const { onSubmit, disabled, usingGiphy } = React.useContext(MessageFormContext)\n\n return (\n <IconButton\n disabled={disabled}\n accessibilityLabel={usingGiphy ? 'Search Giphy' : 'Send message'}\n size=\"md\"\n appearance=\"neutral\"\n style={styles.textInputSend}\n name={usingGiphy ? 'general.search' : 'general.upArrow'}\n onPress={onSubmit}\n />\n )\n}\n\nfunction MessageFormAttachmentPicker() {\n const styles = useMessageFormStyles()\n const { usingGiphy, attachmentUploader } = React.useContext(MessageFormContext)\n const [isOpen, setIsOpen] = useState(false)\n\n function uploadImagePickerResult(result: ImagePickerResult) {\n if (result.canceled) {\n return\n }\n\n const filteredAssets = result.assets\n .filter(asset => {\n return asset.fileSize && asset.fileName && asset.mimeType\n })\n .map(asset => {\n return {\n uri: asset.uri,\n name: asset.fileName as string,\n type: asset.mimeType as string,\n size: asset.fileSize as number,\n height: asset.height,\n width: asset.width,\n }\n })\n\n attachmentUploader?.handleFilesAttached(filteredAssets)\n }\n\n const openCamera = async () => {\n setIsOpen(false)\n let result = await ImagePicker.openCameraAsync()\n if (!result.canceled) {\n uploadImagePickerResult(result)\n }\n }\n\n const pickImage = async () => {\n setIsOpen(false)\n let result = await ImagePicker.openImageLibraryAsync()\n if (!result.canceled) {\n uploadImagePickerResult(result)\n }\n }\n\n if (usingGiphy) {\n return null\n }\n\n return (\n // TODO: Design Pass\n <View style={styles.attachmentPicker}>\n {isOpen && (\n <View style={styles.attachmentPickerButtons}>\n <IconButton\n accessibilityLabel=\"Take a photo\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.videoCamera'}\n onPress={openCamera}\n />\n <IconButton\n accessibilityLabel=\"Choose a photo\"\n size=\"md\"\n appearance=\"neutral\"\n name={'churchCenter.photosIos'}\n onPress={pickImage}\n />\n </View>\n )}\n <IconButton\n accessibilityLabel=\"File Menu\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.outlinedPlusCircle'}\n onPress={() => setIsOpen(!isOpen)}\n />\n </View>\n )\n}\n\nfunction MessageFormCommands() {\n const { canGiphy, usingGiphy, setUsingGiphy } = React.useContext(MessageFormContext)\n\n if (!canGiphy) {\n return null\n }\n\n if (usingGiphy) {\n return (\n <IconButton\n accessibilityLabel=\"Exit Giphy Search\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.x'}\n onPress={() => setUsingGiphy(false)}\n />\n )\n }\n\n return (\n <IconButton\n accessibilityLabel=\"Search Giphy\"\n size=\"md\"\n appearance=\"neutral\"\n name={'general.bolt'}\n onPress={() => setUsingGiphy(true)}\n />\n )\n}\n\nconst useMessageFormStyles = () => {\n const theme = useTheme()\n const navigationTheme = useNavigationTheme()\n\n return StyleSheet.create({\n textInputContainer: {\n borderColor: theme.colors.fillColorNeutral050Base,\n borderTopWidth: 1,\n padding: 12,\n backgroundColor: navigationTheme.colors.card,\n flexDirection: 'row',\n alignItems: 'center',\n gap: 12,\n },\n textInputBoundary: {\n borderRadius: 24,\n borderWidth: 1,\n padding: 12,\n paddingHorizontal: 20,\n borderColor: theme.colors.fillColorNeutral050Base,\n flex: 1,\n gap: 12,\n },\n textInput: {\n flexDirection: 'row',\n gap: 12,\n },\n giphyBadge: {\n backgroundColor: theme.colors.fillColorNeutral050Base,\n borderRadius: 24,\n padding: 8,\n paddingHorizontal: 12,\n },\n textInputSend: {\n borderRadius: 24,\n height: 36,\n width: 36,\n },\n attachmentPicker: {\n position: 'relative',\n },\n attachmentPickerButtons: {\n position: 'absolute',\n left: 0,\n bottom: 40,\n zIndex: 10,\n gap: 16,\n },\n messageFormAttachments: {\n flexDirection: 'row',\n gap: 8,\n },\n inputErrorMessage: {\n color: theme.colors.statusErrorText,\n fontSize: 14,\n },\n })\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const SUPPORTED_EXTENSIONS: string[];
2
+ //# sourceMappingURL=supported_extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supported_extensions.d.ts","sourceRoot":"","sources":["../../../src/hooks/attachments/supported_extensions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,UA8ChC,CAAA"}
@@ -0,0 +1,48 @@
1
+ export const SUPPORTED_EXTENSIONS = [
2
+ '.3ga',
3
+ '.3gp',
4
+ '.aac',
5
+ '.amr',
6
+ '.avi',
7
+ '.bmp',
8
+ '.doc',
9
+ '.docx',
10
+ '.gif',
11
+ '.h263',
12
+ '.h264',
13
+ '.heic',
14
+ '.heif',
15
+ '.jpeg',
16
+ '.jpg',
17
+ '.key',
18
+ '.m4a',
19
+ '.m4b',
20
+ '.m4p',
21
+ '.m4r',
22
+ '.m4v',
23
+ '.mkv',
24
+ '.mov',
25
+ '.mp3',
26
+ '.mp4',
27
+ '.mp4-latm',
28
+ '.mpeg',
29
+ '.mpeg4',
30
+ '.mpg',
31
+ '.numbers',
32
+ '.ogg',
33
+ '.pages',
34
+ '.pdf',
35
+ '.png',
36
+ '.ppt',
37
+ '.pptx',
38
+ '.rtf',
39
+ '.txt',
40
+ '.vcf',
41
+ '.wav',
42
+ '.webm',
43
+ '.webp',
44
+ '.wmv',
45
+ '.xls',
46
+ '.xlsx',
47
+ ];
48
+ //# sourceMappingURL=supported_extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supported_extensions.js","sourceRoot":"","sources":["../../../src/hooks/attachments/supported_extensions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,WAAW;IACX,OAAO;IACP,QAAQ;IACR,MAAM;IACN,UAAU;IACV,MAAM;IACN,QAAQ;IACR,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;CACR,CAAA","sourcesContent":["export const SUPPORTED_EXTENSIONS = [\n '.3ga',\n '.3gp',\n '.aac',\n '.amr',\n '.avi',\n '.bmp',\n '.doc',\n '.docx',\n '.gif',\n '.h263',\n '.h264',\n '.heic',\n '.heif',\n '.jpeg',\n '.jpg',\n '.key',\n '.m4a',\n '.m4b',\n '.m4p',\n '.m4r',\n '.m4v',\n '.mkv',\n '.mov',\n '.mp3',\n '.mp4',\n '.mp4-latm',\n '.mpeg',\n '.mpeg4',\n '.mpg',\n '.numbers',\n '.ogg',\n '.pages',\n '.pdf',\n '.png',\n '.ppt',\n '.pptx',\n '.rtf',\n '.txt',\n '.vcf',\n '.wav',\n '.webm',\n '.webp',\n '.wmv',\n '.xls',\n '.xlsx',\n]\n"]}
@@ -0,0 +1,26 @@
1
+ import { FileForUploadClient } from './use_upload_client';
2
+ type AttachmentStatus = 'uploading' | 'success' | 'error';
3
+ interface AttachmentFile extends FileForUploadClient {
4
+ size: number;
5
+ width?: number;
6
+ height?: number;
7
+ }
8
+ export interface FileAttachment {
9
+ id?: string;
10
+ file: AttachmentFile;
11
+ status: AttachmentStatus;
12
+ }
13
+ export declare function useAttachmentUploader({ conversationId }: {
14
+ conversationId: number;
15
+ }): {
16
+ attachments: FileAttachment[];
17
+ attachmentIds: string[];
18
+ handleFilesAttached: (files: AttachmentFile[]) => void;
19
+ removeAttachment: (attachment: FileAttachment) => void;
20
+ reset: () => void;
21
+ pendingUploads: boolean;
22
+ errorMessage: string | null;
23
+ remainingAttachable: number;
24
+ };
25
+ export {};
26
+ //# sourceMappingURL=use_attachment_uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use_attachment_uploader.d.ts","sourceRoot":"","sources":["../../src/hooks/use_attachment_uploader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,qBAAqB,CAAA;AAI1E,KAAK,gBAAgB,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAA;AAEzD,UAAU,cAAe,SAAQ,mBAAmB;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,cAAc,CAAA;IACpB,MAAM,EAAE,gBAAgB,CAAA;CACzB;AAkBD,wBAAgB,qBAAqB,CAAC,EAAE,cAAc,EAAE,EAAE;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE;;;iCAUxE,cAAc,EAAE;mCA4FwB,cAAc;;;;;EA4BjE"}
@@ -0,0 +1,111 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { SUPPORTED_EXTENSIONS } from './attachments/supported_extensions';
3
+ import { useUploadClient } from './use_upload_client';
4
+ import { useApiClient } from './use_api_client';
5
+ const MAX_FILE_SIZE_IN_MB = 50;
6
+ const MAX_FILE_SIZE_IN_BYTES = MAX_FILE_SIZE_IN_MB * 1024 * 1024;
7
+ const MAX_NUMBER_OF_ATTACHMENTS = 10;
8
+ export function useAttachmentUploader({ conversationId }) {
9
+ const apiClient = useApiClient();
10
+ const uploadApi = useUploadClient();
11
+ const [attachments, setAttachments] = useState([]);
12
+ const uploadState = useRef({});
13
+ const [lastUploadId, setLastUploadId] = useState();
14
+ const numberOfAttachments = attachments.length;
15
+ const [errorMessage, setErrorMessage] = useState(null);
16
+ const handleFilesAttached = useCallback((files) => {
17
+ const fileErrors = {};
18
+ const validFiles = files.filter(file => {
19
+ const extension = file.name.split('.').pop();
20
+ const isValidExtension = SUPPORTED_EXTENSIONS.includes(`.${extension}`);
21
+ const isValidFileSize = file.size <= MAX_FILE_SIZE_IN_BYTES;
22
+ if (!isValidExtension) {
23
+ fileErrors.file_type ||= [];
24
+ fileErrors.file_type.push(extension);
25
+ }
26
+ if (!isValidFileSize) {
27
+ fileErrors.file_size = true;
28
+ }
29
+ return isValidFileSize && isValidExtension;
30
+ });
31
+ const errorMessages = [];
32
+ if (fileErrors.file_type) {
33
+ errorMessages.push(`The following file types are not supported: ${fileErrors.file_type.join(', ')}`);
34
+ }
35
+ if (fileErrors.file_size) {
36
+ errorMessages.push(`File size exceeds ${MAX_FILE_SIZE_IN_MB} MB`);
37
+ }
38
+ if (numberOfAttachments + validFiles.length > MAX_NUMBER_OF_ATTACHMENTS) {
39
+ errorMessages.push(`You can't attach more than ${MAX_NUMBER_OF_ATTACHMENTS} files at once.`);
40
+ }
41
+ if (errorMessages.length > 0) {
42
+ setErrorMessage(errorMessages.join('\n'));
43
+ return;
44
+ }
45
+ const newAttachments = validFiles.map(file => ({
46
+ file,
47
+ status: 'uploading',
48
+ }));
49
+ if (newAttachments && newAttachments.length > 0) {
50
+ setAttachments(prevAttachments => [...prevAttachments, ...newAttachments]);
51
+ newAttachments.forEach(attachment => {
52
+ uploadApi
53
+ .uploadFile(attachment.file)
54
+ .then(({ id: uploadedFileId }) => apiClient.chat.post({
55
+ url: `/me/conversations/${conversationId}/message_attachments`,
56
+ data: {
57
+ data: {
58
+ type: 'MessageAttachment',
59
+ attributes: { uploaded_file_id: uploadedFileId },
60
+ },
61
+ },
62
+ }))
63
+ .then(({ data: { id: messageAttachmentId } }) => {
64
+ uploadState.current[attachment.file.name] = {
65
+ status: 'success',
66
+ id: messageAttachmentId,
67
+ };
68
+ setLastUploadId(messageAttachmentId);
69
+ })
70
+ .catch(() => {
71
+ uploadState.current[attachment.file.name] = {
72
+ status: 'error',
73
+ };
74
+ setLastUploadId(attachment.file.name);
75
+ });
76
+ });
77
+ }
78
+ }, [numberOfAttachments, uploadApi, apiClient.chat, conversationId]);
79
+ useEffect(() => {
80
+ if (!lastUploadId)
81
+ return;
82
+ setLastUploadId(undefined);
83
+ setAttachments(attachments.map(attachment => {
84
+ const state = uploadState.current[attachment.file.name];
85
+ if (state) {
86
+ return { ...attachment, id: state.id, status: state.status };
87
+ }
88
+ return attachment;
89
+ }));
90
+ }, [attachments, lastUploadId]);
91
+ const removeAttachment = useCallback((attachment) => {
92
+ setAttachments(prevAttachments => prevAttachments.filter(a => a.file.uri !== attachment.file.uri));
93
+ }, []);
94
+ const reset = useCallback(() => {
95
+ setAttachments([]);
96
+ setErrorMessage(null);
97
+ }, []);
98
+ const pendingUploads = attachments.filter(a => a.status === 'uploading').length > 0;
99
+ const attachmentIds = useMemo(() => attachments.filter(a => a.status === 'success' && a.id).map(a => a.id), [attachments]);
100
+ return {
101
+ attachments,
102
+ attachmentIds,
103
+ handleFilesAttached,
104
+ removeAttachment,
105
+ reset,
106
+ pendingUploads,
107
+ errorMessage,
108
+ remainingAttachable: MAX_NUMBER_OF_ATTACHMENTS - numberOfAttachments,
109
+ };
110
+ }
111
+ //# sourceMappingURL=use_attachment_uploader.js.map