@droppii-org/chat-mobile 0.2.3 → 0.2.6
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/ThreadCard/NamePrefixIcon.js +2 -3
- package/lib/module/components/ThreadCard/NamePrefixIcon.js.map +1 -1
- package/lib/module/config/feature-flags.js +38 -0
- package/lib/module/config/feature-flags.js.map +1 -0
- package/lib/module/hooks/query-keys.js +4 -0
- package/lib/module/hooks/query-keys.js.map +1 -1
- package/lib/module/hooks/useChatMessages.js +45 -0
- package/lib/module/hooks/useChatMessages.js.map +1 -1
- package/lib/module/hooks/useLinkPreview/useFetchUrlMetadata.js +17 -0
- package/lib/module/hooks/useLinkPreview/useFetchUrlMetadata.js.map +1 -0
- package/lib/module/hooks/useLinkPreview/useLinkPreview.js +34 -0
- package/lib/module/hooks/useLinkPreview/useLinkPreview.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +24 -7
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetail.js +119 -19
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetailHeader.js +43 -20
- package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatLinkPreview.js +79 -0
- package/lib/module/screens/chat-detail/ChatLinkPreview.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatList.js +2 -0
- package/lib/module/screens/chat-detail/ChatList.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js +352 -0
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatQuickActions.js +12 -2
- package/lib/module/screens/chat-detail/ChatQuickActions.js.map +1 -1
- package/lib/module/screens/chat-detail/conversationHeader.utils.js +31 -0
- package/lib/module/screens/chat-detail/conversationHeader.utils.js.map +1 -0
- package/lib/module/screens/chat-detail/index.js +1 -0
- package/lib/module/screens/chat-detail/index.js.map +1 -1
- package/lib/module/screens/chat-detail/legend/LegendChatDay.js +57 -0
- package/lib/module/screens/chat-detail/legend/LegendChatDay.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatLoadEarlier.js +21 -0
- package/lib/module/screens/chat-detail/legend/LegendChatLoadEarlier.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +47 -0
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatScrollToBottom.js +58 -0
- package/lib/module/screens/chat-detail/legend/LegendChatScrollToBottom.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/message-types.js +122 -0
- package/lib/module/screens/chat-detail/legend/message-types.js.map +1 -0
- package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js.map +1 -1
- package/lib/module/services/apis.js +1 -1
- package/lib/module/services/apis.js.map +1 -1
- package/lib/module/services/endpoints.js +8 -0
- package/lib/module/services/endpoints.js.map +1 -0
- package/lib/module/types/common.js +2 -0
- package/lib/module/types/common.js.map +1 -0
- package/lib/module/utils/legendListMessage.js +80 -0
- package/lib/module/utils/legendListMessage.js.map +1 -0
- package/lib/module/utils/url.js +7 -0
- package/lib/module/utils/url.js.map +1 -0
- package/lib/typescript/src/components/Avatar/Avatar.d.ts +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/AvatarBadge.d.ts +1 -1
- package/lib/typescript/src/components/Avatar/AvatarBadge.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts +1 -1
- package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts +1 -1
- package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +1 -1
- package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts +1 -1
- package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts +1 -1
- package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/UnreadBadge.d.ts +1 -1
- package/lib/typescript/src/components/ThreadCard/UnreadBadge.d.ts.map +1 -1
- package/lib/typescript/src/config/feature-flags.d.ts +12 -0
- package/lib/typescript/src/config/feature-flags.d.ts.map +1 -0
- package/lib/typescript/src/context/ChatContext.d.ts +1 -1
- package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
- package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
- package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useChatMessages.d.ts +3 -0
- package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useLinkPreview/useFetchUrlMetadata.d.ts +3 -0
- package/lib/typescript/src/hooks/useLinkPreview/useFetchUrlMetadata.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts +7 -0
- package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts.map +1 -0
- 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/ChatAttachmentPanel.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatLinkPreview.d.ts +9 -0
- package/lib/typescript/src/screens/chat-detail/ChatLinkPreview.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatList.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatList.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +3 -0
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatScrollToBottom.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatScrollToBottom.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts +6 -0
- package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/index.d.ts +2 -1
- package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts +6 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatLoadEarlier.d.ts +6 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatLoadEarlier.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +15 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatScrollToBottom.d.ts +6 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatScrollToBottom.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts +12 -0
- package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts +34 -5
- package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/Inbox.d.ts +1 -1
- package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/MessagesTab.d.ts +1 -1
- package/lib/typescript/src/screens/inbox/MessagesTab.d.ts.map +1 -1
- package/lib/typescript/src/services/apis.d.ts +1 -0
- package/lib/typescript/src/services/apis.d.ts.map +1 -1
- package/lib/typescript/src/services/endpoints.d.ts +6 -0
- package/lib/typescript/src/services/endpoints.d.ts.map +1 -0
- package/lib/typescript/src/types/common.d.ts +6 -0
- package/lib/typescript/src/types/common.d.ts.map +1 -0
- package/lib/typescript/src/utils/legendListMessage.d.ts +25 -0
- package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -0
- package/lib/typescript/src/utils/url.d.ts +2 -0
- package/lib/typescript/src/utils/url.d.ts.map +1 -0
- package/package.json +4 -2
- package/src/components/ThreadCard/NamePrefixIcon.tsx +2 -3
- package/src/config/feature-flags.ts +49 -0
- package/src/hooks/query-keys.ts +5 -0
- package/src/hooks/useChatMessages.ts +60 -0
- package/src/hooks/useLinkPreview/useFetchUrlMetadata.ts +18 -0
- package/src/hooks/useLinkPreview/useLinkPreview.ts +30 -0
- package/src/index.tsx +1 -0
- package/src/screens/chat-detail/ChatComposer.tsx +30 -9
- package/src/screens/chat-detail/ChatDetail.tsx +163 -27
- package/src/screens/chat-detail/ChatDetailHeader.tsx +58 -26
- package/src/screens/chat-detail/ChatLinkPreview.tsx +86 -0
- package/src/screens/chat-detail/ChatList.tsx +3 -0
- package/src/screens/chat-detail/ChatListLegend.tsx +404 -0
- package/src/screens/chat-detail/ChatQuickActions.tsx +19 -2
- package/src/screens/chat-detail/conversationHeader.utils.ts +52 -0
- package/src/screens/chat-detail/index.ts +7 -0
- package/src/screens/chat-detail/legend/LegendChatDay.tsx +70 -0
- package/src/screens/chat-detail/legend/LegendChatLoadEarlier.tsx +21 -0
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +66 -0
- package/src/screens/chat-detail/legend/LegendChatScrollToBottom.tsx +56 -0
- package/src/screens/chat-detail/legend/message-types.tsx +149 -0
- package/src/screens/chat-detail/messages/ChatMessageBubble.tsx +0 -1
- package/src/screens/chat-detail/types.ts +47 -5
- package/src/services/apis.ts +1 -1
- package/src/services/endpoints.ts +5 -0
- package/src/types/common.ts +5 -0
- package/src/utils/legendListMessage.ts +102 -0
- package/src/utils/url.ts +5 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { ActivityIndicator } from 'react-native';
|
|
3
|
+
import { KContainer, KColors } from '@droppii/libs';
|
|
4
|
+
|
|
5
|
+
interface LegendChatLoadEarlierProps {
|
|
6
|
+
isLoading?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const LegendChatLoadEarlier = memo(
|
|
10
|
+
({ isLoading = false }: LegendChatLoadEarlierProps) => {
|
|
11
|
+
if (!isLoading) return null;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<KContainer.View center marginV="0.75rem">
|
|
15
|
+
<ActivityIndicator color={KColors.palette.primary.w400} size="small" />
|
|
16
|
+
</KContainer.View>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
LegendChatLoadEarlier.displayName = 'LegendChatLoadEarlier';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { memo, ComponentType } from 'react';
|
|
2
|
+
import type { DMessageItem } from '../../../types/chat';
|
|
3
|
+
import type { DChatMessageType } from '../../../types/message';
|
|
4
|
+
import {
|
|
5
|
+
LegendTextMessage,
|
|
6
|
+
LegendImageMessage,
|
|
7
|
+
LegendVideoMessage,
|
|
8
|
+
LegendFileMessage,
|
|
9
|
+
} from './message-types';
|
|
10
|
+
import { DChatMessageType as MessageTypeEnum } from '../../../types/message';
|
|
11
|
+
|
|
12
|
+
interface LegendChatMessageProps {
|
|
13
|
+
message: DMessageItem;
|
|
14
|
+
messageType: DChatMessageType;
|
|
15
|
+
isOutgoing: boolean;
|
|
16
|
+
createdAtTime: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Message type to component mapping
|
|
20
|
+
const messageComponentMap: Partial<
|
|
21
|
+
Record<DChatMessageType, ComponentType<any>>
|
|
22
|
+
> = {
|
|
23
|
+
[MessageTypeEnum.Text]: LegendTextMessage,
|
|
24
|
+
[MessageTypeEnum.Image]: LegendImageMessage,
|
|
25
|
+
[MessageTypeEnum.Video]: LegendVideoMessage,
|
|
26
|
+
[MessageTypeEnum.File]: LegendFileMessage,
|
|
27
|
+
[MessageTypeEnum.Link]: LegendTextMessage, // Link renders as text for now
|
|
28
|
+
[MessageTypeEnum.Order]: LegendTextMessage, // Order renders as text for now
|
|
29
|
+
[MessageTypeEnum.Unsupported]: LegendTextMessage, // Unsupported renders as text for now
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Dispatcher component - renders the appropriate message component
|
|
34
|
+
* based on message type. Extensible for new message types.
|
|
35
|
+
*/
|
|
36
|
+
export const LegendChatMessage = memo(
|
|
37
|
+
({
|
|
38
|
+
message,
|
|
39
|
+
messageType,
|
|
40
|
+
isOutgoing,
|
|
41
|
+
createdAtTime,
|
|
42
|
+
}: LegendChatMessageProps) => {
|
|
43
|
+
const MessageComponent = messageComponentMap[messageType];
|
|
44
|
+
|
|
45
|
+
// Fallback to text message if type not found
|
|
46
|
+
if (!MessageComponent) {
|
|
47
|
+
return (
|
|
48
|
+
<LegendTextMessage
|
|
49
|
+
message={message}
|
|
50
|
+
isOutgoing={isOutgoing}
|
|
51
|
+
createdAtTime={createdAtTime}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<MessageComponent
|
|
58
|
+
message={message}
|
|
59
|
+
isOutgoing={isOutgoing}
|
|
60
|
+
createdAtTime={createdAtTime}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
LegendChatMessage.displayName = 'LegendChatMessage';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { memo, useCallback } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { KContainer, KColors, KLabel } from '@droppii/libs';
|
|
4
|
+
|
|
5
|
+
interface LegendChatScrollToBottomProps {
|
|
6
|
+
listRef: React.RefObject<any>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const LegendChatScrollToBottom = memo(
|
|
10
|
+
({ listRef }: LegendChatScrollToBottomProps) => {
|
|
11
|
+
const handlePress = useCallback(() => {
|
|
12
|
+
listRef.current?.scrollToEnd({ animated: true });
|
|
13
|
+
}, [listRef]);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<KContainer.Touchable
|
|
17
|
+
onPress={handlePress}
|
|
18
|
+
style={styles.scrollToBottom}
|
|
19
|
+
activeOpacity={0.7}
|
|
20
|
+
>
|
|
21
|
+
<KContainer.View style={styles.scrollToBottomContent}>
|
|
22
|
+
<KLabel.Text style={styles.arrowDown}>↓</KLabel.Text>
|
|
23
|
+
</KContainer.View>
|
|
24
|
+
</KContainer.Touchable>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
LegendChatScrollToBottom.displayName = 'LegendChatScrollToBottom';
|
|
30
|
+
|
|
31
|
+
const styles = StyleSheet.create({
|
|
32
|
+
scrollToBottom: {
|
|
33
|
+
position: 'absolute',
|
|
34
|
+
bottom: 20,
|
|
35
|
+
right: 16,
|
|
36
|
+
zIndex: 999,
|
|
37
|
+
},
|
|
38
|
+
scrollToBottomContent: {
|
|
39
|
+
backgroundColor: KColors.white,
|
|
40
|
+
width: 32,
|
|
41
|
+
height: 32,
|
|
42
|
+
borderRadius: 16,
|
|
43
|
+
shadowColor: KColors.black,
|
|
44
|
+
shadowOffset: { width: 0, height: 1 },
|
|
45
|
+
shadowOpacity: 0.16,
|
|
46
|
+
shadowRadius: 8,
|
|
47
|
+
elevation: 8,
|
|
48
|
+
justifyContent: 'center',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
},
|
|
51
|
+
arrowDown: {
|
|
52
|
+
fontSize: 16,
|
|
53
|
+
fontWeight: 'bold',
|
|
54
|
+
color: KColors.palette.primary.w400,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { KContainer, KLabel, KSpacingValue } from '@droppii/libs';
|
|
4
|
+
import type { DMessageItem } from '../../../types/chat';
|
|
5
|
+
import { getMessageText } from '../../../utils/legendListMessage';
|
|
6
|
+
import { CHAT_BUBBLE_COLORS } from '../constants';
|
|
7
|
+
|
|
8
|
+
interface BaseLegendMessageProps {
|
|
9
|
+
message: DMessageItem;
|
|
10
|
+
isOutgoing: boolean;
|
|
11
|
+
createdAtTime: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const formatMessageTime = (createdAt: number) => {
|
|
15
|
+
const date = new Date(createdAt);
|
|
16
|
+
if (Number.isNaN(date.getTime())) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return date.toLocaleTimeString('vi-VN', {
|
|
21
|
+
hour: '2-digit',
|
|
22
|
+
minute: '2-digit',
|
|
23
|
+
hour12: false,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Text Message Component
|
|
28
|
+
export const LegendTextMessage = memo(
|
|
29
|
+
({ message, isOutgoing, createdAtTime }: BaseLegendMessageProps) => {
|
|
30
|
+
const messageText = getMessageText(message);
|
|
31
|
+
const timeLabel = formatMessageTime(createdAtTime);
|
|
32
|
+
|
|
33
|
+
if (!messageText?.trim()) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<KContainer.View style={styles.wrapper}>
|
|
39
|
+
<KContainer.View
|
|
40
|
+
style={[
|
|
41
|
+
styles.bubble,
|
|
42
|
+
isOutgoing ? styles.bubbleSent : styles.bubbleReceived,
|
|
43
|
+
]}
|
|
44
|
+
>
|
|
45
|
+
<KLabel.Text typo="TextMdNormal" color={CHAT_BUBBLE_COLORS.text}>
|
|
46
|
+
{messageText}
|
|
47
|
+
</KLabel.Text>
|
|
48
|
+
</KContainer.View>
|
|
49
|
+
|
|
50
|
+
{!isOutgoing && timeLabel ? (
|
|
51
|
+
<KLabel.Text
|
|
52
|
+
typo="TextXsNormal"
|
|
53
|
+
color={CHAT_BUBBLE_COLORS.timestamp}
|
|
54
|
+
marginL={'0.25rem'}
|
|
55
|
+
>
|
|
56
|
+
{timeLabel}
|
|
57
|
+
</KLabel.Text>
|
|
58
|
+
) : null}
|
|
59
|
+
</KContainer.View>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
LegendTextMessage.displayName = 'LegendTextMessage';
|
|
65
|
+
|
|
66
|
+
// Image Message Component (placeholder)
|
|
67
|
+
export const LegendImageMessage = memo(
|
|
68
|
+
({ isOutgoing }: BaseLegendMessageProps) => {
|
|
69
|
+
return (
|
|
70
|
+
<KContainer.View style={styles.wrapper}>
|
|
71
|
+
<KContainer.View
|
|
72
|
+
style={[
|
|
73
|
+
styles.bubble,
|
|
74
|
+
isOutgoing ? styles.bubbleSent : styles.bubbleReceived,
|
|
75
|
+
]}
|
|
76
|
+
>
|
|
77
|
+
<KLabel.Text typo="TextSmNormal" color={CHAT_BUBBLE_COLORS.text}>
|
|
78
|
+
[Image]
|
|
79
|
+
</KLabel.Text>
|
|
80
|
+
</KContainer.View>
|
|
81
|
+
</KContainer.View>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
LegendImageMessage.displayName = 'LegendImageMessage';
|
|
87
|
+
|
|
88
|
+
// Video Message Component (placeholder)
|
|
89
|
+
export const LegendVideoMessage = memo(
|
|
90
|
+
({ isOutgoing }: BaseLegendMessageProps) => {
|
|
91
|
+
return (
|
|
92
|
+
<KContainer.View style={styles.wrapper}>
|
|
93
|
+
<KContainer.View
|
|
94
|
+
style={[
|
|
95
|
+
styles.bubble,
|
|
96
|
+
isOutgoing ? styles.bubbleSent : styles.bubbleReceived,
|
|
97
|
+
]}
|
|
98
|
+
>
|
|
99
|
+
<KLabel.Text typo="TextSmNormal" color={CHAT_BUBBLE_COLORS.text}>
|
|
100
|
+
[Video]
|
|
101
|
+
</KLabel.Text>
|
|
102
|
+
</KContainer.View>
|
|
103
|
+
</KContainer.View>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
LegendVideoMessage.displayName = 'LegendVideoMessage';
|
|
109
|
+
|
|
110
|
+
// File Message Component
|
|
111
|
+
export const LegendFileMessage = memo(
|
|
112
|
+
({ message, isOutgoing }: BaseLegendMessageProps) => {
|
|
113
|
+
const fileName = message.fileElem?.fileName || message.content || '[File]';
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<KContainer.View style={styles.wrapper}>
|
|
117
|
+
<KContainer.View
|
|
118
|
+
style={[
|
|
119
|
+
styles.bubble,
|
|
120
|
+
isOutgoing ? styles.bubbleSent : styles.bubbleReceived,
|
|
121
|
+
]}
|
|
122
|
+
>
|
|
123
|
+
<KLabel.Text typo="TextSmNormal" color={CHAT_BUBBLE_COLORS.text}>
|
|
124
|
+
📎 {fileName}
|
|
125
|
+
</KLabel.Text>
|
|
126
|
+
</KContainer.View>
|
|
127
|
+
</KContainer.View>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
LegendFileMessage.displayName = 'LegendFileMessage';
|
|
133
|
+
|
|
134
|
+
const styles = StyleSheet.create({
|
|
135
|
+
wrapper: {
|
|
136
|
+
maxWidth: '80%',
|
|
137
|
+
},
|
|
138
|
+
bubble: {
|
|
139
|
+
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
140
|
+
paddingVertical: KSpacingValue['0.5rem'],
|
|
141
|
+
borderRadius: KSpacingValue['1.25rem'],
|
|
142
|
+
},
|
|
143
|
+
bubbleReceived: {
|
|
144
|
+
backgroundColor: CHAT_BUBBLE_COLORS.received,
|
|
145
|
+
},
|
|
146
|
+
bubbleSent: {
|
|
147
|
+
backgroundColor: CHAT_BUBBLE_COLORS.sent,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { DChatType } from '../../types/chat';
|
|
3
|
-
import type { DMessageItem } from '../../types/chat';
|
|
2
|
+
import type { DChatType, DChatCategory } from '../../types/chat';
|
|
3
|
+
import type { DConversationItem, DMessageItem } from '../../types/chat';
|
|
4
4
|
|
|
5
5
|
export type DChatActionIconProvider = 'MaterialCommunityIcons' | 'DroppiiNew';
|
|
6
6
|
|
|
@@ -23,6 +23,8 @@ export interface ChatDetailHeaderProps {
|
|
|
23
23
|
avatarUri?: string | null;
|
|
24
24
|
avatarFullName?: string;
|
|
25
25
|
chatType?: DChatType;
|
|
26
|
+
chatCategory?: DChatCategory;
|
|
27
|
+
applicationType?: DConversationItem['applicationType'];
|
|
26
28
|
showAddMember?: boolean;
|
|
27
29
|
onBack?: () => void;
|
|
28
30
|
onPressSearch?: () => void;
|
|
@@ -31,10 +33,17 @@ export interface ChatDetailHeaderProps {
|
|
|
31
33
|
onPressAvatar?: () => void;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
export interface ChatQuickActionsRenderParams {
|
|
37
|
+
actions: DChatQuickAction[];
|
|
38
|
+
onActionPress: (action: DChatQuickAction) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
export interface ChatQuickActionsProps {
|
|
42
|
+
visible?: boolean;
|
|
35
43
|
actions?: DChatQuickAction[];
|
|
36
44
|
onActionPress?: (action: DChatQuickAction) => void;
|
|
37
45
|
renderAction?: (action: DChatQuickAction, onPress: () => void) => ReactNode;
|
|
46
|
+
renderQuickActions?: (params: ChatQuickActionsRenderParams) => ReactNode;
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
export interface ChatAttachmentPanelProps {
|
|
@@ -54,11 +63,13 @@ export interface ChatComposerProps {
|
|
|
54
63
|
onSend?: () => void;
|
|
55
64
|
onPressAttach?: () => void;
|
|
56
65
|
onPressEmoji?: () => void;
|
|
66
|
+
showQuickActions?: boolean;
|
|
57
67
|
quickActions?: DChatQuickAction[];
|
|
58
68
|
attachmentActions?: DChatAttachmentAction[];
|
|
59
69
|
onQuickActionPress?: (action: DChatQuickAction) => void;
|
|
60
70
|
onAttachmentAction?: (action: DChatAttachmentAction) => void;
|
|
61
71
|
renderQuickAction?: ChatQuickActionsProps['renderAction'];
|
|
72
|
+
renderQuickActions?: ChatQuickActionsProps['renderQuickActions'];
|
|
62
73
|
renderAttachmentAction?: ChatAttachmentPanelProps['renderAction'];
|
|
63
74
|
attachmentColumns?: number;
|
|
64
75
|
}
|
|
@@ -71,17 +82,47 @@ export interface ChatListProps {
|
|
|
71
82
|
isLoading?: boolean;
|
|
72
83
|
isLoadingEarlier?: boolean;
|
|
73
84
|
hasMoreEarlier?: boolean;
|
|
85
|
+
onLoadNewer?: () => void;
|
|
86
|
+
isLoadingNewer?: boolean;
|
|
87
|
+
hasMoreNewer?: boolean;
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
export interface ChatDetailProps extends
|
|
77
|
-
|
|
78
|
-
|
|
90
|
+
export interface ChatDetailProps extends Omit<
|
|
91
|
+
ChatDetailHeaderProps,
|
|
92
|
+
| 'title'
|
|
93
|
+
| 'subtitle'
|
|
94
|
+
| 'avatarUri'
|
|
95
|
+
| 'avatarFullName'
|
|
96
|
+
| 'chatType'
|
|
97
|
+
| 'chatCategory'
|
|
98
|
+
| 'applicationType'
|
|
99
|
+
| 'showAddMember'
|
|
100
|
+
> {
|
|
101
|
+
conversationId: string;
|
|
102
|
+
enabled?: boolean;
|
|
103
|
+
title?: string;
|
|
104
|
+
subtitle?: string;
|
|
105
|
+
avatarUri?: string | null;
|
|
106
|
+
avatarFullName?: string;
|
|
107
|
+
chatType?: DChatType;
|
|
108
|
+
chatCategory?: DChatCategory;
|
|
109
|
+
applicationType?: DConversationItem['applicationType'];
|
|
110
|
+
showAddMember?: boolean;
|
|
111
|
+
getSubtitle?: (conversation: DConversationItem) => string | undefined;
|
|
79
112
|
renderChat?: (item: DMessageItem) => ReactNode;
|
|
113
|
+
showQuickActions?: boolean;
|
|
114
|
+
showAttachmentActions?: boolean;
|
|
80
115
|
onLoadEarlier?: () => void;
|
|
81
116
|
isLoading?: boolean;
|
|
82
117
|
isLoadingEarlier?: boolean;
|
|
83
118
|
hasMoreEarlier?: boolean;
|
|
119
|
+
onLoadNewer?: () => void;
|
|
120
|
+
isLoadingNewer?: boolean;
|
|
121
|
+
hasMoreNewer?: boolean;
|
|
84
122
|
quickActions?: DChatQuickAction[];
|
|
123
|
+
getQuickActions?: (
|
|
124
|
+
conversation: DConversationItem
|
|
125
|
+
) => DChatQuickAction[] | undefined;
|
|
85
126
|
attachmentActions?: DChatAttachmentAction[];
|
|
86
127
|
inputValue?: string;
|
|
87
128
|
inputPlaceholder?: string;
|
|
@@ -92,6 +133,7 @@ export interface ChatDetailProps extends ChatDetailHeaderProps {
|
|
|
92
133
|
onQuickActionPress?: (action: DChatQuickAction) => void;
|
|
93
134
|
onAttachmentAction?: (action: DChatAttachmentAction) => void;
|
|
94
135
|
renderQuickAction?: ChatQuickActionsProps['renderAction'];
|
|
136
|
+
renderQuickActions?: ChatQuickActionsProps['renderQuickActions'];
|
|
95
137
|
renderAttachmentAction?: ChatAttachmentPanelProps['renderAction'];
|
|
96
138
|
attachmentColumns?: number;
|
|
97
139
|
}
|
package/src/services/apis.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { BaseResponse, GetOpenIMTokenResponse } from '../types/auth';
|
|
|
8
8
|
import { Platform } from 'react-native';
|
|
9
9
|
import { useConversationStore } from '../store';
|
|
10
10
|
|
|
11
|
-
let apiInstance: AxiosInstance | null = null;
|
|
11
|
+
export let apiInstance: AxiosInstance | null = null;
|
|
12
12
|
|
|
13
13
|
export namespace ChatAPI {
|
|
14
14
|
export function initApiInstance(api: AxiosInstance): void {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
2
|
+
import type { DMessageItem } from '../types/chat';
|
|
3
|
+
import { resolveChatMessageType } from './resolveMessageType';
|
|
4
|
+
import type { DChatMessageType } from '../types/message';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extract message text based on content type
|
|
8
|
+
*/
|
|
9
|
+
export const getMessageText = (message: DMessageItem): string => {
|
|
10
|
+
switch (message.contentType) {
|
|
11
|
+
case MessageType.TextMessage:
|
|
12
|
+
return message.textElem?.content ?? message.content;
|
|
13
|
+
case MessageType.AtTextMessage:
|
|
14
|
+
return message.atTextElem?.text ?? message.content;
|
|
15
|
+
case MessageType.QuoteMessage:
|
|
16
|
+
return message.quoteElem?.text ?? message.content;
|
|
17
|
+
case MessageType.PictureMessage:
|
|
18
|
+
return '';
|
|
19
|
+
case MessageType.VoiceMessage:
|
|
20
|
+
return message.soundElem?.sourceUrl ? '' : '[Tin nhắn thoại]';
|
|
21
|
+
case MessageType.VideoMessage:
|
|
22
|
+
return message.videoElem?.videoUrl ? '' : '[Video]';
|
|
23
|
+
case MessageType.FileMessage:
|
|
24
|
+
return message.fileElem?.fileName ?? message.content;
|
|
25
|
+
case MessageType.CardMessage:
|
|
26
|
+
return message.cardElem?.nickname ?? message.content;
|
|
27
|
+
case MessageType.LocationMessage:
|
|
28
|
+
return message.locationElem?.description ?? message.content;
|
|
29
|
+
case MessageType.CustomMessage:
|
|
30
|
+
return message.customElem?.description ?? message.content;
|
|
31
|
+
case MessageType.FaceMessage:
|
|
32
|
+
return '[Sticker]';
|
|
33
|
+
default:
|
|
34
|
+
if (message.notificationElem?.detail) {
|
|
35
|
+
return message.notificationElem.detail;
|
|
36
|
+
}
|
|
37
|
+
return message.content || '';
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export interface PrecomputedMessageData {
|
|
42
|
+
messageId: string;
|
|
43
|
+
dayStart: boolean;
|
|
44
|
+
messageType: DChatMessageType;
|
|
45
|
+
createdAt: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Precompute all message rendering data in single pass O(n)
|
|
50
|
+
* Eliminates redundant Date creation and type resolution during render
|
|
51
|
+
*
|
|
52
|
+
* Returns: Map of messageId -> {dayStart, messageType, createdAt}
|
|
53
|
+
*/
|
|
54
|
+
export const precomputeLegendListData = (
|
|
55
|
+
messages: DMessageItem[]
|
|
56
|
+
): Map<string, PrecomputedMessageData> => {
|
|
57
|
+
const dataMap = new Map<string, PrecomputedMessageData>();
|
|
58
|
+
let lastDay: number | null = null;
|
|
59
|
+
|
|
60
|
+
messages.forEach((message, index) => {
|
|
61
|
+
// Get message ID - use timestamp as fallback for newly sent messages
|
|
62
|
+
const messageId =
|
|
63
|
+
message.clientMsgID ||
|
|
64
|
+
message.serverMsgID ||
|
|
65
|
+
`temp-${message.sendTime || message.createTime}-${index}`;
|
|
66
|
+
|
|
67
|
+
const createdAt = message.sendTime || message.createTime;
|
|
68
|
+
const day = new Date(createdAt).getDate();
|
|
69
|
+
const messageType = resolveChatMessageType(message);
|
|
70
|
+
|
|
71
|
+
dataMap.set(messageId, {
|
|
72
|
+
messageId,
|
|
73
|
+
dayStart: lastDay !== day,
|
|
74
|
+
messageType,
|
|
75
|
+
createdAt,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
lastDay = day;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return dataMap;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Legacy: Use precomputeLegendListData instead
|
|
86
|
+
* Kept for backward compatibility
|
|
87
|
+
*/
|
|
88
|
+
export const computeDayStarts = (
|
|
89
|
+
messages: DMessageItem[]
|
|
90
|
+
): Map<string, boolean> => {
|
|
91
|
+
const dayStartMap = new Map<string, boolean>();
|
|
92
|
+
let lastDay: number | null = null;
|
|
93
|
+
|
|
94
|
+
messages.forEach((message) => {
|
|
95
|
+
const day = new Date(message.sendTime || message.createTime).getDate();
|
|
96
|
+
const messageId = message.clientMsgID || message.serverMsgID || '';
|
|
97
|
+
dayStartMap.set(messageId, lastDay !== day);
|
|
98
|
+
lastDay = day;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return dayStartMap;
|
|
102
|
+
};
|