@ethora/chat-component-rn 26.5.4 → 26.5.7
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 +66 -0
- package/README.md +25 -4
- package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js +1 -7
- package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +7 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatRoom.js +8 -2
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +2 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +11 -5
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageList.js +10 -2
- package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/Message.js +60 -7
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +1 -38
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -28
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +35 -16
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +11 -2
- package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js +9 -6
- package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
- package/lib/commonjs/components/UsersList/Checkbox.js +68 -0
- package/lib/commonjs/components/UsersList/Checkbox.js.map +1 -0
- package/lib/commonjs/components/UsersList/StyledComponents.js +1 -7
- package/lib/commonjs/components/UsersList/StyledComponents.js.map +1 -1
- package/lib/commonjs/components/UsersList/UsersList.js +3 -1
- package/lib/commonjs/components/UsersList/UsersList.js.map +1 -1
- package/lib/commonjs/components/styled/MediaFilePreview.js +96 -5
- package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +1 -2
- package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +4 -2
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +2 -10
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +71 -2
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/mimeToExtension.js +131 -0
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -0
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +73 -1
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/commonjs/hooks/useChatWrapperInit.js +22 -10
- package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/commonjs/hooks/useEventHandlers.js +9 -1
- package/lib/commonjs/hooks/useEventHandlers.js.map +1 -1
- package/lib/commonjs/hooks/useMessageHeapState.js +6 -2
- package/lib/commonjs/hooks/useMessageHeapState.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +48 -4
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/networking/api-requests/auth.api.js +7 -1
- package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +87 -1
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/roomHeapSlice.js +26 -8
- package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/module/components/ContextMenu/ContextMenuComponents.js +0 -6
- package/lib/module/components/ContextMenu/ContextMenuComponents.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +7 -1
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatRoom.js +8 -2
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +2 -1
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +11 -5
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MainComponents/MessageList.js +11 -3
- package/lib/module/components/MainComponents/MessageList.js.map +1 -1
- package/lib/module/components/MessageBubble/Message.js +60 -7
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +3 -40
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +21 -29
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +36 -17
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +11 -2
- package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js +9 -6
- package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
- package/lib/module/components/UsersList/Checkbox.js +63 -0
- package/lib/module/components/UsersList/Checkbox.js.map +1 -0
- package/lib/module/components/UsersList/StyledComponents.js +0 -6
- package/lib/module/components/UsersList/StyledComponents.js.map +1 -1
- package/lib/module/components/UsersList/UsersList.js +2 -1
- package/lib/module/components/UsersList/UsersList.js.map +1 -1
- package/lib/module/components/styled/MediaFilePreview.js +97 -6
- package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +1 -2
- package/lib/module/components/styled/MessageImage.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +4 -2
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +2 -10
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/xmppProvider.js +73 -3
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/mimeToExtension.js +124 -0
- package/lib/module/helpers/mimeToExtension.js.map +1 -0
- package/lib/module/helpers/resolveInitBeforeLoadUser.js +71 -0
- package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/module/hooks/useChatWrapperInit.js +22 -10
- package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/module/hooks/useEventHandlers.js +9 -1
- package/lib/module/hooks/useEventHandlers.js.map +1 -1
- package/lib/module/hooks/useMessageHeapState.js +6 -2
- package/lib/module/hooks/useMessageHeapState.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +49 -5
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/networking/api-requests/auth.api.js +7 -1
- package/lib/module/networking/api-requests/auth.api.js.map +1 -1
- package/lib/module/networking/xmppClient.js +87 -1
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/roomHeapSlice.js +24 -7
- package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/typescript/components/ContextMenu/ContextMenuComponents.d.ts +0 -1
- package/lib/typescript/components/ContextMenu/ContextMenuComponents.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/MainComponents/MessageList.d.ts.map +1 -1
- 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/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts +6 -0
- package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts +8 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts.map +1 -1
- package/lib/typescript/components/UsersList/Checkbox.d.ts +16 -0
- package/lib/typescript/components/UsersList/Checkbox.d.ts.map +1 -0
- package/lib/typescript/components/UsersList/StyledComponents.d.ts +0 -1
- package/lib/typescript/components/UsersList/StyledComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
- package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/mimeToExtension.d.ts +37 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -0
- package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts +18 -0
- package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
- package/lib/typescript/hooks/useEventHandlers.d.ts +6 -0
- package/lib/typescript/hooks/useEventHandlers.d.ts.map +1 -1
- package/lib/typescript/hooks/useMessageHeapState.d.ts +2 -0
- package/lib/typescript/hooks/useMessageHeapState.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts +25 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
- package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +6 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/metro.js +17 -57
- package/package.json +1 -1
- package/src/components/ContextMenu/ContextMenuComponents.tsx +0 -7
- package/src/components/MainComponents/ChatHeader.tsx +5 -1
- package/src/components/MainComponents/ChatRoom.tsx +6 -1
- package/src/components/MainComponents/ChatWrapper.tsx +1 -0
- package/src/components/MainComponents/MediaMessage.tsx +15 -8
- package/src/components/MainComponents/MessageList.tsx +10 -3
- package/src/components/MessageBubble/Message.tsx +64 -10
- package/src/components/MessageBubble/MessageInteractions.tsx +2 -105
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +20 -41
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +30 -12
- package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +22 -1
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +1 -1
- package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +9 -7
- package/src/components/UsersList/Checkbox.tsx +68 -0
- package/src/components/UsersList/StyledComponents.ts +0 -8
- package/src/components/UsersList/UsersList.tsx +1 -1
- package/src/components/styled/MediaFilePreview.tsx +91 -5
- package/src/components/styled/MessageImage.tsx +1 -2
- package/src/components/styled/SendInput.tsx +3 -0
- package/src/components/styled/VideoMessage.tsx +3 -10
- package/src/context/xmppProvider.tsx +90 -2
- package/src/helpers/mimeToExtension.ts +135 -0
- package/src/helpers/resolveInitBeforeLoadUser.ts +79 -0
- package/src/hooks/useChatWrapperInit.ts +51 -46
- package/src/hooks/useEventHandlers.tsx +24 -1
- package/src/hooks/useMessageHeapState.tsx +6 -2
- package/src/hooks/useSendMessage.tsx +75 -4
- package/src/networking/api-requests/auth.api.ts +5 -1
- package/src/networking/xmppClient.ts +95 -1
- package/src/roomStore/roomHeapSlice.ts +47 -4
- package/src/types/types.ts +10 -0
- package/lib/commonjs/components/InputComponents/AudioInput.js +0 -23
- package/lib/commonjs/components/InputComponents/AudioInput.js.map +0 -1
- package/lib/commonjs/components/InputComponents/AudioRecorder.js +0 -101
- package/lib/commonjs/components/InputComponents/AudioRecorder.js.map +0 -1
- package/lib/commonjs/components/InputComponents/MediaInput.js +0 -98
- package/lib/commonjs/components/InputComponents/MediaInput.js.map +0 -1
- package/lib/commonjs/components/InputComponents/RecordingIndicator.js +0 -70
- package/lib/commonjs/components/InputComponents/RecordingIndicator.js.map +0 -1
- package/lib/commonjs/components/Modals/ModalSelectMedia/ModalSelectMedia.js +0 -207
- package/lib/commonjs/components/Modals/ModalSelectMedia/ModalSelectMedia.js.map +0 -1
- package/lib/commonjs/components/OperationalModal/OperationalModal.js +0 -83
- package/lib/commonjs/components/OperationalModal/OperationalModal.js.map +0 -1
- package/lib/module/components/InputComponents/AudioInput.js +0 -18
- package/lib/module/components/InputComponents/AudioInput.js.map +0 -1
- package/lib/module/components/InputComponents/AudioRecorder.js +0 -95
- package/lib/module/components/InputComponents/AudioRecorder.js.map +0 -1
- package/lib/module/components/InputComponents/MediaInput.js +0 -92
- package/lib/module/components/InputComponents/MediaInput.js.map +0 -1
- package/lib/module/components/InputComponents/RecordingIndicator.js +0 -65
- package/lib/module/components/InputComponents/RecordingIndicator.js.map +0 -1
- package/lib/module/components/Modals/ModalSelectMedia/ModalSelectMedia.js +0 -200
- package/lib/module/components/Modals/ModalSelectMedia/ModalSelectMedia.js.map +0 -1
- package/lib/module/components/OperationalModal/OperationalModal.js +0 -77
- package/lib/module/components/OperationalModal/OperationalModal.js.map +0 -1
- package/lib/typescript/components/InputComponents/AudioInput.d.ts +0 -9
- package/lib/typescript/components/InputComponents/AudioInput.d.ts.map +0 -1
- package/lib/typescript/components/InputComponents/AudioRecorder.d.ts +0 -9
- package/lib/typescript/components/InputComponents/AudioRecorder.d.ts.map +0 -1
- package/lib/typescript/components/InputComponents/MediaInput.d.ts +0 -16
- package/lib/typescript/components/InputComponents/MediaInput.d.ts.map +0 -1
- package/lib/typescript/components/InputComponents/RecordingIndicator.d.ts +0 -4
- package/lib/typescript/components/InputComponents/RecordingIndicator.d.ts.map +0 -1
- package/lib/typescript/components/Modals/ModalSelectMedia/ModalSelectMedia.d.ts +0 -9
- package/lib/typescript/components/Modals/ModalSelectMedia/ModalSelectMedia.d.ts.map +0 -1
- package/lib/typescript/components/OperationalModal/OperationalModal.d.ts +0 -9
- package/lib/typescript/components/OperationalModal/OperationalModal.d.ts.map +0 -1
- package/src/components/InputComponents/AudioInput.tsx +0 -24
- package/src/components/InputComponents/AudioRecorder.tsx +0 -100
- package/src/components/InputComponents/MediaInput.tsx +0 -113
- package/src/components/InputComponents/RecordingIndicator.tsx +0 -77
- package/src/components/Modals/ModalSelectMedia/ModalSelectMedia.tsx +0 -283
- package/src/components/OperationalModal/OperationalModal.tsx +0 -78
|
@@ -31,6 +31,7 @@ import { parseMessageBody } from '../../helpers/parseMessageBody';
|
|
|
31
31
|
import { useMessageHeapState } from '../../hooks/useMessageHeapState';
|
|
32
32
|
import { DoubleTick } from '../../assets/icons';
|
|
33
33
|
import { useXmppClient } from '../../context/xmppProvider';
|
|
34
|
+
import { useSendMessage } from '../../hooks/useSendMessage';
|
|
34
35
|
import { MessageReaction } from './MessageReaction';
|
|
35
36
|
import { MessageFooter } from '../styled/StyledComponents';
|
|
36
37
|
|
|
@@ -47,14 +48,17 @@ const CustomMessageBubble = styled.View<{
|
|
|
47
48
|
deleted?: boolean;
|
|
48
49
|
backgroundMessageUser?: string;
|
|
49
50
|
backgroundMessage?: string;
|
|
51
|
+
isMedia?: boolean;
|
|
50
52
|
}>`
|
|
51
53
|
position: relative;
|
|
52
54
|
max-width: 85%;
|
|
53
55
|
min-width: 30%;
|
|
54
|
-
padding:
|
|
56
|
+
padding: ${({isMedia}) => isMedia ? '0' : '10'}px;
|
|
57
|
+
padding-bottom: ${({isMedia}) => isMedia ? '6' : '10'}px;
|
|
55
58
|
margin-right: ${({isUser}) => isUser ? '0' : '10px'};
|
|
56
59
|
margin-left: ${({isUser}) => isUser ? '10px' : '0'};
|
|
57
60
|
border-radius: 10px;
|
|
61
|
+
overflow: hidden;
|
|
58
62
|
border-bottom-left-radius: ${({ isUser }) => (isUser ? '10' : '0')}px;
|
|
59
63
|
border-bottom-right-radius: ${({ isUser }) => (isUser ? '0' : '10')}px;
|
|
60
64
|
background-color: ${({
|
|
@@ -113,7 +117,8 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
113
117
|
const dispatch = useDispatch();
|
|
114
118
|
const { client } = useXmppClient();
|
|
115
119
|
const { config, langSource, user} = useChatSettingState();
|
|
116
|
-
const { idSet } = useMessageHeapState();
|
|
120
|
+
const { idSet, failedIdSet } = useMessageHeapState();
|
|
121
|
+
const { retryMessage } = useSendMessage();
|
|
117
122
|
|
|
118
123
|
const [isPressed, setIsPressed] = useState(false);
|
|
119
124
|
|
|
@@ -218,14 +223,41 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
218
223
|
if (messageRef.current) {
|
|
219
224
|
const nodeHandle = findNodeHandle(messageRef.current);
|
|
220
225
|
if (nodeHandle) {
|
|
221
|
-
UIManager.measure(nodeHandle, (
|
|
226
|
+
UIManager.measure(nodeHandle, (_x, _y, _width, height, pageX, pageY) => {
|
|
222
227
|
const screenHeight = Dimensions.get('window').height;
|
|
228
|
+
// MessageInteractions is ~5 rows (delete/edit/reply/react/copy)
|
|
229
|
+
// at ~48px each + padding, plus emoji bar on top. 280 covers
|
|
230
|
+
// both even with a couple of long labels. The legacy 150
|
|
231
|
+
// figure assumed a much shorter menu and stranded the popup
|
|
232
|
+
// off-screen on the last message of the room.
|
|
233
|
+
const MENU_HEIGHT = 280;
|
|
234
|
+
// Treat the consumer-supplied keyboardVerticalOffset as a
|
|
235
|
+
// proxy for tab bar + bottom safe-area when present; falls
|
|
236
|
+
// back to a reasonable default so the menu doesn't bump into
|
|
237
|
+
// the nav bar on phones without one in config.
|
|
238
|
+
const bottomReserve =
|
|
239
|
+
(config?.keyboardVerticalOffset ?? 0) + 24;
|
|
240
|
+
const topReserve = 16;
|
|
241
|
+
|
|
242
|
+
const spaceBelow = screenHeight - (pageY + height) - bottomReserve;
|
|
243
|
+
const spaceAbove = pageY - topReserve;
|
|
244
|
+
|
|
245
|
+
let menuY: number;
|
|
246
|
+
if (spaceBelow >= MENU_HEIGHT) {
|
|
247
|
+
menuY = pageY + height;
|
|
248
|
+
} else if (spaceAbove >= MENU_HEIGHT) {
|
|
249
|
+
menuY = pageY - MENU_HEIGHT;
|
|
250
|
+
} else {
|
|
251
|
+
// Neither side has room (e.g. very small phone, large
|
|
252
|
+
// menu) — clamp to the visible region so at least the
|
|
253
|
+
// top of the menu is reachable.
|
|
254
|
+
menuY = Math.max(
|
|
255
|
+
topReserve,
|
|
256
|
+
screenHeight - MENU_HEIGHT - bottomReserve
|
|
257
|
+
);
|
|
258
|
+
}
|
|
223
259
|
|
|
224
|
-
|
|
225
|
-
setContextMenuPosition({
|
|
226
|
-
x: pageX,
|
|
227
|
-
y: enoughSpaceBelow ? pageY + height : pageY - 150,
|
|
228
|
-
});
|
|
260
|
+
setContextMenuPosition({ x: pageX, y: menuY });
|
|
229
261
|
});
|
|
230
262
|
}
|
|
231
263
|
}
|
|
@@ -236,7 +268,13 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
236
268
|
? parseMessageBody(config?.messageTextFilter.filterFunction(message.body))
|
|
237
269
|
: parseMessageBody(message.body);
|
|
238
270
|
|
|
239
|
-
const
|
|
271
|
+
const isFailed = failedIdSet.has(message.id);
|
|
272
|
+
const isPending =
|
|
273
|
+
!isFailed && (idSet.has(message.id) || message?.pending || false);
|
|
274
|
+
|
|
275
|
+
const onRetryPress = () => {
|
|
276
|
+
if (isFailed) {retryMessage(message.id);}
|
|
277
|
+
};
|
|
240
278
|
|
|
241
279
|
return (
|
|
242
280
|
<View>
|
|
@@ -279,6 +317,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
279
317
|
<CustomMessageBubble
|
|
280
318
|
isUser={isUser}
|
|
281
319
|
deleted={message.isDeleted}
|
|
320
|
+
isMedia={message?.isMediafile === 'true' && !message?.isDeleted}
|
|
282
321
|
backgroundMessageUser={config?.messageColor?.backgroundMessageUser}
|
|
283
322
|
backgroundMessage={config?.messageColor?.backgroundMessage}
|
|
284
323
|
>
|
|
@@ -327,11 +366,21 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
327
366
|
)}
|
|
328
367
|
<CustomMessageTimestamp>
|
|
329
368
|
{!config?.disableSentLogic && isUser && isPending && <Text>sending...</Text>}
|
|
369
|
+
{!config?.disableSentLogic && isUser && isFailed && (
|
|
370
|
+
<Text
|
|
371
|
+
onPress={onRetryPress}
|
|
372
|
+
style={styles.failedText}
|
|
373
|
+
accessibilityRole="button"
|
|
374
|
+
accessibilityLabel="Retry sending message"
|
|
375
|
+
>
|
|
376
|
+
! Failed — tap to retry
|
|
377
|
+
</Text>
|
|
378
|
+
)}
|
|
330
379
|
{new Date(message.date).toLocaleTimeString([], {
|
|
331
380
|
hour: '2-digit',
|
|
332
381
|
minute: '2-digit',
|
|
333
382
|
})}
|
|
334
|
-
{!config?.disableSentLogic && isUser && !isPending && (
|
|
383
|
+
{!config?.disableSentLogic && isUser && !isPending && !isFailed && (
|
|
335
384
|
<DoubleTick />
|
|
336
385
|
)}
|
|
337
386
|
</CustomMessageTimestamp>
|
|
@@ -401,4 +450,9 @@ const styles = StyleSheet.create({
|
|
|
401
450
|
marginTop: 5,
|
|
402
451
|
alignSelf: 'flex-end',
|
|
403
452
|
},
|
|
453
|
+
failedText: {
|
|
454
|
+
color: '#E53935',
|
|
455
|
+
fontWeight: '600',
|
|
456
|
+
marginRight: 6,
|
|
457
|
+
},
|
|
404
458
|
});
|
|
@@ -1,40 +1,21 @@
|
|
|
1
|
-
import React, { useMemo
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { Delimeter, MenuItem } from '../ContextMenu/ContextMenuComponents';
|
|
3
|
-
import {
|
|
3
|
+
import { useSelector } from 'react-redux';
|
|
4
4
|
import { RootState } from '../../roomStore';
|
|
5
5
|
import {
|
|
6
6
|
MESSAGE_INTERACTIONS,
|
|
7
7
|
MESSAGE_INTERACTIONS_ICONS,
|
|
8
8
|
} from '../../helpers/constants/MESSAGE_INTERACTIONS';
|
|
9
9
|
import { IMessage } from '../../types/types';
|
|
10
|
-
import { useXmppClient } from '../../context/xmppProvider';
|
|
11
|
-
import { setActiveMessage } from '../../roomStore/roomsSlice';
|
|
12
10
|
import {
|
|
13
|
-
Alert,
|
|
14
11
|
Modal,
|
|
15
12
|
Text,
|
|
16
|
-
TouchableWithoutFeedback,
|
|
17
13
|
StyleSheet,
|
|
18
14
|
View,
|
|
19
15
|
Pressable,
|
|
20
16
|
} from 'react-native';
|
|
21
17
|
import * as Clipboard from 'expo-clipboard';
|
|
22
18
|
import { useToast } from '../../context/ToastContext';
|
|
23
|
-
import EmojiPicker from 'react-native-emoji-selector';
|
|
24
|
-
|
|
25
|
-
const fixedEmojiIds = ['joy', 'heart', 'fire', '+1', 'smile', 'scream'];
|
|
26
|
-
|
|
27
|
-
function convertIdToEmoji(id: string) {
|
|
28
|
-
const mapping = {
|
|
29
|
-
joy: '😂',
|
|
30
|
-
heart: '❤️',
|
|
31
|
-
fire: '🔥',
|
|
32
|
-
'+1': '👍',
|
|
33
|
-
smile: '😄',
|
|
34
|
-
scream: '😱',
|
|
35
|
-
};
|
|
36
|
-
return mapping[id as keyof typeof mapping] || '❓';
|
|
37
|
-
}
|
|
38
19
|
|
|
39
20
|
interface MessageInteractionsProps {
|
|
40
21
|
isReply?: boolean;
|
|
@@ -57,38 +38,13 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
57
38
|
handleReplyMessage: replyMessage,
|
|
58
39
|
handleDeleteMessage,
|
|
59
40
|
handleEditMessage,
|
|
60
|
-
handleReactionMessage,
|
|
61
41
|
}) => {
|
|
62
|
-
const { client } = useXmppClient();
|
|
63
|
-
const dispatch = useDispatch();
|
|
64
42
|
const { showToast } = useToast();
|
|
65
43
|
|
|
66
|
-
const [pickerVisible, setPickerVisible] = useState(false);
|
|
67
|
-
|
|
68
|
-
const handleReactionClick = (id: string) => {
|
|
69
|
-
handleReactionMessage(id);
|
|
70
|
-
closeMenu();
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const onClose = () => {
|
|
74
|
-
setPickerVisible(false);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// const handleDeleteMessage = (roomJid: string, messageId: string) => {
|
|
78
|
-
// // dispatch(deleteRoomMessage({ roomJID: room, messageId: msgId }));
|
|
79
|
-
// client.deleteMessageStanza(roomJid, messageId);
|
|
80
|
-
// };
|
|
81
|
-
|
|
82
44
|
const config = useSelector(
|
|
83
45
|
(state: RootState) => state.chatSettingStore.config
|
|
84
46
|
);
|
|
85
47
|
|
|
86
|
-
const closeContextMenu = () => {
|
|
87
|
-
// if (!config?.disableInteractions) {
|
|
88
|
-
// setContextMenu({ visible: false, x: 0, y: 0 });
|
|
89
|
-
// }
|
|
90
|
-
};
|
|
91
|
-
|
|
92
48
|
const handleCopyMessage = async (text: string) => {
|
|
93
49
|
try {
|
|
94
50
|
await Clipboard.setStringAsync(text);
|
|
@@ -143,60 +99,6 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
143
99
|
<View style={styles.overlayFill}>
|
|
144
100
|
<Pressable style={StyleSheet.absoluteFill} onPress={closeMenu} />
|
|
145
101
|
<View style={[styles.contextMenu, memoPosition]}>
|
|
146
|
-
{/* <MenuItem onClick={() => console.log(MESSAGE_INTERACTIONS.SEND_COINS)}>
|
|
147
|
-
{MESSAGE_INTERACTIONS.SEND_COINS}
|
|
148
|
-
<MESSAGE_INTERACTIONS_ICONS.SEND_COINS />{' '}
|
|
149
|
-
</MenuItem>
|
|
150
|
-
<Delimeter />
|
|
151
|
-
<MenuItem onClick={() => console.log(MESSAGE_INTERACTIONS.SEND_ITEM)}>
|
|
152
|
-
{MESSAGE_INTERACTIONS.SEND_ITEM}
|
|
153
|
-
<MESSAGE_INTERACTIONS_ICONS.SEND_ITEM />{' '}
|
|
154
|
-
</MenuItem> */}
|
|
155
|
-
{/*
|
|
156
|
-
{!config?.disableReactions && (
|
|
157
|
-
<View style={{ flexDirection: 'row', paddingBottom: 10 }}>
|
|
158
|
-
{fixedEmojiIds.map((id) => (
|
|
159
|
-
<Pressable
|
|
160
|
-
key={id}
|
|
161
|
-
onPress={() => handleReactionClick(id)}
|
|
162
|
-
style={{ marginRight: 6 }}
|
|
163
|
-
>
|
|
164
|
-
<Text style={{ fontSize: 26 }}>{convertIdToEmoji(id)}</Text>
|
|
165
|
-
</Pressable>
|
|
166
|
-
))}
|
|
167
|
-
<Pressable
|
|
168
|
-
onPress={() => setPickerVisible(!pickerVisible)}
|
|
169
|
-
style={{ marginLeft: 10 }}
|
|
170
|
-
>
|
|
171
|
-
<Text style={{ fontSize: 24 }}>⌄</Text>
|
|
172
|
-
</Pressable>
|
|
173
|
-
</View>
|
|
174
|
-
)}
|
|
175
|
-
|
|
176
|
-
{!config?.disableReactions && pickerVisible && (
|
|
177
|
-
<View style={{ height: 250 }}>
|
|
178
|
-
<EmojiPicker
|
|
179
|
-
onEmojiSelected={(emoji) => {
|
|
180
|
-
handleReactionClick(emoji);
|
|
181
|
-
}}
|
|
182
|
-
showSearchBar={false}
|
|
183
|
-
showTabs={false}
|
|
184
|
-
/>
|
|
185
|
-
</View>
|
|
186
|
-
)}
|
|
187
|
-
*/}
|
|
188
|
-
|
|
189
|
-
{/*
|
|
190
|
-
{!isReply && (
|
|
191
|
-
<>
|
|
192
|
-
<MenuItem onPress={handleReplyMessage}>
|
|
193
|
-
<Text>{MESSAGE_INTERACTIONS.REPLY}</Text>
|
|
194
|
-
<MESSAGE_INTERACTIONS_ICONS.REPLY />
|
|
195
|
-
</MenuItem>
|
|
196
|
-
<Delimeter />
|
|
197
|
-
</>
|
|
198
|
-
)}
|
|
199
|
-
*/}
|
|
200
102
|
<MenuItem onPress={() => handleCopyMessage(message.body!)}>
|
|
201
103
|
<Text>{MESSAGE_INTERACTIONS.COPY}</Text>
|
|
202
104
|
<MESSAGE_INTERACTIONS_ICONS.COPY />
|
|
@@ -215,11 +117,6 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
215
117
|
</MenuItem>
|
|
216
118
|
</>
|
|
217
119
|
)}
|
|
218
|
-
{/* <Delimeter />
|
|
219
|
-
<MenuItem onClick={() => console.log(MESSAGE_INTERACTIONS.REPORT)}>
|
|
220
|
-
{MESSAGE_INTERACTIONS.REPORT}
|
|
221
|
-
<MESSAGE_INTERACTIONS_ICONS.REPORT />{' '}
|
|
222
|
-
</MenuItem> */}
|
|
223
120
|
</View>
|
|
224
121
|
</View>
|
|
225
122
|
)}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
Viewider,
|
|
12
12
|
Divider,
|
|
13
13
|
} from '../styledModalComponents';
|
|
14
|
-
import { Pressable, View, Text, ScrollView, Alert, Linking
|
|
14
|
+
import { Pressable, View, Text, ScrollView, Alert, Linking } from 'react-native';
|
|
15
15
|
import ModalHeaderComponent from '../ModalHeaderComponent';
|
|
16
16
|
import { ProfileImagePlaceholder } from '../../MainComponents/ProfileImagePlaceholder';
|
|
17
17
|
import { useDispatch, useSelector } from 'react-redux';
|
|
@@ -19,14 +19,7 @@ import { RootState, getActiveRoom } from '../../../roomStore';
|
|
|
19
19
|
import { uploadFile } from '../../../networking/api-requests/auth.api';
|
|
20
20
|
import { useXmppClient } from '../../../context/xmppProvider';
|
|
21
21
|
import { updateRoom } from '../../../roomStore/roomsSlice';
|
|
22
|
-
import ImagePicker from '
|
|
23
|
-
import {
|
|
24
|
-
check,
|
|
25
|
-
request,
|
|
26
|
-
PERMISSIONS,
|
|
27
|
-
RESULTS,
|
|
28
|
-
Permission,
|
|
29
|
-
} from 'react-native-permissions';
|
|
22
|
+
import * as ImagePicker from 'expo-image-picker';
|
|
30
23
|
import Loader from '../../styled/Loader';
|
|
31
24
|
import Button from '../../styled/Button';
|
|
32
25
|
import { DeleteIcon, MoreIcon, QrIcon } from '../../../assets/icons';
|
|
@@ -89,40 +82,24 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
|
|
|
89
82
|
}
|
|
90
83
|
}, [client, activeRoom?.jid]);
|
|
91
84
|
|
|
92
|
-
const checkPermission = async (permission: Permission) => {
|
|
93
|
-
const status = await check(permission);
|
|
94
|
-
if (status === RESULTS.GRANTED) {
|
|
95
|
-
return status;
|
|
96
|
-
} else if (status === RESULTS.DENIED) {
|
|
97
|
-
const requestStatus = await request(permission);
|
|
98
|
-
return requestStatus;
|
|
99
|
-
}
|
|
100
|
-
return status;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
85
|
const onUpload = async () => {
|
|
104
86
|
let loadingSet = false;
|
|
105
87
|
try {
|
|
106
88
|
setLoading(true);
|
|
107
89
|
loadingSet = true;
|
|
108
90
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (permissionStatus !== RESULTS.GRANTED) {
|
|
91
|
+
// expo-image-picker owns the permission prompt internally and
|
|
92
|
+
// returns a `granted` flag. No separate react-native-permissions
|
|
93
|
+
// round-trip needed.
|
|
94
|
+
const perm = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
95
|
+
if (!perm.granted) {
|
|
116
96
|
Alert.alert(
|
|
117
97
|
'Permission required',
|
|
118
98
|
'Photo library permission is needed to select images.',
|
|
119
99
|
[
|
|
120
100
|
{
|
|
121
101
|
text: 'Cancel',
|
|
122
|
-
onPress: () =>
|
|
123
|
-
setLoading(false);
|
|
124
|
-
console.log('Photo permission cancelled');
|
|
125
|
-
},
|
|
102
|
+
onPress: () => setLoading(false),
|
|
126
103
|
style: 'cancel',
|
|
127
104
|
},
|
|
128
105
|
{
|
|
@@ -134,18 +111,20 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
|
|
|
134
111
|
return;
|
|
135
112
|
}
|
|
136
113
|
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
compressImageQuality: 0.8,
|
|
114
|
+
const result = await ImagePicker.launchImageLibraryAsync({
|
|
115
|
+
mediaTypes: ['images'],
|
|
116
|
+
allowsEditing: true,
|
|
117
|
+
aspect: [1, 1],
|
|
118
|
+
quality: 0.8,
|
|
143
119
|
});
|
|
144
|
-
|
|
145
|
-
|
|
120
|
+
if (result.canceled || !result.assets?.[0]) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const asset = result.assets[0];
|
|
124
|
+
const originalName = asset.fileName || asset.uri.split('/').pop();
|
|
146
125
|
const fileObject = {
|
|
147
|
-
uri:
|
|
148
|
-
type:
|
|
126
|
+
uri: asset.uri,
|
|
127
|
+
type: asset.mimeType || 'image/jpeg',
|
|
149
128
|
name: originalName || `profile_${Date.now()}.jpg`,
|
|
150
129
|
};
|
|
151
130
|
|
|
@@ -17,6 +17,7 @@ import * as FileSystem from 'expo-file-system/legacy';
|
|
|
17
17
|
import * as MediaLibrary from 'expo-media-library';
|
|
18
18
|
import { useToast } from '../../../context/ToastContext';
|
|
19
19
|
import PdfViewer from './PdfView';
|
|
20
|
+
import { ensureFilenameHasExtension } from '../../../helpers/mimeToExtension';
|
|
20
21
|
|
|
21
22
|
export const FullScreenVideo = styled.View`
|
|
22
23
|
width: 100%;
|
|
@@ -61,10 +62,10 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const fileName = ensureFilenameHasExtension(
|
|
66
|
+
activeFile.fileName,
|
|
67
|
+
activeFile.mimetype
|
|
68
|
+
);
|
|
68
69
|
|
|
69
70
|
const filePath = FileSystem.cacheDirectory + fileName;
|
|
70
71
|
const download = await FileSystem.downloadAsync(activeFile.fileURL, filePath);
|
|
@@ -87,7 +88,14 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
87
88
|
|
|
88
89
|
const saveFileToDownloads = async () => {
|
|
89
90
|
try {
|
|
90
|
-
|
|
91
|
+
// Always include a valid extension on the cache filename. expo-media-
|
|
92
|
+
// library's createAssetAsync inspects the URI's extension and throws
|
|
93
|
+
// "Could not get the file's extension" when missing — see bug #9 in
|
|
94
|
+
// sdk-bug-tracker.md.
|
|
95
|
+
const fileName = ensureFilenameHasExtension(
|
|
96
|
+
activeFile.fileName,
|
|
97
|
+
activeFile.mimetype
|
|
98
|
+
);
|
|
91
99
|
const filePath = FileSystem.cacheDirectory + fileName;
|
|
92
100
|
const download = await FileSystem.downloadAsync(activeFile.fileURL, filePath);
|
|
93
101
|
|
|
@@ -162,23 +170,33 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
162
170
|
);
|
|
163
171
|
case activeFile.mimetype === 'application/pdf':
|
|
164
172
|
return <PdfViewer pdfUrl={activeFile.fileURL} />;
|
|
165
|
-
default:
|
|
173
|
+
default: {
|
|
174
|
+
const displayName = ensureFilenameHasExtension(
|
|
175
|
+
activeFile.fileName,
|
|
176
|
+
activeFile.mimetype
|
|
177
|
+
);
|
|
166
178
|
return (
|
|
167
179
|
<View
|
|
168
180
|
style={{
|
|
169
181
|
backgroundColor: '#FFF8ED',
|
|
170
182
|
borderRadius: 16,
|
|
171
|
-
|
|
172
|
-
|
|
183
|
+
padding: 20,
|
|
184
|
+
gap: 8,
|
|
173
185
|
}}
|
|
174
186
|
>
|
|
175
|
-
<Text>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
<Text style={{ fontSize: 16, fontWeight: '600' }}>
|
|
188
|
+
{displayName}
|
|
189
|
+
</Text>
|
|
190
|
+
<Text style={{ color: '#666' }}>
|
|
191
|
+
{activeFile.mimetype || 'unknown type'}
|
|
192
|
+
</Text>
|
|
193
|
+
<Text style={{ marginTop: 8 }}>
|
|
194
|
+
This file format can't be previewed inline. Tap the save icon
|
|
195
|
+
above to download it.
|
|
179
196
|
</Text>
|
|
180
197
|
</View>
|
|
181
198
|
);
|
|
199
|
+
}
|
|
182
200
|
}
|
|
183
201
|
}, [activeFile]);
|
|
184
202
|
|
|
@@ -22,6 +22,12 @@ interface ModalWrapperProps {
|
|
|
22
22
|
setTextarea?: (value: string) => void;
|
|
23
23
|
handleCloseModal: () => void;
|
|
24
24
|
handleClick: () => void;
|
|
25
|
+
/**
|
|
26
|
+
* When true, render a small centered dialog instead of the default
|
|
27
|
+
* full-screen container. Used for confirm-style modals (Delete, Leave
|
|
28
|
+
* room) where edge-to-edge feels too heavy.
|
|
29
|
+
*/
|
|
30
|
+
compact?: boolean;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
export const ModalWrapper: FC<ModalWrapperProps> = ({
|
|
@@ -35,6 +41,7 @@ export const ModalWrapper: FC<ModalWrapperProps> = ({
|
|
|
35
41
|
setTextarea,
|
|
36
42
|
handleCloseModal,
|
|
37
43
|
handleClick,
|
|
44
|
+
compact = false,
|
|
38
45
|
}) => {
|
|
39
46
|
const textareaRef = useRef<TextInput>(null);
|
|
40
47
|
|
|
@@ -54,7 +61,21 @@ export const ModalWrapper: FC<ModalWrapperProps> = ({
|
|
|
54
61
|
onRequestClose={handleCloseModal}
|
|
55
62
|
>
|
|
56
63
|
<ModalBackground>
|
|
57
|
-
<ModalContainer
|
|
64
|
+
<ModalContainer
|
|
65
|
+
style={
|
|
66
|
+
compact
|
|
67
|
+
? {
|
|
68
|
+
flex: 0,
|
|
69
|
+
width: '85%',
|
|
70
|
+
maxWidth: 360,
|
|
71
|
+
height: 'auto',
|
|
72
|
+
padding: 24,
|
|
73
|
+
borderRadius: 16,
|
|
74
|
+
gap: 16,
|
|
75
|
+
}
|
|
76
|
+
: { maxWidth: 640 }
|
|
77
|
+
}
|
|
78
|
+
>
|
|
58
79
|
<CloseButton onPress={handleCloseModal}>
|
|
59
80
|
<Text style={{ fontSize: 24 }}>×</Text>
|
|
60
81
|
</CloseButton>
|
|
@@ -5,20 +5,22 @@ import {
|
|
|
5
5
|
LastRoomMessageText,
|
|
6
6
|
} from './StyledRoomComponents';
|
|
7
7
|
import { LastMessage } from '../../../types/types';
|
|
8
|
-
import emojiData from 'emoji-mart';
|
|
9
8
|
|
|
10
9
|
interface LastMessageEmojiProps extends Pick<LastMessage, 'user' | 'emoji'> {}
|
|
11
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Renders the per-room last-message preview when the latest message
|
|
13
|
+
* is a reaction (emoji). The `emoji` field is expected to already be
|
|
14
|
+
* a unicode glyph (the reaction stanza carries the rendered character,
|
|
15
|
+
* not an `:id:`) so we just render it as-is — no emoji-id resolution
|
|
16
|
+
* library needed. If a legacy payload still ships an `:id:`, it
|
|
17
|
+
* renders verbatim instead of crashing.
|
|
18
|
+
*/
|
|
12
19
|
const LastMessageEmoji: FC<LastMessageEmojiProps> = ({ user, emoji }) => {
|
|
13
|
-
const memoEmoji = (id: string) => {
|
|
14
|
-
const emoji = (emojiData as any).emojis[id];
|
|
15
|
-
return emoji ? emoji.skins[0].native : '';
|
|
16
|
-
};
|
|
17
|
-
|
|
18
20
|
return (
|
|
19
21
|
<LastRoomMessageContainer>
|
|
20
22
|
<LastRoomMessageName>{user?.name || ''}:</LastRoomMessageName>
|
|
21
|
-
<LastRoomMessageText>{emoji
|
|
23
|
+
<LastRoomMessageText>{emoji || ''}</LastRoomMessageText>
|
|
22
24
|
</LastRoomMessageContainer>
|
|
23
25
|
);
|
|
24
26
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface CheckboxProps {
|
|
5
|
+
value: boolean;
|
|
6
|
+
onValueChange: (next: boolean) => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
color?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Minimal RN-native checkbox replacement for the user-list multi-select.
|
|
13
|
+
* Avoids pulling in `@react-native-community/checkbox` (legacy native
|
|
14
|
+
* module that needs the Metro shim) — the UI footprint is one tap-target
|
|
15
|
+
* with a check glyph, so a plain Pressable does the job.
|
|
16
|
+
*/
|
|
17
|
+
const Checkbox: React.FC<CheckboxProps> = ({
|
|
18
|
+
value,
|
|
19
|
+
onValueChange,
|
|
20
|
+
disabled,
|
|
21
|
+
color = '#0052cd',
|
|
22
|
+
}) => {
|
|
23
|
+
return (
|
|
24
|
+
<Pressable
|
|
25
|
+
accessibilityRole="checkbox"
|
|
26
|
+
accessibilityState={{ checked: value, disabled: !!disabled }}
|
|
27
|
+
disabled={disabled}
|
|
28
|
+
onPress={() => onValueChange(!value)}
|
|
29
|
+
style={[
|
|
30
|
+
styles.box,
|
|
31
|
+
{ borderColor: color, backgroundColor: value ? color : 'transparent' },
|
|
32
|
+
disabled && styles.disabled,
|
|
33
|
+
]}
|
|
34
|
+
>
|
|
35
|
+
{value && (
|
|
36
|
+
<View style={styles.checkInner}>
|
|
37
|
+
<Text style={styles.checkGlyph}>✓</Text>
|
|
38
|
+
</View>
|
|
39
|
+
)}
|
|
40
|
+
</Pressable>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const styles = StyleSheet.create({
|
|
45
|
+
box: {
|
|
46
|
+
width: 18,
|
|
47
|
+
height: 18,
|
|
48
|
+
borderWidth: 1.5,
|
|
49
|
+
borderRadius: 3,
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
justifyContent: 'center',
|
|
52
|
+
},
|
|
53
|
+
checkInner: {
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
justifyContent: 'center',
|
|
56
|
+
},
|
|
57
|
+
checkGlyph: {
|
|
58
|
+
color: '#fff',
|
|
59
|
+
fontSize: 12,
|
|
60
|
+
fontWeight: '700',
|
|
61
|
+
lineHeight: 14,
|
|
62
|
+
},
|
|
63
|
+
disabled: {
|
|
64
|
+
opacity: 0.4,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export default Checkbox;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import styled from 'styled-components/native';
|
|
2
|
-
import CheckBox from '@react-native-community/checkbox';
|
|
3
2
|
|
|
4
3
|
export const ScrollableContainer = styled.View`
|
|
5
4
|
max-height: 100px;
|
|
@@ -17,13 +16,6 @@ export const UserItem = styled.TouchableHighlight`
|
|
|
17
16
|
gap: 8px;
|
|
18
17
|
`;
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
export const Checkbox = styled(CheckBox)`
|
|
22
|
-
width: 16px;
|
|
23
|
-
height: 16px;
|
|
24
|
-
tint-color: #0052cd;
|
|
25
|
-
`;
|
|
26
|
-
|
|
27
19
|
export const Label = styled.Text`
|
|
28
20
|
font-size: 16px;
|
|
29
21
|
color: #333;
|
|
@@ -9,9 +9,9 @@ import { ModalTitle, LabelData } from '../Modals/styledModalComponents';
|
|
|
9
9
|
import {
|
|
10
10
|
ScrollableContainer,
|
|
11
11
|
UserItem,
|
|
12
|
-
Checkbox,
|
|
13
12
|
Label,
|
|
14
13
|
} from './StyledComponents';
|
|
14
|
+
import Checkbox from './Checkbox';
|
|
15
15
|
import { useSelector } from 'react-redux';
|
|
16
16
|
import { RootState } from '../../roomStore';
|
|
17
17
|
import { RoomMember } from '../../types/types';
|