@ethora/chat-component-rn 26.5.10 → 26.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -1
- package/README.md +30 -1
- package/lib/commonjs/assets/icons.js +7 -1
- package/lib/commonjs/assets/icons.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +31 -16
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatRoom.js +34 -4
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +17 -14
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +298 -12
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/InteractionsOverlay.js +119 -0
- package/lib/commonjs/components/MessageBubble/InteractionsOverlay.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/Message.js +39 -3
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +87 -39
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +153 -62
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -5
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js +5 -44
- package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +40 -3
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/GViewWebView.js +149 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/GViewWebView.js.map +1 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +4 -17
- package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
- package/lib/commonjs/components/Modals/ModalHeaderComponent.js +10 -0
- package/lib/commonjs/components/Modals/ModalHeaderComponent.js.map +1 -1
- package/lib/commonjs/components/Modals/styledModalComponents.js +13 -4
- package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js +19 -33
- package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
- package/lib/commonjs/components/styled/AudioMessage.js +643 -66
- package/lib/commonjs/components/styled/AudioMessage.js.map +1 -1
- package/lib/commonjs/components/styled/Button.js +9 -3
- package/lib/commonjs/components/styled/Button.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +239 -40
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/StyledComponents.js +6 -3
- package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
- package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js +31 -18
- package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
- package/lib/commonjs/components/styled/UnsupportedType.js +47 -5
- package/lib/commonjs/components/styled/UnsupportedType.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +44 -25
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +5 -2
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/keyboardLayout.js +5 -3
- package/lib/commonjs/helpers/keyboardLayout.js.map +1 -1
- package/lib/commonjs/helpers/mimeToExtension.js +123 -0
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
- package/lib/commonjs/hooks/useEventHandlers.js +1 -1
- package/lib/commonjs/hooks/useEventHandlers.js.map +1 -1
- package/lib/commonjs/hooks/useLogout.js +33 -8
- package/lib/commonjs/hooks/useLogout.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +6 -3
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js +29 -4
- package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +6 -0
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/assets/icons.js +7 -1
- package/lib/module/assets/icons.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +31 -16
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatRoom.js +36 -6
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +17 -14
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +301 -15
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MessageBubble/InteractionsOverlay.js +112 -0
- package/lib/module/components/MessageBubble/InteractionsOverlay.js.map +1 -0
- package/lib/module/components/MessageBubble/Message.js +40 -4
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +89 -41
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js +155 -64
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -5
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js +5 -45
- package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +41 -4
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/GViewWebView.js +144 -0
- package/lib/module/components/Modals/FilePreviewModal/GViewWebView.js.map +1 -0
- package/lib/module/components/Modals/FilePreviewModal/PdfView.js +3 -17
- package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
- package/lib/module/components/Modals/ModalHeaderComponent.js +10 -0
- package/lib/module/components/Modals/ModalHeaderComponent.js.map +1 -1
- package/lib/module/components/Modals/styledModalComponents.js +13 -4
- package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastAudioMessage.js +20 -34
- package/lib/module/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
- package/lib/module/components/styled/AudioMessage.js +644 -67
- package/lib/module/components/styled/AudioMessage.js.map +1 -1
- package/lib/module/components/styled/Button.js +9 -3
- package/lib/module/components/styled/Button.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +240 -42
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/StyledComponents.js +6 -3
- package/lib/module/components/styled/StyledComponents.js.map +1 -1
- package/lib/module/components/styled/StyledInputComponents/MediaComponents.js +31 -18
- package/lib/module/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
- package/lib/module/components/styled/UnsupportedType.js +47 -5
- package/lib/module/components/styled/UnsupportedType.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +45 -26
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/xmppProvider.js +5 -2
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/keyboardLayout.js +4 -2
- package/lib/module/helpers/keyboardLayout.js.map +1 -1
- package/lib/module/helpers/mimeToExtension.js +119 -0
- package/lib/module/helpers/mimeToExtension.js.map +1 -1
- package/lib/module/hooks/useEventHandlers.js +1 -1
- package/lib/module/hooks/useEventHandlers.js.map +1 -1
- package/lib/module/hooks/useLogout.js +33 -8
- package/lib/module/hooks/useLogout.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +6 -3
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/networking/api-requests/rooms.api.js +29 -4
- package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +6 -0
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/typescript/assets/icons.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/InteractionsOverlay.d.ts +13 -0
- package/lib/typescript/components/MessageBubble/InteractionsOverlay.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
- package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts +0 -7
- package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/GViewWebView.d.ts +8 -0
- package/lib/typescript/components/Modals/FilePreviewModal/GViewWebView.d.ts.map +1 -0
- package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ModalHeaderComponent.d.ts.map +1 -1
- package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts.map +1 -1
- package/lib/typescript/components/styled/AudioMessage.d.ts +8 -2
- package/lib/typescript/components/styled/AudioMessage.d.ts.map +1 -1
- package/lib/typescript/components/styled/Button.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts +5 -1
- package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/UnsupportedType.d.ts +5 -0
- package/lib/typescript/components/styled/UnsupportedType.d.ts.map +1 -1
- package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/keyboardLayout.d.ts +3 -1
- package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -1
- package/lib/typescript/helpers/mimeToExtension.d.ts +38 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
- package/lib/typescript/hooks/useLogout.d.ts +23 -1
- package/lib/typescript/hooks/useLogout.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/types/models/modal.model.d.ts +3 -0
- package/lib/typescript/types/models/modal.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +29 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/package.json +11 -4
- package/src/assets/icons.tsx +30 -25
- package/src/components/MainComponents/ChatHeader.tsx +46 -15
- package/src/components/MainComponents/ChatRoom.tsx +38 -3
- package/src/components/MainComponents/ChatWrapper.tsx +28 -21
- package/src/components/MainComponents/MediaMessage.tsx +304 -11
- package/src/components/MessageBubble/InteractionsOverlay.tsx +121 -0
- package/src/components/MessageBubble/Message.tsx +44 -3
- package/src/components/MessageBubble/MessageInteractions.tsx +103 -52
- package/src/components/Modals/AttachSheet/AttachSheet.tsx +144 -51
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +25 -3
- package/src/components/Modals/FilePreviewModal/DocumentViewer.tsx +3 -40
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +48 -4
- package/src/components/Modals/FilePreviewModal/GViewWebView.tsx +142 -0
- package/src/components/Modals/FilePreviewModal/PdfView.tsx +2 -18
- package/src/components/Modals/ModalHeaderComponent.tsx +8 -1
- package/src/components/Modals/styledModalComponents.tsx +13 -4
- package/src/components/RoomComponents/styled/LastAudioMessage.tsx +20 -35
- package/src/components/styled/AudioMessage.tsx +724 -59
- package/src/components/styled/Button.tsx +20 -11
- package/src/components/styled/SendInput.tsx +255 -46
- package/src/components/styled/StyledComponents.ts +6 -3
- package/src/components/styled/StyledInputComponents/MediaComponents.tsx +29 -18
- package/src/components/styled/UnsupportedType.tsx +60 -6
- package/src/components/styled/VideoMessage.tsx +21 -5
- package/src/context/xmppProvider.tsx +6 -2
- package/src/helpers/keyboardLayout.ts +5 -1
- package/src/helpers/mimeToExtension.ts +178 -0
- package/src/hooks/useEventHandlers.tsx +1 -1
- package/src/hooks/useLogout.tsx +33 -10
- package/src/hooks/useSendMessage.tsx +12 -6
- package/src/networking/api-requests/rooms.api.ts +31 -7
- package/src/roomStore/roomsSlice.ts +7 -0
- package/src/types/models/modal.model.ts +3 -0
- package/src/types/types.ts +29 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useId, useMemo, useState } from 'react';
|
|
2
2
|
import { Delimeter, MenuItem } from '../ContextMenu/ContextMenuComponents';
|
|
3
3
|
import { useSelector } from 'react-redux';
|
|
4
4
|
import { RootState } from '../../roomStore';
|
|
@@ -8,16 +8,17 @@ import {
|
|
|
8
8
|
} from '../../helpers/constants/MESSAGE_INTERACTIONS';
|
|
9
9
|
import { IMessage } from '../../types/types';
|
|
10
10
|
import {
|
|
11
|
-
Modal,
|
|
12
11
|
Text,
|
|
13
12
|
StyleSheet,
|
|
14
13
|
View,
|
|
15
14
|
Pressable,
|
|
16
15
|
Dimensions,
|
|
16
|
+
Keyboard,
|
|
17
17
|
type LayoutChangeEvent,
|
|
18
18
|
} from 'react-native';
|
|
19
19
|
import * as Clipboard from 'expo-clipboard';
|
|
20
20
|
import { useToast } from '../../context/ToastContext';
|
|
21
|
+
import { useInteractionsOverlay } from './InteractionsOverlay';
|
|
21
22
|
|
|
22
23
|
interface MessageInteractionsProps {
|
|
23
24
|
isReply?: boolean;
|
|
@@ -47,6 +48,8 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
47
48
|
handleEditMessage,
|
|
48
49
|
}) => {
|
|
49
50
|
const { showToast } = useToast();
|
|
51
|
+
const { present, dismiss, originX, originY } = useInteractionsOverlay();
|
|
52
|
+
const overlayId = useId();
|
|
50
53
|
|
|
51
54
|
const config = useSelector(
|
|
52
55
|
(state: RootState) => state.chatSettingStore.config
|
|
@@ -54,6 +57,28 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
54
57
|
|
|
55
58
|
const [menuSize, setMenuSize] = useState({ width: 0, height: 0 });
|
|
56
59
|
|
|
60
|
+
// Live keyboard height so the menu knows the REAL space below the message.
|
|
61
|
+
// Without this the position math used the full screen height and happily
|
|
62
|
+
// opened the menu downward into the area the keyboard now covers, hiding
|
|
63
|
+
// it under the keyboard / behind the input. Seeded from current metrics so
|
|
64
|
+
// an already-open keyboard (the common case: menu opened while typing) is
|
|
65
|
+
// accounted for on first render, not only after the next show event.
|
|
66
|
+
const [keyboardHeight, setKeyboardHeight] = useState(
|
|
67
|
+
() => Keyboard.metrics?.()?.height ?? 0
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const onShow = (e: any) =>
|
|
72
|
+
setKeyboardHeight(e?.endCoordinates?.height ?? 0);
|
|
73
|
+
const onHide = () => setKeyboardHeight(0);
|
|
74
|
+
const subs = [
|
|
75
|
+
Keyboard.addListener('keyboardDidShow', onShow),
|
|
76
|
+
Keyboard.addListener('keyboardDidChangeFrame', onShow),
|
|
77
|
+
Keyboard.addListener('keyboardDidHide', onHide),
|
|
78
|
+
];
|
|
79
|
+
return () => subs.forEach((s) => s.remove());
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
57
82
|
const handleMenuLayout = (e: LayoutChangeEvent) => {
|
|
58
83
|
const { width, height } = e.nativeEvent.layout;
|
|
59
84
|
if (
|
|
@@ -105,7 +130,13 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
105
130
|
const topReserve = 16;
|
|
106
131
|
const sideMargin = 8;
|
|
107
132
|
|
|
108
|
-
|
|
133
|
+
// The keyboard occludes the bottom `keyboardHeight` px, so the real
|
|
134
|
+
// visible bottom edge is above it. Measuring space below against this
|
|
135
|
+
// (not the full screen) is what makes the menu flip ABOVE the message
|
|
136
|
+
// when the keyboard is open and the message sits near the input.
|
|
137
|
+
const visibleBottom = screenHeight - keyboardHeight;
|
|
138
|
+
|
|
139
|
+
const spaceBelow = visibleBottom - position.bottom - bottomReserve;
|
|
109
140
|
const spaceAbove = position.top - topReserve;
|
|
110
141
|
|
|
111
142
|
let top: number;
|
|
@@ -114,7 +145,10 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
114
145
|
} else if (spaceAbove >= menuSize.height) {
|
|
115
146
|
top = position.top - menuSize.height;
|
|
116
147
|
} else {
|
|
117
|
-
top = Math.max(
|
|
148
|
+
top = Math.max(
|
|
149
|
+
topReserve,
|
|
150
|
+
visibleBottom - menuSize.height - bottomReserve
|
|
151
|
+
);
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
const leftMargin = position.left;
|
|
@@ -128,56 +162,73 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
128
162
|
);
|
|
129
163
|
|
|
130
164
|
return { top, left };
|
|
131
|
-
}, [position, menuSize, config?.keyboardVerticalOffset]);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
165
|
+
}, [position, menuSize, config?.keyboardVerticalOffset, keyboardHeight]);
|
|
166
|
+
|
|
167
|
+
// Window coords (memoPosition) → host-local coords. The overlay host
|
|
168
|
+
// sits below the status bar / header, so subtract its measured origin.
|
|
169
|
+
const localPosition = useMemo(() => {
|
|
170
|
+
if (!memoPosition) {return undefined;}
|
|
171
|
+
return {
|
|
172
|
+
...memoPosition,
|
|
173
|
+
top: (memoPosition as { top: number }).top - originY,
|
|
174
|
+
left: (memoPosition as { left: number }).left - originX,
|
|
175
|
+
};
|
|
176
|
+
}, [memoPosition, originX, originY]);
|
|
177
|
+
|
|
178
|
+
const content =
|
|
179
|
+
config?.disableInteractions || message.isDeleted ? null : (
|
|
180
|
+
<View style={styles.overlayFill}>
|
|
181
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={closeMenu} />
|
|
182
|
+
<View
|
|
183
|
+
style={[styles.contextMenu, localPosition]}
|
|
184
|
+
onLayout={handleMenuLayout}
|
|
185
|
+
>
|
|
186
|
+
<MenuItem onPress={() => handleCopyMessage(message.body!)}>
|
|
187
|
+
<Text style={styles.menuText}>{MESSAGE_INTERACTIONS.COPY}</Text>
|
|
188
|
+
<MESSAGE_INTERACTIONS_ICONS.COPY />
|
|
189
|
+
</MenuItem>
|
|
190
|
+
{isUser && (
|
|
191
|
+
<>
|
|
192
|
+
{/* Edit only for non-media messages (upstream guard) +
|
|
193
|
+
consistent label styling (menuText). */}
|
|
194
|
+
{message?.isMediafile !== 'true' && (
|
|
195
|
+
<>
|
|
196
|
+
<Delimeter />
|
|
197
|
+
<MenuItem onPress={handleEditMessage}>
|
|
198
|
+
<Text style={styles.menuText}>
|
|
199
|
+
{MESSAGE_INTERACTIONS.EDIT}
|
|
200
|
+
</Text>
|
|
201
|
+
<MESSAGE_INTERACTIONS_ICONS.EDIT />
|
|
202
|
+
</MenuItem>
|
|
203
|
+
</>
|
|
204
|
+
)}
|
|
205
|
+
<Delimeter />
|
|
206
|
+
<MenuItem onPress={handleDeleteMessage}>
|
|
207
|
+
<Text style={styles.menuText}>{MESSAGE_INTERACTIONS.DELETE}</Text>
|
|
208
|
+
<MESSAGE_INTERACTIONS_ICONS.DELETE />
|
|
209
|
+
</MenuItem>
|
|
210
|
+
</>
|
|
211
|
+
)}
|
|
176
212
|
</View>
|
|
177
|
-
|
|
213
|
+
</View>
|
|
214
|
+
);
|
|
178
215
|
|
|
179
|
-
|
|
180
|
-
|
|
216
|
+
// Render the menu in the in-tree overlay host instead of a React Native
|
|
217
|
+
// <Modal>. A Modal opens its own window on Android, which steals focus
|
|
218
|
+
// from the chat input and dismisses the keyboard when the menu opens;
|
|
219
|
+
// hosting it in the same view tree keeps the keyboard up. Re-run every
|
|
220
|
+
// render so menuSize/position updates refresh the hosted node.
|
|
221
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
if (content) {
|
|
224
|
+
present(overlayId, content);
|
|
225
|
+
} else {
|
|
226
|
+
dismiss(overlayId);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
useEffect(() => () => dismiss(overlayId), [dismiss, overlayId]);
|
|
230
|
+
|
|
231
|
+
return null;
|
|
181
232
|
};
|
|
182
233
|
|
|
183
234
|
export default MessageInteractions;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/** @format */
|
|
2
2
|
|
|
3
|
-
import React, { useRef } from 'react';
|
|
3
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import {
|
|
5
|
+
Animated,
|
|
6
|
+
Easing,
|
|
5
7
|
Modal,
|
|
6
8
|
Platform,
|
|
7
9
|
StyleSheet,
|
|
@@ -20,6 +22,11 @@ interface AttachSheetProps {
|
|
|
20
22
|
primaryColor?: string;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
// Starting translateY for the sheet. Larger than any realistic sheet
|
|
26
|
+
// height so it sits fully below the viewport before opening; the exact
|
|
27
|
+
// number doesn't matter visually because it animates to 0.
|
|
28
|
+
const SHEET_OFFSCREEN = 600;
|
|
29
|
+
|
|
23
30
|
const AttachSheet: React.FC<AttachSheetProps> = ({
|
|
24
31
|
visible,
|
|
25
32
|
onClose,
|
|
@@ -28,22 +35,94 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
|
|
|
28
35
|
onDocument,
|
|
29
36
|
primaryColor = '#0052CD',
|
|
30
37
|
}) => {
|
|
38
|
+
// Pending handler captured on row tap — runs once the EXIT animation
|
|
39
|
+
// finishes so iOS doesn't try to present an image picker over a
|
|
40
|
+
// still-dismissing modal (was the original reason for the
|
|
41
|
+
// platform-split + Modal.onDismiss dance). The animation completion
|
|
42
|
+
// callback now drives this on both platforms.
|
|
31
43
|
const pendingRef = useRef<(() => void) | null>(null);
|
|
32
44
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
45
|
+
// Local "modal stays mounted while animating out" state. The parent
|
|
46
|
+
// controls `visible`; we mirror it but delay unmount until the exit
|
|
47
|
+
// animation has played so the slide-down is actually seen.
|
|
48
|
+
const [mounted, setMounted] = useState(visible);
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
// Two separate Animated values — the whole point of the rewrite:
|
|
51
|
+
// • backdropOpacity: fades the dim layer in/out (no slide).
|
|
52
|
+
// • sheetTranslateY: slides ONLY the sheet up from below.
|
|
53
|
+
// RN Modal's built-in `animationType="slide"` slides the entire
|
|
54
|
+
// contents (backdrop included) so the dim shade entered from the
|
|
55
|
+
// bottom — what looked "awful". Splitting these gives the standard
|
|
56
|
+
// bottom-sheet feel.
|
|
57
|
+
const backdropOpacity = useRef(new Animated.Value(0)).current;
|
|
58
|
+
const sheetTranslateY = useRef(
|
|
59
|
+
new Animated.Value(SHEET_OFFSCREEN)
|
|
60
|
+
).current;
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (visible) {
|
|
64
|
+
setMounted(true);
|
|
65
|
+
// Reset to starting positions BEFORE the Modal becomes visible so
|
|
66
|
+
// the first animated frame is below-the-fold (not a flash of the
|
|
67
|
+
// settled sheet).
|
|
68
|
+
backdropOpacity.setValue(0);
|
|
69
|
+
sheetTranslateY.setValue(SHEET_OFFSCREEN);
|
|
70
|
+
// requestAnimationFrame lets the Modal commit its mount before the
|
|
71
|
+
// animation starts; without this the first frame can be skipped.
|
|
72
|
+
const raf = requestAnimationFrame(() => {
|
|
73
|
+
Animated.parallel([
|
|
74
|
+
Animated.timing(backdropOpacity, {
|
|
75
|
+
toValue: 1,
|
|
76
|
+
duration: 220,
|
|
77
|
+
easing: Easing.out(Easing.cubic),
|
|
78
|
+
useNativeDriver: true,
|
|
79
|
+
}),
|
|
80
|
+
Animated.spring(sheetTranslateY, {
|
|
81
|
+
toValue: 0,
|
|
82
|
+
// Tuned for a quick-but-not-bouncy entry. tension is the
|
|
83
|
+
// "stiffness" knob, friction the "damping" knob in RN's
|
|
84
|
+
// spring config.
|
|
85
|
+
tension: 90,
|
|
86
|
+
friction: 14,
|
|
87
|
+
useNativeDriver: true,
|
|
88
|
+
}),
|
|
89
|
+
]).start();
|
|
90
|
+
});
|
|
91
|
+
return () => cancelAnimationFrame(raf);
|
|
46
92
|
}
|
|
93
|
+
if (!mounted) {return;}
|
|
94
|
+
// Exit: fade the backdrop, slide the sheet down, then unmount and
|
|
95
|
+
// fire any pending picker handler.
|
|
96
|
+
Animated.parallel([
|
|
97
|
+
Animated.timing(backdropOpacity, {
|
|
98
|
+
toValue: 0,
|
|
99
|
+
duration: 180,
|
|
100
|
+
easing: Easing.in(Easing.cubic),
|
|
101
|
+
useNativeDriver: true,
|
|
102
|
+
}),
|
|
103
|
+
Animated.timing(sheetTranslateY, {
|
|
104
|
+
toValue: SHEET_OFFSCREEN,
|
|
105
|
+
duration: 220,
|
|
106
|
+
easing: Easing.in(Easing.cubic),
|
|
107
|
+
useNativeDriver: true,
|
|
108
|
+
}),
|
|
109
|
+
]).start(({ finished }) => {
|
|
110
|
+
if (!finished) {return;} // interrupted (rapid re-open) → leave state alone
|
|
111
|
+
setMounted(false);
|
|
112
|
+
const fn = pendingRef.current;
|
|
113
|
+
pendingRef.current = null;
|
|
114
|
+
fn?.();
|
|
115
|
+
});
|
|
116
|
+
return undefined;
|
|
117
|
+
}, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
118
|
+
|
|
119
|
+
const trigger = (handler: () => void) => () => {
|
|
120
|
+
// Capture the handler and ask parent to close. The exit-animation
|
|
121
|
+
// completion above fires `handler` AFTER the sheet has slid away,
|
|
122
|
+
// so iOS no longer tries to present a picker over a dismissing
|
|
123
|
+
// modal — and Android no longer needs the ad-hoc 150ms setTimeout.
|
|
124
|
+
pendingRef.current = handler;
|
|
125
|
+
onClose();
|
|
47
126
|
};
|
|
48
127
|
|
|
49
128
|
const rows: {
|
|
@@ -75,52 +154,66 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
|
|
|
75
154
|
return (
|
|
76
155
|
<Modal
|
|
77
156
|
transparent
|
|
78
|
-
visible={
|
|
79
|
-
|
|
157
|
+
visible={mounted}
|
|
158
|
+
// We drive the animation ourselves — disable RN's built-in slide
|
|
159
|
+
// (which slid the backdrop too) so the fade + spring above are the
|
|
160
|
+
// only motion.
|
|
161
|
+
animationType="none"
|
|
80
162
|
onRequestClose={onClose}
|
|
81
|
-
|
|
163
|
+
// Hardware-back / iOS gesture also routes through onClose, which
|
|
164
|
+
// flips `visible` and triggers our exit animation above.
|
|
82
165
|
>
|
|
83
|
-
<
|
|
84
|
-
style={styles.backdrop}
|
|
85
|
-
activeOpacity={1}
|
|
86
|
-
onPress={onClose}
|
|
166
|
+
<Animated.View
|
|
167
|
+
style={[styles.backdrop, { opacity: backdropOpacity }]}
|
|
87
168
|
>
|
|
88
|
-
<TouchableOpacity
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
169
|
+
<TouchableOpacity
|
|
170
|
+
style={StyleSheet.absoluteFill}
|
|
171
|
+
activeOpacity={1}
|
|
172
|
+
onPress={onClose}
|
|
173
|
+
/>
|
|
174
|
+
<Animated.View
|
|
175
|
+
style={[
|
|
176
|
+
styles.sheet,
|
|
177
|
+
{ transform: [{ translateY: sheetTranslateY }] },
|
|
178
|
+
]}
|
|
179
|
+
>
|
|
180
|
+
<TouchableOpacity activeOpacity={1}>
|
|
181
|
+
<View style={styles.grabber} />
|
|
182
|
+
<Text style={styles.title}>Attach</Text>
|
|
183
|
+
{rows.map(({ label, hint, Icon, handler }, idx) => (
|
|
184
|
+
<TouchableOpacity
|
|
185
|
+
key={label}
|
|
186
|
+
activeOpacity={0.6}
|
|
102
187
|
style={[
|
|
103
|
-
styles.
|
|
104
|
-
|
|
188
|
+
styles.row,
|
|
189
|
+
idx < rows.length - 1 && styles.rowDivider,
|
|
105
190
|
]}
|
|
191
|
+
onPress={trigger(handler)}
|
|
106
192
|
>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
193
|
+
<View
|
|
194
|
+
style={[
|
|
195
|
+
styles.iconBubble,
|
|
196
|
+
{ backgroundColor: primaryColor + '14' },
|
|
197
|
+
]}
|
|
198
|
+
>
|
|
199
|
+
<Icon color={primaryColor} width={20} height={20} />
|
|
200
|
+
</View>
|
|
201
|
+
<View style={styles.rowText}>
|
|
202
|
+
<Text style={styles.rowLabel}>{label}</Text>
|
|
203
|
+
<Text style={styles.rowHint}>{hint}</Text>
|
|
204
|
+
</View>
|
|
205
|
+
</TouchableOpacity>
|
|
206
|
+
))}
|
|
207
|
+
<TouchableOpacity
|
|
208
|
+
activeOpacity={0.6}
|
|
209
|
+
style={styles.cancelRow}
|
|
210
|
+
onPress={onClose}
|
|
211
|
+
>
|
|
212
|
+
<Text style={styles.cancelLabel}>Cancel</Text>
|
|
113
213
|
</TouchableOpacity>
|
|
114
|
-
))}
|
|
115
|
-
<TouchableOpacity
|
|
116
|
-
activeOpacity={0.6}
|
|
117
|
-
style={styles.cancelRow}
|
|
118
|
-
onPress={onClose}
|
|
119
|
-
>
|
|
120
|
-
<Text style={styles.cancelLabel}>Cancel</Text>
|
|
121
214
|
</TouchableOpacity>
|
|
122
|
-
</
|
|
123
|
-
</
|
|
215
|
+
</Animated.View>
|
|
216
|
+
</Animated.View>
|
|
124
217
|
</Modal>
|
|
125
218
|
);
|
|
126
219
|
};
|
|
@@ -293,11 +293,22 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
|
|
|
293
293
|
<ProfileImagePlaceholder
|
|
294
294
|
name={activeRoom.title || activeRoom.name}
|
|
295
295
|
icon={activeRoom.icon}
|
|
296
|
+
// Two gates control whether the icon is editable:
|
|
297
|
+
// 1. role — only non-participants could edit before.
|
|
298
|
+
// 2. NEW: `disableChatInfo.disableIconEdit` — hard kill from
|
|
299
|
+
// config so a consumer can make the icon read-only for
|
|
300
|
+
// everyone (e.g. branded rooms, patient-facing apps).
|
|
301
|
+
// Both must allow it for the picker/remove affordances to show.
|
|
296
302
|
upload={{
|
|
297
303
|
onUpload,
|
|
298
|
-
active:
|
|
304
|
+
active:
|
|
305
|
+
!config?.disableChatInfo?.disableIconEdit &&
|
|
306
|
+
activeRoom?.role !== 'participant',
|
|
307
|
+
}}
|
|
308
|
+
remove={{
|
|
309
|
+
enabled: !config?.disableChatInfo?.disableIconEdit,
|
|
310
|
+
onRemoveClick,
|
|
299
311
|
}}
|
|
300
|
-
remove={{ enabled: true, onRemoveClick }}
|
|
301
312
|
role={activeRoom?.role}
|
|
302
313
|
size={128}
|
|
303
314
|
/>
|
|
@@ -377,7 +388,18 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
|
|
|
377
388
|
>
|
|
378
389
|
<Pressable
|
|
379
390
|
style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', gap: 16 }}
|
|
380
|
-
|
|
391
|
+
// `disableChatInfo.disableMemberTap` blocks the
|
|
392
|
+
// tap entirely so the user-profile popup never
|
|
393
|
+
// opens. `disableMemberProfileActions` only hid
|
|
394
|
+
// the action buttons INSIDE that popup, leaving
|
|
395
|
+
// the popup itself to open on tap — this gate
|
|
396
|
+
// closes the door. Customer-reported #16.
|
|
397
|
+
onPress={
|
|
398
|
+
config?.disableChatInfo?.disableMemberTap
|
|
399
|
+
? undefined
|
|
400
|
+
: () => handleUserAvatarClick(user)
|
|
401
|
+
}
|
|
402
|
+
disabled={!!config?.disableChatInfo?.disableMemberTap}
|
|
381
403
|
>
|
|
382
404
|
<ProfileImagePlaceholder
|
|
383
405
|
name={`${user.firstName} ${user.lastName}`}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import GViewWebView from './GViewWebView';
|
|
4
4
|
|
|
5
5
|
interface DocumentViewerProps {
|
|
6
6
|
/** Public CDN URL of the doc. Must be reachable from Google's servers. */
|
|
@@ -14,28 +14,16 @@ interface DocumentViewerProps {
|
|
|
14
14
|
* PPTX / TXT / RTF / CSV) and other non-PDF formats supported by
|
|
15
15
|
* Google's free gview rendering service.
|
|
16
16
|
*
|
|
17
|
-
* Strategy: embed `https://docs.google.com/gview?url=<encoded>&embedded=true`
|
|
18
|
-
* inside a WebView. Google fetches the doc from its public URL and
|
|
19
|
-
* renders it to a viewable iframe — no client-side dependency on
|
|
20
|
-
* native office libraries, no extra peer dep. Works for any backend
|
|
21
|
-
* whose file URLs are publicly reachable (which Ethora's `/files/`
|
|
22
|
-
* already are — they're CDN URLs returned in `item.location`).
|
|
23
17
|
*
|
|
24
18
|
* Limitations (documented for the integrator):
|
|
25
19
|
* - Requires public URL — won't work for auth-gated downloads.
|
|
26
20
|
* - First load can take 2-5s on slow networks.
|
|
27
21
|
* - Read-only — no editing, no print/share buttons.
|
|
28
22
|
*
|
|
29
|
-
* For PDFs use the existing PdfViewer (downloads then renders locally).
|
|
30
23
|
*/
|
|
31
24
|
const DocumentViewer: React.FC<DocumentViewerProps> = ({ url, fileName }) => {
|
|
32
|
-
const [loaded, setLoaded] = useState(false);
|
|
33
25
|
const [errored, setErrored] = useState(false);
|
|
34
26
|
|
|
35
|
-
const viewerUrl = `https://docs.google.com/gview?url=${encodeURIComponent(
|
|
36
|
-
url
|
|
37
|
-
)}&embedded=true`;
|
|
38
|
-
|
|
39
27
|
if (errored) {
|
|
40
28
|
return (
|
|
41
29
|
<View style={styles.center}>
|
|
@@ -50,37 +38,12 @@ const DocumentViewer: React.FC<DocumentViewerProps> = ({ url, fileName }) => {
|
|
|
50
38
|
);
|
|
51
39
|
}
|
|
52
40
|
|
|
53
|
-
return (
|
|
54
|
-
<View style={styles.container}>
|
|
55
|
-
<WebView
|
|
56
|
-
source={{ uri: viewerUrl }}
|
|
57
|
-
style={styles.webview}
|
|
58
|
-
originWhitelist={['*']}
|
|
59
|
-
startInLoadingState
|
|
60
|
-
onLoadEnd={() => setLoaded(true)}
|
|
61
|
-
onError={() => setErrored(true)}
|
|
62
|
-
onHttpError={() => setErrored(true)}
|
|
63
|
-
/>
|
|
64
|
-
{!loaded && (
|
|
65
|
-
<View style={styles.loadingOverlay} pointerEvents="none">
|
|
66
|
-
<ActivityIndicator size="large" />
|
|
67
|
-
</View>
|
|
68
|
-
)}
|
|
69
|
-
</View>
|
|
70
|
-
);
|
|
41
|
+
return <GViewWebView url={url} onError={() => setErrored(true)} />;
|
|
71
42
|
};
|
|
72
43
|
|
|
73
44
|
export default DocumentViewer;
|
|
74
45
|
|
|
75
46
|
const styles = StyleSheet.create({
|
|
76
|
-
container: { flex: 1, width: '100%', backgroundColor: '#fff' },
|
|
77
|
-
webview: { flex: 1, backgroundColor: '#fff' },
|
|
78
|
-
loadingOverlay: {
|
|
79
|
-
...StyleSheet.absoluteFillObject,
|
|
80
|
-
justifyContent: 'center',
|
|
81
|
-
alignItems: 'center',
|
|
82
|
-
backgroundColor: '#ffffff',
|
|
83
|
-
},
|
|
84
47
|
center: {
|
|
85
48
|
flex: 1,
|
|
86
49
|
alignItems: 'center',
|
|
@@ -27,7 +27,7 @@ import { useToast } from '../../../context/ToastContext';
|
|
|
27
27
|
import PdfViewer from './PdfView';
|
|
28
28
|
import DocumentViewer from './DocumentViewer';
|
|
29
29
|
import AudioMessage from '../../styled/AudioMessage';
|
|
30
|
-
import { ensureFilenameHasExtension } from '../../../helpers/mimeToExtension';
|
|
30
|
+
import { ensureFilenameHasExtension, isLikelyAudio } from '../../../helpers/mimeToExtension';
|
|
31
31
|
|
|
32
32
|
// MIME types Google's gview embed renders reliably. Everything else
|
|
33
33
|
// falls through to the info-card so the user can still download.
|
|
@@ -240,7 +240,44 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
240
240
|
);
|
|
241
241
|
case activeFile.mimetype.startsWith('video/'):
|
|
242
242
|
return <ModalVideo uri={activeFile.fileURL} />;
|
|
243
|
-
case activeFile.mimetype.
|
|
243
|
+
case activeFile.mimetype.includes('application/octet-stream'):
|
|
244
|
+
return (
|
|
245
|
+
<View
|
|
246
|
+
style={{
|
|
247
|
+
width: '100%',
|
|
248
|
+
padding: 20,
|
|
249
|
+
gap: 12,
|
|
250
|
+
backgroundColor: '#FFF8ED',
|
|
251
|
+
borderRadius: 16,
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
<Text style={{ fontSize: 16, fontWeight: '600' }}>
|
|
255
|
+
Voice message
|
|
256
|
+
</Text>
|
|
257
|
+
<AudioMessage
|
|
258
|
+
src={activeFile.fileURL}
|
|
259
|
+
mimeType={activeFile.mimetype}
|
|
260
|
+
fileName={activeFile.fileName}
|
|
261
|
+
originalName={activeFile.originalName}
|
|
262
|
+
duration={activeFile.duration}
|
|
263
|
+
waveForm={activeFile.waveForm}
|
|
264
|
+
/>
|
|
265
|
+
</View>
|
|
266
|
+
);
|
|
267
|
+
// Mirrors the MediaMessage heuristic: treat octet-stream voicemails
|
|
268
|
+
// with audio-shaped filenames / URLs as audio so the preview shows
|
|
269
|
+
// the player instead of an "Unsupported" card. Customer-reported
|
|
270
|
+
// #9 voicemail fix — see isLikelyAudio in mimeToExtension.ts.
|
|
271
|
+
case isLikelyAudio(
|
|
272
|
+
activeFile.mimetype,
|
|
273
|
+
activeFile.fileName,
|
|
274
|
+
activeFile.fileURL,
|
|
275
|
+
{
|
|
276
|
+
duration: activeFile.duration,
|
|
277
|
+
waveForm: activeFile.waveForm,
|
|
278
|
+
originalName: activeFile.originalName,
|
|
279
|
+
}
|
|
280
|
+
):
|
|
244
281
|
return (
|
|
245
282
|
<View
|
|
246
283
|
style={{
|
|
@@ -253,11 +290,18 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
253
290
|
>
|
|
254
291
|
<Text style={{ fontSize: 16, fontWeight: '600' }}>
|
|
255
292
|
{ensureFilenameHasExtension(
|
|
256
|
-
activeFile.fileName,
|
|
293
|
+
activeFile.originalName || activeFile.fileName,
|
|
257
294
|
activeFile.mimetype
|
|
258
295
|
)}
|
|
259
296
|
</Text>
|
|
260
|
-
<AudioMessage
|
|
297
|
+
<AudioMessage
|
|
298
|
+
src={activeFile.fileURL}
|
|
299
|
+
mimeType={activeFile.mimetype}
|
|
300
|
+
fileName={activeFile.fileName}
|
|
301
|
+
originalName={activeFile.originalName}
|
|
302
|
+
duration={activeFile.duration}
|
|
303
|
+
waveForm={activeFile.waveForm}
|
|
304
|
+
/>
|
|
261
305
|
</View>
|
|
262
306
|
);
|
|
263
307
|
case activeFile.mimetype === 'application/pdf':
|