@droppii-org/chat-mobile 0.2.35 → 0.2.38
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/lib/module/components/messages/linkMessage/index.js +1 -10
- package/lib/module/components/messages/linkMessage/index.js.map +1 -1
- package/lib/module/components/messages/quotedMessage/index.js +173 -0
- package/lib/module/components/messages/quotedMessage/index.js.map +1 -0
- package/lib/module/context/ChatContext.js +26 -22
- package/lib/module/context/ChatContext.js.map +1 -1
- package/lib/module/context/ChatDetailContext.js +7 -4
- package/lib/module/context/ChatDetailContext.js.map +1 -1
- package/lib/module/core/useChatListener.js +0 -1
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/core/useUserListener.js +12 -21
- package/lib/module/core/useUserListener.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +26 -4
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/useChatMessages.js.map +1 -1
- package/lib/module/hooks/useFileAttachment.js +3 -1
- package/lib/module/hooks/useFileAttachment.js.map +1 -1
- package/lib/module/index.js +0 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +2 -1
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatCurrentQuotedMessage.js +142 -0
- package/lib/module/screens/chat-detail/ChatCurrentQuotedMessage.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatDetail.js +10 -1
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/PinnedMessageBar.js +8 -3
- package/lib/module/screens/chat-detail/PinnedMessageBar.js.map +1 -1
- package/lib/module/screens/chat-detail/PinnedMessagesSheet.js +173 -0
- package/lib/module/screens/chat-detail/PinnedMessagesSheet.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +2 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js +17 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js +11 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +1 -0
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js +5 -2
- package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js.map +1 -1
- package/lib/module/screens/inbox/Inbox.js +1 -5
- package/lib/module/screens/inbox/Inbox.js.map +1 -1
- package/lib/module/services/apis.js +0 -3
- package/lib/module/services/apis.js.map +1 -1
- package/lib/module/services/message.js.map +1 -1
- package/lib/module/store/message.js.map +1 -1
- package/lib/module/store/pinnedMessage.js.map +1 -1
- package/lib/module/store/user.js +2 -3
- package/lib/module/store/user.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +9 -2
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/auth.js +0 -7
- package/lib/module/types/auth.js.map +1 -1
- package/lib/module/types/chat.js +12 -0
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/utils/UIUtils.js +6 -0
- package/lib/module/utils/UIUtils.js.map +1 -1
- package/lib/module/utils/legendListMessage.js +1 -0
- package/lib/module/utils/legendListMessage.js.map +1 -1
- package/lib/module/utils/message.js.map +1 -1
- package/lib/module/utils/messageUtils.js +18 -1
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/quotedMessage/index.d.ts +7 -0
- package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/types.d.ts +2 -2
- package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
- package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
- package/lib/typescript/src/context/ChatDetailContext.d.ts.map +1 -1
- package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
- package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/usePinMessage.d.ts +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useChatMessages.d.ts +2 -2
- package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useFileAttachment.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatCurrentQuotedMessage.d.ts +2 -0
- package/lib/typescript/src/screens/chat-detail/ChatCurrentQuotedMessage.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/PinnedMessageBar.d.ts +4 -3
- package/lib/typescript/src/screens/chat-detail/PinnedMessageBar.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts +9 -0
- package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +2 -3
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts +11 -7
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts +4 -4
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts +5 -3
- package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts +2 -2
- package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts +5 -5
- package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
- package/lib/typescript/src/services/apis.d.ts +0 -1
- package/lib/typescript/src/services/apis.d.ts.map +1 -1
- package/lib/typescript/src/services/message.d.ts +1 -2
- package/lib/typescript/src/services/message.d.ts.map +1 -1
- package/lib/typescript/src/store/message.d.ts.map +1 -1
- package/lib/typescript/src/store/pinnedMessage.d.ts +4 -4
- package/lib/typescript/src/store/pinnedMessage.d.ts.map +1 -1
- package/lib/typescript/src/store/user.d.ts +4 -4
- package/lib/typescript/src/store/user.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/auth.d.ts +0 -5
- package/lib/typescript/src/types/auth.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +19 -36
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/utils/UIUtils.d.ts.map +1 -1
- package/lib/typescript/src/utils/legendListMessage.d.ts +4 -4
- package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
- package/lib/typescript/src/utils/message.d.ts +4 -5
- package/lib/typescript/src/utils/message.d.ts.map +1 -1
- package/lib/typescript/src/utils/messageUtils.d.ts +9 -7
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/messages/linkMessage/index.tsx +1 -11
- package/src/components/messages/quotedMessage/index.tsx +197 -0
- package/src/components/messages/types.ts +2 -2
- package/src/context/ChatContext.tsx +30 -25
- package/src/context/ChatDetailContext.tsx +9 -3
- package/src/core/useChatListener.ts +0 -1
- package/src/core/useUserListener.ts +9 -25
- package/src/hooks/message/useSendMessage.ts +25 -4
- package/src/hooks/useChatMessages.ts +4 -6
- package/src/hooks/useFileAttachment.ts +3 -1
- package/src/index.tsx +5 -6
- package/src/screens/chat-detail/ChatComposer.tsx +2 -0
- package/src/screens/chat-detail/ChatCurrentQuotedMessage.tsx +150 -0
- package/src/screens/chat-detail/ChatDetail.tsx +13 -2
- package/src/screens/chat-detail/ChatListLegend.tsx +3 -3
- package/src/screens/chat-detail/PinnedMessageBar.tsx +53 -49
- package/src/screens/chat-detail/PinnedMessagesSheet.tsx +243 -0
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +4 -4
- package/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.tsx +30 -6
- package/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.tsx +15 -6
- package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +1 -0
- package/src/screens/chat-detail/messageToolbox/messageToolbox.utils.ts +21 -12
- package/src/screens/chat-detail/messageToolbox/types.ts +2 -2
- package/src/screens/chat-detail/types.ts +5 -5
- package/src/screens/inbox/Inbox.tsx +1 -5
- package/src/services/apis.ts +0 -4
- package/src/services/message.ts +1 -2
- package/src/store/message.ts +3 -3
- package/src/store/pinnedMessage.ts +4 -4
- package/src/store/user.ts +5 -5
- package/src/translation/resources/i18n.ts +10 -1
- package/src/types/auth.ts +0 -6
- package/src/types/chat.ts +19 -40
- package/src/utils/UIUtils.ts +5 -0
- package/src/utils/legendListMessage.ts +4 -5
- package/src/utils/message.ts +10 -11
- package/src/utils/messageUtils.ts +37 -9
|
@@ -11,12 +11,16 @@ interface MessageToolboxMenuProps {
|
|
|
11
11
|
showUnpinMessage?: boolean;
|
|
12
12
|
showRetryMessage?: boolean;
|
|
13
13
|
showRevokeMessage?: boolean;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
showViewOriginalMessage?: boolean;
|
|
15
|
+
onPinMessage?: () => void;
|
|
16
|
+
onUnpinMessage?: () => void;
|
|
17
|
+
onViewOriginalMessage?: () => void;
|
|
18
|
+
showQuoteMessage?: boolean;
|
|
19
|
+
onCopyMessage?: () => void;
|
|
20
|
+
onCopyMessageLink?: () => void;
|
|
21
|
+
onRetryMessage?: () => void;
|
|
22
|
+
onRevokeMessage?: () => void;
|
|
23
|
+
onQuoteMessage?: () => void;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export const MessageToolboxMenu = memo(
|
|
@@ -27,15 +31,27 @@ export const MessageToolboxMenu = memo(
|
|
|
27
31
|
showUnpinMessage,
|
|
28
32
|
showRetryMessage,
|
|
29
33
|
showRevokeMessage,
|
|
34
|
+
showViewOriginalMessage,
|
|
35
|
+
showQuoteMessage,
|
|
30
36
|
onCopyMessage,
|
|
31
37
|
onCopyMessageLink,
|
|
32
38
|
onPinMessage,
|
|
33
39
|
onUnpinMessage,
|
|
34
40
|
onRetryMessage,
|
|
35
41
|
onRevokeMessage,
|
|
42
|
+
onViewOriginalMessage,
|
|
43
|
+
onQuoteMessage,
|
|
36
44
|
}: MessageToolboxMenuProps) => {
|
|
37
45
|
return (
|
|
38
46
|
<KContainer.View style={styles.card}>
|
|
47
|
+
<MessageToolboxItem
|
|
48
|
+
icon="arrow-reply-o"
|
|
49
|
+
color={KColors.gray.dark}
|
|
50
|
+
label={trans('chat:reply_message')}
|
|
51
|
+
visible={showQuoteMessage}
|
|
52
|
+
onPress={onQuoteMessage}
|
|
53
|
+
/>
|
|
54
|
+
|
|
39
55
|
<MessageToolboxItem
|
|
40
56
|
icon="copy-o"
|
|
41
57
|
color={KColors.gray.dark}
|
|
@@ -68,6 +84,14 @@ export const MessageToolboxMenu = memo(
|
|
|
68
84
|
onPress={onUnpinMessage}
|
|
69
85
|
/>
|
|
70
86
|
|
|
87
|
+
<MessageToolboxItem
|
|
88
|
+
icon="chat-square-o"
|
|
89
|
+
color={KColors.gray.dark}
|
|
90
|
+
label={trans('chat:view_original_message')}
|
|
91
|
+
visible={showViewOriginalMessage}
|
|
92
|
+
onPress={onViewOriginalMessage}
|
|
93
|
+
/>
|
|
94
|
+
|
|
71
95
|
<MessageToolboxItem
|
|
72
96
|
icon="arrow-reload-o"
|
|
73
97
|
color={KColors.danger.normal}
|
|
@@ -15,17 +15,18 @@ import {
|
|
|
15
15
|
getCopyableMessageText,
|
|
16
16
|
getMessageToolboxItems,
|
|
17
17
|
} from './messageToolbox.utils';
|
|
18
|
-
import type { DMessageItem } from '../../../types/chat';
|
|
19
18
|
import type { MeasuredRect, MessageToolboxRef } from './types';
|
|
20
19
|
import { MessageToolboxWrapper } from './MessageToolboxWrapper';
|
|
21
20
|
import type { ReactNode } from 'react';
|
|
22
21
|
import { useSendMessage } from '../../../hooks/message/useSendMessage';
|
|
22
|
+
import { useChatDetailContext } from '../../../context';
|
|
23
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
23
24
|
|
|
24
25
|
interface MessageToolboxOverlayProps {
|
|
25
26
|
currentUserId?: string;
|
|
26
|
-
customMessageItemFactory?: (item:
|
|
27
|
-
onPinMessage?: (message:
|
|
28
|
-
onUnpinMessage?: (message:
|
|
27
|
+
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
28
|
+
onPinMessage?: (message: MessageItem) => void;
|
|
29
|
+
onUnpinMessage?: (message: MessageItem) => void;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export const MessageToolboxOverlay = forwardRef<
|
|
@@ -41,8 +42,9 @@ export const MessageToolboxOverlay = forwardRef<
|
|
|
41
42
|
},
|
|
42
43
|
ref
|
|
43
44
|
) => {
|
|
45
|
+
const { setQuotedMessage } = useChatDetailContext();
|
|
44
46
|
const [visible, setVisible] = useState(false);
|
|
45
|
-
const [message, setMessage] = useState<
|
|
47
|
+
const [message, setMessage] = useState<MessageItem | undefined>(undefined);
|
|
46
48
|
const [rect, setRect] = useState<MeasuredRect | null>(null);
|
|
47
49
|
const toolboxItems = useMemo(
|
|
48
50
|
() => getMessageToolboxItems(message, currentUserId),
|
|
@@ -57,7 +59,7 @@ export const MessageToolboxOverlay = forwardRef<
|
|
|
57
59
|
}, []);
|
|
58
60
|
|
|
59
61
|
const show = useCallback(
|
|
60
|
-
(msg:
|
|
62
|
+
(msg: MessageItem, msgRect: MeasuredRect) => {
|
|
61
63
|
const toolboxNotAvailable =
|
|
62
64
|
Object.values(getMessageToolboxItems(msg, currentUserId)).filter(
|
|
63
65
|
Boolean
|
|
@@ -135,6 +137,12 @@ export const MessageToolboxOverlay = forwardRef<
|
|
|
135
137
|
onUnpinMessage?.(message);
|
|
136
138
|
}, [message, hide, onUnpinMessage]);
|
|
137
139
|
|
|
140
|
+
const handleQuoteMessage = useCallback(() => {
|
|
141
|
+
if (!message) return;
|
|
142
|
+
setQuotedMessage(message);
|
|
143
|
+
hide();
|
|
144
|
+
}, [message, hide, setQuotedMessage]);
|
|
145
|
+
|
|
138
146
|
if (!visible || !message || !rect) {
|
|
139
147
|
return null;
|
|
140
148
|
}
|
|
@@ -171,6 +179,7 @@ export const MessageToolboxOverlay = forwardRef<
|
|
|
171
179
|
onUnpinMessage={handleUnpinMessage}
|
|
172
180
|
onRetryMessage={handleRetryMessage}
|
|
173
181
|
onRevokeMessage={handleRevokeMessage}
|
|
182
|
+
onQuoteMessage={handleQuoteMessage}
|
|
174
183
|
/>
|
|
175
184
|
}
|
|
176
185
|
/>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
MessageStatus,
|
|
3
|
+
MessageType,
|
|
4
|
+
type MessageItem,
|
|
5
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
3
6
|
import { getMessageText } from '../../../utils/legendListMessage';
|
|
4
7
|
import { isMessageRevoked } from '../../../utils/messageUtils';
|
|
5
8
|
|
|
@@ -22,46 +25,50 @@ const PIN_SUPPORTED_TYPES: number[] = [
|
|
|
22
25
|
MessageType.FileMessage,
|
|
23
26
|
];
|
|
24
27
|
|
|
25
|
-
const isMessageSucceeded = (message:
|
|
28
|
+
const isMessageSucceeded = (message: MessageItem): boolean =>
|
|
26
29
|
message.status !== MessageStatus.Failed &&
|
|
27
30
|
message.status !== MessageStatus.Sending;
|
|
28
31
|
|
|
29
32
|
// Failed or revoked messages never offer either copy action.
|
|
30
|
-
const isEligibleForToolbox = (message:
|
|
33
|
+
const isEligibleForToolbox = (message: MessageItem): boolean =>
|
|
31
34
|
isMessageSucceeded(message) && !isMessageRevoked(message);
|
|
32
35
|
|
|
33
|
-
const isCopyMessageSupported = (message:
|
|
36
|
+
const isCopyMessageSupported = (message: MessageItem): boolean =>
|
|
34
37
|
isEligibleForToolbox(message) &&
|
|
35
38
|
COPYABLE_TEXT_TYPES.includes(message.contentType);
|
|
36
39
|
|
|
37
40
|
// All message types are supported, as long as the message is eligible.
|
|
38
|
-
// const isCopyMessageLinkSupported = (message:
|
|
41
|
+
// const isCopyMessageLinkSupported = (message: MessageItem): boolean =>
|
|
39
42
|
// isEligibleForToolbox(message);
|
|
40
43
|
|
|
41
|
-
const isRetrySupported = (message:
|
|
44
|
+
const isRetrySupported = (message: MessageItem): boolean =>
|
|
42
45
|
message.status === MessageStatus.Failed;
|
|
43
46
|
|
|
44
47
|
// Merged/album messages (grouped photos) count as "image" for pinning
|
|
45
48
|
// purposes, mirroring the preview logic in utils/messageUtils.ts.
|
|
46
|
-
const isMergedImageMessage = (message:
|
|
49
|
+
const isMergedImageMessage = (message: MessageItem): boolean =>
|
|
47
50
|
message.contentType === MessageType.MergeMessage &&
|
|
48
51
|
!!message.mergeElem?.multiMessage?.some(
|
|
49
52
|
(msg) => msg.contentType === MessageType.PictureMessage
|
|
50
53
|
);
|
|
51
54
|
|
|
52
55
|
// BR-002: Pin message supports only successfully sent messages.
|
|
53
|
-
const isPinMessageSupported = (message:
|
|
56
|
+
const isPinMessageSupported = (message: MessageItem): boolean =>
|
|
54
57
|
isEligibleForToolbox(message) &&
|
|
55
58
|
(PIN_SUPPORTED_TYPES.includes(message.contentType) ||
|
|
56
59
|
isMergedImageMessage(message));
|
|
57
60
|
|
|
61
|
+
const isQuoteMessageSupported = (message: MessageItem): boolean =>
|
|
62
|
+
isMessageSucceeded(message) &&
|
|
63
|
+
message?.contentType !== MessageType.RevokeMessage;
|
|
64
|
+
|
|
58
65
|
// Revoke is only available within 24h of a successful send.
|
|
59
66
|
const REVOKE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
60
67
|
|
|
61
68
|
// Revoke is only available for messages sent by the current user.
|
|
62
69
|
// And not limited by message content type.
|
|
63
70
|
const isRevokeMessageSupported = (
|
|
64
|
-
message:
|
|
71
|
+
message: MessageItem,
|
|
65
72
|
currentUserId?: string
|
|
66
73
|
): boolean =>
|
|
67
74
|
isEligibleForToolbox(message) &&
|
|
@@ -69,7 +76,7 @@ const isRevokeMessageSupported = (
|
|
|
69
76
|
message.sendID === currentUserId &&
|
|
70
77
|
Date.now() - (message.sendTime || message.createTime || 0) < REVOKE_WINDOW_MS;
|
|
71
78
|
|
|
72
|
-
export const getCopyableMessageText = (message:
|
|
79
|
+
export const getCopyableMessageText = (message: MessageItem): string => {
|
|
73
80
|
if (message.contentType === MessageType.UrlTextMessage) {
|
|
74
81
|
return message.urlTextElem?.content ?? '';
|
|
75
82
|
}
|
|
@@ -77,7 +84,7 @@ export const getCopyableMessageText = (message: DMessageItem): string => {
|
|
|
77
84
|
};
|
|
78
85
|
|
|
79
86
|
export const getMessageToolboxItems = (
|
|
80
|
-
message?:
|
|
87
|
+
message?: MessageItem,
|
|
81
88
|
currentUserId?: string
|
|
82
89
|
) => {
|
|
83
90
|
if (message == null) {
|
|
@@ -88,6 +95,7 @@ export const getMessageToolboxItems = (
|
|
|
88
95
|
// const showCopyMessageLink = isCopyMessageLinkSupported(message);
|
|
89
96
|
const showRetryMessage = isRetrySupported(message);
|
|
90
97
|
const showRevokeMessage = isRevokeMessageSupported(message, currentUserId);
|
|
98
|
+
const showQuoteMessage = isQuoteMessageSupported(message);
|
|
91
99
|
|
|
92
100
|
const isPinSupported = isPinMessageSupported(message);
|
|
93
101
|
const showPinMessage = isPinSupported && !message.isPinned;
|
|
@@ -100,5 +108,6 @@ export const getMessageToolboxItems = (
|
|
|
100
108
|
showRevokeMessage,
|
|
101
109
|
showPinMessage,
|
|
102
110
|
showUnpinMessage,
|
|
111
|
+
showQuoteMessage,
|
|
103
112
|
};
|
|
104
113
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
2
2
|
|
|
3
3
|
export interface MeasuredRect {
|
|
4
4
|
x: number;
|
|
@@ -8,6 +8,6 @@ export interface MeasuredRect {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export interface MessageToolboxRef {
|
|
11
|
-
show: (message:
|
|
11
|
+
show: (message: MessageItem, rect: MeasuredRect) => void;
|
|
12
12
|
hide: () => void;
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { MediaItem } from '../../hooks/useMediaViewer';
|
|
3
|
-
import type { DMessageItem } from '../../types/chat';
|
|
4
3
|
import type { MeasuredRect } from './messageToolbox/types';
|
|
4
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
5
5
|
|
|
6
6
|
export type DChatActionIconProvider = 'MaterialCommunityIcons' | 'DroppiiNew';
|
|
7
7
|
|
|
@@ -70,9 +70,9 @@ export interface ChatComposerProps {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export interface ChatListProps {
|
|
73
|
-
messages?:
|
|
73
|
+
messages?: MessageItem[];
|
|
74
74
|
currentUserId?: string;
|
|
75
|
-
customMessageItemFactory?: (item:
|
|
75
|
+
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
76
76
|
onLoadEarlier?: () => void;
|
|
77
77
|
isLoading?: boolean;
|
|
78
78
|
isLoadingEarlier?: boolean;
|
|
@@ -82,7 +82,7 @@ export interface ChatListProps {
|
|
|
82
82
|
hasMoreNewer?: boolean;
|
|
83
83
|
onMediaPress?: (items: MediaItem[], index?: number) => void;
|
|
84
84
|
onPressUrl?: (url: string) => void;
|
|
85
|
-
onLongPress?: (message:
|
|
85
|
+
onLongPress?: (message: MessageItem, rect: MeasuredRect) => void;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
export interface ChatDetailProps extends Omit<
|
|
@@ -96,6 +96,6 @@ export interface ChatDetailProps extends Omit<
|
|
|
96
96
|
enabled?: boolean;
|
|
97
97
|
showAddMember?: boolean;
|
|
98
98
|
onPressUrl?: (url: string) => void;
|
|
99
|
-
customMessageItemFactory?: (item:
|
|
99
|
+
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
100
100
|
pannelActions?: DChatPannelAction[];
|
|
101
101
|
}
|
|
@@ -3,8 +3,6 @@ import { StyleSheet } from 'react-native';
|
|
|
3
3
|
import { TabView } from 'react-native-tab-view';
|
|
4
4
|
import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
|
|
5
5
|
import { MessagesTab } from './MessagesTab';
|
|
6
|
-
import { useUserStore } from '../../store';
|
|
7
|
-
import { LoginStatus } from '@droppii/openim-rn-client-sdk';
|
|
8
6
|
|
|
9
7
|
interface InboxProps {
|
|
10
8
|
onPressThread?: (item: string) => void;
|
|
@@ -17,7 +15,6 @@ type TabRoute = { key: string; title: string };
|
|
|
17
15
|
|
|
18
16
|
const Inbox = memo(
|
|
19
17
|
({ onPressThread, onPressSearch, onPressEdit, onPressGroup }: InboxProps) => {
|
|
20
|
-
const connectStatus = useUserStore((state) => state.connectStatus);
|
|
21
18
|
const [index, setIndex] = useState(0);
|
|
22
19
|
const [routes] = useState<TabRoute[]>([
|
|
23
20
|
{ key: 'messages', title: 'Tin nhắn' },
|
|
@@ -25,13 +22,12 @@ const Inbox = memo(
|
|
|
25
22
|
|
|
26
23
|
const renderScene = useCallback(
|
|
27
24
|
({ route }: { route: TabRoute }) => {
|
|
28
|
-
if (connectStatus !== LoginStatus.Logged) return null;
|
|
29
25
|
if (route.key === 'messages') {
|
|
30
26
|
return <MessagesTab onPressThread={onPressThread} />;
|
|
31
27
|
}
|
|
32
28
|
return null;
|
|
33
29
|
},
|
|
34
|
-
[onPressThread
|
|
30
|
+
[onPressThread]
|
|
35
31
|
);
|
|
36
32
|
|
|
37
33
|
const renderTabBar = useCallback(
|
package/src/services/apis.ts
CHANGED
package/src/services/message.ts
CHANGED
|
@@ -3,7 +3,6 @@ import OpenIMSDK, {
|
|
|
3
3
|
type AdvancedGetMessageResult,
|
|
4
4
|
type MessageItem,
|
|
5
5
|
} from '@droppii/openim-rn-client-sdk';
|
|
6
|
-
import type { DMessageItem } from '../types/chat';
|
|
7
6
|
import { useConversationStore } from '../store';
|
|
8
7
|
import { getReceiverId } from '../utils/message';
|
|
9
8
|
|
|
@@ -34,7 +33,7 @@ export namespace ChatMessageAPI {
|
|
|
34
33
|
|
|
35
34
|
export async function sendTextMessage(params: {
|
|
36
35
|
text: string;
|
|
37
|
-
}): Promise<
|
|
36
|
+
}): Promise<MessageItem> {
|
|
38
37
|
const currentConversation = useConversationStore
|
|
39
38
|
.getState()
|
|
40
39
|
.getCurrentConversation();
|
package/src/store/message.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
3
3
|
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
4
|
-
import type {
|
|
4
|
+
import type { MessageStore } from '../types/chat';
|
|
5
5
|
import { mergeMessages } from '../utils/message';
|
|
6
6
|
|
|
7
7
|
export const useMessageStore = create<MessageStore>()((set, get) => ({
|
|
@@ -15,7 +15,7 @@ export const useMessageStore = create<MessageStore>()((set, get) => ({
|
|
|
15
15
|
|
|
16
16
|
pushNewMessage: (message: MessageItem) => {
|
|
17
17
|
set((state) => ({
|
|
18
|
-
messages: mergeMessages(state.messages, [message
|
|
18
|
+
messages: mergeMessages(state.messages, [message]),
|
|
19
19
|
}));
|
|
20
20
|
},
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ export const useMessageStore = create<MessageStore>()((set, get) => ({
|
|
|
25
25
|
(msg) => msg.clientMsgID === message.clientMsgID
|
|
26
26
|
);
|
|
27
27
|
if (idx < 0) return;
|
|
28
|
-
tmpList[idx] = { ...tmpList[idx]!, ...message }
|
|
28
|
+
tmpList[idx] = { ...tmpList[idx]!, ...message };
|
|
29
29
|
set({ messages: tmpList });
|
|
30
30
|
},
|
|
31
31
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
1
2
|
import { create } from 'zustand';
|
|
2
|
-
import type { DMessageItem } from '../types/chat';
|
|
3
3
|
|
|
4
4
|
export interface PinnedMessageStore {
|
|
5
|
-
messages:
|
|
5
|
+
messages: MessageItem[];
|
|
6
6
|
totalCount: number;
|
|
7
|
-
setMessages: (messages:
|
|
8
|
-
addMessage: (message:
|
|
7
|
+
setMessages: (messages: MessageItem[], totalCount: number) => void;
|
|
8
|
+
addMessage: (message: MessageItem) => void;
|
|
9
9
|
removeMessage: (clientMsgID: string) => void;
|
|
10
10
|
reset: () => void;
|
|
11
11
|
}
|
package/src/store/user.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
|
-
import {
|
|
3
|
-
import { SyncStatus } from '../types/
|
|
2
|
+
import { type SelfUserInfo } from '@droppii/openim-rn-client-sdk';
|
|
3
|
+
import { ConnectStatus, SyncStatus } from '../types/chat';
|
|
4
4
|
|
|
5
5
|
export interface UserStore {
|
|
6
6
|
user?: SelfUserInfo;
|
|
7
7
|
setUser: (user: SelfUserInfo) => void;
|
|
8
|
-
connectStatus:
|
|
9
|
-
updateConnectStatus: (status:
|
|
8
|
+
connectStatus: ConnectStatus;
|
|
9
|
+
updateConnectStatus: (status: ConnectStatus) => void;
|
|
10
10
|
syncStatus: SyncStatus;
|
|
11
11
|
updateSyncStatus: (status: SyncStatus) => void;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const useUserStore = create<UserStore>()((set) => ({
|
|
15
|
-
connectStatus:
|
|
15
|
+
connectStatus: ConnectStatus.Disconnected,
|
|
16
16
|
syncStatus: SyncStatus.Loading,
|
|
17
17
|
setUser(user) {
|
|
18
18
|
set({ user });
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const i18nKey: Record<string, string> = {
|
|
2
2
|
'thread_card_bot_crm_name': 'Droppii Hỗ Trợ',
|
|
3
3
|
'thread_card_fallback_name': 'Người dùng',
|
|
4
|
-
'chat:file_too_large':
|
|
4
|
+
'chat:file_too_large':
|
|
5
|
+
'Tệp tải lên không hợp lệ. Kích thước tệp không được vượt quá {{maxSize}}MB',
|
|
5
6
|
'chat:file_max_count': 'Chỉ có thể đính kèm tối đa 3 tệp',
|
|
6
7
|
'chat:file_pick_failed': 'Không thể chọn tệp',
|
|
7
8
|
'chat:file_upload_failed': 'Lỗi tải tệp lên',
|
|
@@ -47,6 +48,10 @@ const i18nKey: Record<string, string> = {
|
|
|
47
48
|
'chat:pin_limit_desc':
|
|
48
49
|
'Chỉ được ghim tối đa 5 tin nhắn. Vui lòng bỏ ghim một tin nhắn hiện có trước khi ghim tin nhắn mới.',
|
|
49
50
|
'chat:pin_limit_primary': 'Đã hiểu',
|
|
51
|
+
'chat:pinned_messages_title': 'Tin nhắn đã ghim',
|
|
52
|
+
'chat:pinned_messages_hint':
|
|
53
|
+
'Nhấn giữ tin nhắn để chọn "Bỏ Ghim" hoặc "Xem tin nhắn gốc"',
|
|
54
|
+
'chat:view_original_message': 'Xem tin nhắn gốc',
|
|
50
55
|
'msg_type_image': '[Hình ảnh]',
|
|
51
56
|
'msg_type_voice': '[Tin nhắn thoại]',
|
|
52
57
|
'msg_type_video': '[Video]',
|
|
@@ -71,6 +76,10 @@ const i18nKey: Record<string, string> = {
|
|
|
71
76
|
'send_order': 'Gửi đơn hàng',
|
|
72
77
|
'send_product': 'Gửi sản phẩm',
|
|
73
78
|
'create_appointment': 'Tạo lịch hẹn',
|
|
79
|
+
'chat:reply_message': 'Trả lời',
|
|
80
|
+
'chat:reply_to': 'Trả lời {{name}}',
|
|
81
|
+
'yourself': 'chính mình',
|
|
82
|
+
'you': 'Bạn',
|
|
74
83
|
};
|
|
75
84
|
|
|
76
85
|
export default {
|
package/src/types/auth.ts
CHANGED
package/src/types/chat.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface ChatUIAdapter {
|
|
|
28
28
|
toast?: { open: (params: any) => void };
|
|
29
29
|
alert?: { open: (params: any) => void };
|
|
30
30
|
popup?: { open: (params: any) => void };
|
|
31
|
+
bottomSheet?: { open: (params: any) => void };
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export interface ChatContextType {
|
|
@@ -36,6 +37,7 @@ export interface ChatContextType {
|
|
|
36
37
|
onPressFlashMessage?: (message: ConversationItem) => void;
|
|
37
38
|
isShowFLashMessage: boolean;
|
|
38
39
|
setIsShowFLashMessage: (value: boolean) => void;
|
|
40
|
+
handleLoginSdk: () => void;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export type ChatProviderProps = PropsWithChildren<{
|
|
@@ -97,6 +99,18 @@ export interface DBotStatus {
|
|
|
97
99
|
botSupportPlatform: unknown | null;
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
export enum SyncStatus {
|
|
103
|
+
Loading = 0,
|
|
104
|
+
Success = 1,
|
|
105
|
+
Failed = 2,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export enum ConnectStatus {
|
|
109
|
+
Disconnected = 0,
|
|
110
|
+
Connected = 1,
|
|
111
|
+
Connecting = 2,
|
|
112
|
+
}
|
|
113
|
+
|
|
100
114
|
export interface DConversationPeerUser {
|
|
101
115
|
id: string;
|
|
102
116
|
username?: string;
|
|
@@ -118,42 +132,6 @@ export interface DConversationQueryParams {
|
|
|
118
132
|
pageSize: number;
|
|
119
133
|
}
|
|
120
134
|
|
|
121
|
-
export interface DMessageItem extends MessageItem {
|
|
122
|
-
isInternal?: boolean;
|
|
123
|
-
atUserIDList?: unknown;
|
|
124
|
-
attachedInfo?: 'null';
|
|
125
|
-
options?: DMessageOption;
|
|
126
|
-
isPinned?: boolean;
|
|
127
|
-
pinnedByUserID?: string;
|
|
128
|
-
pinnedTime?: number;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface DMessageOption {
|
|
132
|
-
conversationUpdate: boolean;
|
|
133
|
-
offlinePush: boolean;
|
|
134
|
-
senderConversationUpdate: boolean;
|
|
135
|
-
unreadCount: boolean;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export interface DMessagePushInfo {
|
|
139
|
-
title: string;
|
|
140
|
-
desc: string;
|
|
141
|
-
/**
|
|
142
|
-
* @instance DMessagePushInfoEx
|
|
143
|
-
*/
|
|
144
|
-
ex: string;
|
|
145
|
-
signalInfo: string;
|
|
146
|
-
iospushSound: 'default';
|
|
147
|
-
iosbadgeCount: boolean;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export interface DMessagePushInfoEx {
|
|
151
|
-
icon: string; // url
|
|
152
|
-
conversationId: string;
|
|
153
|
-
title: string;
|
|
154
|
-
desc: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
135
|
export interface IMessageItemEx {
|
|
158
136
|
applicationType: DChatApplicationType;
|
|
159
137
|
sessionId?: string;
|
|
@@ -167,13 +145,13 @@ export interface IMessageItemEx {
|
|
|
167
145
|
}
|
|
168
146
|
|
|
169
147
|
export interface MessageStore {
|
|
170
|
-
messages:
|
|
148
|
+
messages: MessageItem[];
|
|
171
149
|
hasMoreEarlier: boolean;
|
|
172
150
|
hasMoreNewer: boolean;
|
|
173
|
-
setMessages: (messages:
|
|
151
|
+
setMessages: (messages: MessageItem[], hasMore: boolean) => void;
|
|
174
152
|
pushNewMessage: (message: MessageItem) => void;
|
|
175
153
|
updateOneMessage: (
|
|
176
|
-
message: Partial<
|
|
154
|
+
message: Partial<MessageItem> & Pick<MessageItem, 'clientMsgID'>
|
|
177
155
|
) => void;
|
|
178
156
|
deleteOneMessage: (clientMsgID: string) => void;
|
|
179
157
|
reset: () => void;
|
|
@@ -181,12 +159,13 @@ export interface MessageStore {
|
|
|
181
159
|
|
|
182
160
|
export interface ChatDetailContextType {
|
|
183
161
|
conversationFlow?: IConversationFlow;
|
|
184
|
-
refetchConversationFlow: () => void;
|
|
185
162
|
isBotConversation: boolean;
|
|
186
163
|
setConverationFlowStep: (step?: IConversationFlowStep) => void;
|
|
187
164
|
flowState?: DFlowState;
|
|
188
165
|
runButtonContext: (stringContext: DFlowContext) => Promise<boolean>;
|
|
189
166
|
conversation?: ConversationItem;
|
|
167
|
+
quotedMessage?: MessageItem;
|
|
168
|
+
setQuotedMessage: (item?: MessageItem) => void;
|
|
190
169
|
}
|
|
191
170
|
|
|
192
171
|
export interface ChatDetailProviderProps extends PropsWithChildren {}
|
package/src/utils/UIUtils.ts
CHANGED
|
@@ -10,6 +10,7 @@ const defaultAdapter: Required<ChatUIAdapter> = {
|
|
|
10
10
|
toast: { open: openWithAlert },
|
|
11
11
|
alert: { open: openWithAlert },
|
|
12
12
|
popup: { open: openWithAlert },
|
|
13
|
+
bottomSheet: { open: openWithAlert },
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
let _adapter: ChatUIAdapter = {};
|
|
@@ -28,4 +29,8 @@ export const UIUtils: Required<ChatUIAdapter> = {
|
|
|
28
29
|
popup: {
|
|
29
30
|
open: (params) => (_adapter.popup ?? defaultAdapter.popup).open(params),
|
|
30
31
|
},
|
|
32
|
+
bottomSheet: {
|
|
33
|
+
open: (params) =>
|
|
34
|
+
(_adapter.bottomSheet ?? defaultAdapter.bottomSheet).open(params),
|
|
35
|
+
},
|
|
31
36
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
2
|
-
import type { DMessageItem } from '../types/chat';
|
|
1
|
+
import { MessageType, type MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Extract message text based on content type
|
|
6
5
|
*/
|
|
7
|
-
export const getMessageText = (message:
|
|
6
|
+
export const getMessageText = (message: MessageItem): string => {
|
|
8
7
|
switch (message.contentType) {
|
|
9
8
|
case MessageType.TextMessage:
|
|
10
9
|
return message.textElem?.content ?? message.content;
|
|
@@ -49,7 +48,7 @@ export interface PrecomputedMessageData {
|
|
|
49
48
|
* Returns: Map of messageId -> {dayStart, messageType, createdAt}
|
|
50
49
|
*/
|
|
51
50
|
export const precomputeLegendListData = (
|
|
52
|
-
messages:
|
|
51
|
+
messages: MessageItem[]
|
|
53
52
|
): Map<string, PrecomputedMessageData> => {
|
|
54
53
|
const dataMap = new Map<string, PrecomputedMessageData>();
|
|
55
54
|
let lastDay: number | null = null;
|
|
@@ -81,7 +80,7 @@ export const precomputeLegendListData = (
|
|
|
81
80
|
* Kept for backward compatibility
|
|
82
81
|
*/
|
|
83
82
|
export const computeDayStarts = (
|
|
84
|
-
messages:
|
|
83
|
+
messages: MessageItem[]
|
|
85
84
|
): Map<string, boolean> => {
|
|
86
85
|
const dayStartMap = new Map<string, boolean>();
|
|
87
86
|
let lastDay: number | null = null;
|
package/src/utils/message.ts
CHANGED
|
@@ -4,20 +4,19 @@ import {
|
|
|
4
4
|
type ConversationItem,
|
|
5
5
|
type MessageItem,
|
|
6
6
|
} from '@droppii/openim-rn-client-sdk';
|
|
7
|
-
import { type DMessageItem } from '../types/chat';
|
|
8
7
|
import { getConversationID } from './conversation';
|
|
9
8
|
|
|
10
|
-
const getMessageTime = (message:
|
|
9
|
+
const getMessageTime = (message: MessageItem) =>
|
|
11
10
|
message.sendTime || message.createTime || 0;
|
|
12
11
|
|
|
13
|
-
const getMessageKey = (message:
|
|
12
|
+
const getMessageKey = (message: MessageItem) =>
|
|
14
13
|
message.clientMsgID || message.serverMsgID;
|
|
15
14
|
|
|
16
15
|
export const mergeMessages = (
|
|
17
|
-
current:
|
|
18
|
-
incoming:
|
|
19
|
-
):
|
|
20
|
-
const map = new Map<string,
|
|
16
|
+
current: MessageItem[],
|
|
17
|
+
incoming: MessageItem[]
|
|
18
|
+
): MessageItem[] => {
|
|
19
|
+
const map = new Map<string, MessageItem>();
|
|
21
20
|
|
|
22
21
|
for (const message of [...current, ...incoming]) {
|
|
23
22
|
const key = getMessageKey(message);
|
|
@@ -54,7 +53,7 @@ export type HistoryPaginationAnchor = {
|
|
|
54
53
|
|
|
55
54
|
export const resolveHistoryLastMinSeq = (
|
|
56
55
|
history: { lastMinSeq?: number },
|
|
57
|
-
fallbackMessage?:
|
|
56
|
+
fallbackMessage?: MessageItem
|
|
58
57
|
): number => {
|
|
59
58
|
if (history.lastMinSeq != null) {
|
|
60
59
|
return history.lastMinSeq;
|
|
@@ -64,7 +63,7 @@ export const resolveHistoryLastMinSeq = (
|
|
|
64
63
|
};
|
|
65
64
|
|
|
66
65
|
export const getHistoryPaginationAnchor = (
|
|
67
|
-
messages:
|
|
66
|
+
messages: MessageItem[]
|
|
68
67
|
): HistoryPaginationAnchor | null => {
|
|
69
68
|
if (!messages.length) {
|
|
70
69
|
return null;
|
|
@@ -86,8 +85,8 @@ export const getHistoryPaginationAnchor = (
|
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
export const hasNewHistoryMessages = (
|
|
89
|
-
current:
|
|
90
|
-
incoming:
|
|
88
|
+
current: MessageItem[],
|
|
89
|
+
incoming: MessageItem[]
|
|
91
90
|
): boolean => {
|
|
92
91
|
const currentKeys = new Set(
|
|
93
92
|
current.map((message) => getMessageKey(message)).filter(Boolean)
|