@droppii-org/chat-mobile 0.2.4 → 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.
Files changed (161) hide show
  1. package/lib/module/config/feature-flags.js +38 -0
  2. package/lib/module/config/feature-flags.js.map +1 -0
  3. package/lib/module/hooks/query-keys.js +4 -0
  4. package/lib/module/hooks/query-keys.js.map +1 -1
  5. package/lib/module/hooks/useChatMessages.js +45 -0
  6. package/lib/module/hooks/useChatMessages.js.map +1 -1
  7. package/lib/module/hooks/useLinkPreview/useFetchUrlMetadata.js +17 -0
  8. package/lib/module/hooks/useLinkPreview/useFetchUrlMetadata.js.map +1 -0
  9. package/lib/module/hooks/useLinkPreview/useLinkPreview.js +34 -0
  10. package/lib/module/hooks/useLinkPreview/useLinkPreview.js.map +1 -0
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/module/screens/chat-detail/ChatComposer.js +18 -2
  13. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  14. package/lib/module/screens/chat-detail/ChatDetail.js +110 -20
  15. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  16. package/lib/module/screens/chat-detail/ChatLinkPreview.js +79 -0
  17. package/lib/module/screens/chat-detail/ChatLinkPreview.js.map +1 -0
  18. package/lib/module/screens/chat-detail/ChatList.js +2 -0
  19. package/lib/module/screens/chat-detail/ChatList.js.map +1 -1
  20. package/lib/module/screens/chat-detail/ChatListLegend.js +352 -0
  21. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -0
  22. package/lib/module/screens/chat-detail/ChatQuickActions.js +12 -2
  23. package/lib/module/screens/chat-detail/ChatQuickActions.js.map +1 -1
  24. package/lib/module/screens/chat-detail/conversationHeader.utils.js +31 -0
  25. package/lib/module/screens/chat-detail/conversationHeader.utils.js.map +1 -0
  26. package/lib/module/screens/chat-detail/index.js +1 -0
  27. package/lib/module/screens/chat-detail/index.js.map +1 -1
  28. package/lib/module/screens/chat-detail/legend/LegendChatDay.js +57 -0
  29. package/lib/module/screens/chat-detail/legend/LegendChatDay.js.map +1 -0
  30. package/lib/module/screens/chat-detail/legend/LegendChatLoadEarlier.js +21 -0
  31. package/lib/module/screens/chat-detail/legend/LegendChatLoadEarlier.js.map +1 -0
  32. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +47 -0
  33. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -0
  34. package/lib/module/screens/chat-detail/legend/LegendChatScrollToBottom.js +58 -0
  35. package/lib/module/screens/chat-detail/legend/LegendChatScrollToBottom.js.map +1 -0
  36. package/lib/module/screens/chat-detail/legend/message-types.js +122 -0
  37. package/lib/module/screens/chat-detail/legend/message-types.js.map +1 -0
  38. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js.map +1 -1
  39. package/lib/module/services/apis.js +1 -1
  40. package/lib/module/services/apis.js.map +1 -1
  41. package/lib/module/services/endpoints.js +8 -0
  42. package/lib/module/services/endpoints.js.map +1 -0
  43. package/lib/module/types/common.js +2 -0
  44. package/lib/module/types/common.js.map +1 -0
  45. package/lib/module/utils/legendListMessage.js +80 -0
  46. package/lib/module/utils/legendListMessage.js.map +1 -0
  47. package/lib/module/utils/url.js +7 -0
  48. package/lib/module/utils/url.js.map +1 -0
  49. package/lib/typescript/src/components/Avatar/Avatar.d.ts +1 -1
  50. package/lib/typescript/src/components/Avatar/Avatar.d.ts.map +1 -1
  51. package/lib/typescript/src/components/Avatar/AvatarBadge.d.ts +1 -1
  52. package/lib/typescript/src/components/Avatar/AvatarBadge.d.ts.map +1 -1
  53. package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts +1 -1
  54. package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts.map +1 -1
  55. package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts +1 -1
  56. package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts.map +1 -1
  57. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +1 -1
  58. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -1
  59. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts +1 -1
  60. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -1
  61. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts +1 -1
  62. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  63. package/lib/typescript/src/components/ThreadCard/UnreadBadge.d.ts +1 -1
  64. package/lib/typescript/src/components/ThreadCard/UnreadBadge.d.ts.map +1 -1
  65. package/lib/typescript/src/config/feature-flags.d.ts +12 -0
  66. package/lib/typescript/src/config/feature-flags.d.ts.map +1 -0
  67. package/lib/typescript/src/context/ChatContext.d.ts +1 -1
  68. package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
  69. package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
  70. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  71. package/lib/typescript/src/hooks/useChatMessages.d.ts +3 -0
  72. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  73. package/lib/typescript/src/hooks/useLinkPreview/useFetchUrlMetadata.d.ts +3 -0
  74. package/lib/typescript/src/hooks/useLinkPreview/useFetchUrlMetadata.d.ts.map +1 -0
  75. package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts +7 -0
  76. package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts.map +1 -0
  77. package/lib/typescript/src/index.d.ts +1 -1
  78. package/lib/typescript/src/index.d.ts.map +1 -1
  79. package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts +1 -1
  80. package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts.map +1 -1
  81. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +1 -1
  82. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  83. package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts +1 -1
  84. package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts.map +1 -1
  85. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  86. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  87. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
  88. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
  89. package/lib/typescript/src/screens/chat-detail/ChatLinkPreview.d.ts +9 -0
  90. package/lib/typescript/src/screens/chat-detail/ChatLinkPreview.d.ts.map +1 -0
  91. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts +1 -1
  92. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts.map +1 -1
  93. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +3 -0
  94. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -0
  95. package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts +1 -1
  96. package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts.map +1 -1
  97. package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts +1 -1
  98. package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts.map +1 -1
  99. package/lib/typescript/src/screens/chat-detail/ChatScrollToBottom.d.ts +1 -1
  100. package/lib/typescript/src/screens/chat-detail/ChatScrollToBottom.d.ts.map +1 -1
  101. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts +1 -1
  102. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts.map +1 -1
  103. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts +6 -0
  104. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts.map +1 -0
  105. package/lib/typescript/src/screens/chat-detail/index.d.ts +2 -1
  106. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
  107. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts +6 -0
  108. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts.map +1 -0
  109. package/lib/typescript/src/screens/chat-detail/legend/LegendChatLoadEarlier.d.ts +6 -0
  110. package/lib/typescript/src/screens/chat-detail/legend/LegendChatLoadEarlier.d.ts.map +1 -0
  111. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +15 -0
  112. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -0
  113. package/lib/typescript/src/screens/chat-detail/legend/LegendChatScrollToBottom.d.ts +6 -0
  114. package/lib/typescript/src/screens/chat-detail/legend/LegendChatScrollToBottom.d.ts.map +1 -0
  115. package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts +12 -0
  116. package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts.map +1 -0
  117. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts +1 -1
  118. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts.map +1 -1
  119. package/lib/typescript/src/screens/chat-detail/types.d.ts +30 -3
  120. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  121. package/lib/typescript/src/screens/inbox/Inbox.d.ts +1 -1
  122. package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
  123. package/lib/typescript/src/screens/inbox/MessagesTab.d.ts +1 -1
  124. package/lib/typescript/src/screens/inbox/MessagesTab.d.ts.map +1 -1
  125. package/lib/typescript/src/services/apis.d.ts +1 -0
  126. package/lib/typescript/src/services/apis.d.ts.map +1 -1
  127. package/lib/typescript/src/services/endpoints.d.ts +6 -0
  128. package/lib/typescript/src/services/endpoints.d.ts.map +1 -0
  129. package/lib/typescript/src/types/common.d.ts +6 -0
  130. package/lib/typescript/src/types/common.d.ts.map +1 -0
  131. package/lib/typescript/src/utils/legendListMessage.d.ts +25 -0
  132. package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -0
  133. package/lib/typescript/src/utils/url.d.ts +2 -0
  134. package/lib/typescript/src/utils/url.d.ts.map +1 -0
  135. package/package.json +4 -2
  136. package/src/config/feature-flags.ts +49 -0
  137. package/src/hooks/query-keys.ts +5 -0
  138. package/src/hooks/useChatMessages.ts +60 -0
  139. package/src/hooks/useLinkPreview/useFetchUrlMetadata.ts +18 -0
  140. package/src/hooks/useLinkPreview/useLinkPreview.ts +30 -0
  141. package/src/index.tsx +1 -0
  142. package/src/screens/chat-detail/ChatComposer.tsx +21 -0
  143. package/src/screens/chat-detail/ChatDetail.tsx +154 -28
  144. package/src/screens/chat-detail/ChatLinkPreview.tsx +86 -0
  145. package/src/screens/chat-detail/ChatList.tsx +3 -0
  146. package/src/screens/chat-detail/ChatListLegend.tsx +404 -0
  147. package/src/screens/chat-detail/ChatQuickActions.tsx +19 -2
  148. package/src/screens/chat-detail/conversationHeader.utils.ts +52 -0
  149. package/src/screens/chat-detail/index.ts +7 -0
  150. package/src/screens/chat-detail/legend/LegendChatDay.tsx +70 -0
  151. package/src/screens/chat-detail/legend/LegendChatLoadEarlier.tsx +21 -0
  152. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +66 -0
  153. package/src/screens/chat-detail/legend/LegendChatScrollToBottom.tsx +56 -0
  154. package/src/screens/chat-detail/legend/message-types.tsx +149 -0
  155. package/src/screens/chat-detail/messages/ChatMessageBubble.tsx +0 -1
  156. package/src/screens/chat-detail/types.ts +43 -3
  157. package/src/services/apis.ts +1 -1
  158. package/src/services/endpoints.ts +5 -0
  159. package/src/types/common.ts +5 -0
  160. package/src/utils/legendListMessage.ts +102 -0
  161. package/src/utils/url.ts +5 -0
@@ -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
+ });
@@ -12,7 +12,6 @@ const getMessageRenderers = (): Partial<
12
12
  export const ChatMessageBubble = memo(
13
13
  ({ message, position }: ChatMessageBubbleProps) => {
14
14
  const Renderer = getMessageRenderers()[message.messageType];
15
-
16
15
  if (!Renderer) {
17
16
  return null;
18
17
  }
@@ -33,10 +33,17 @@ export interface ChatDetailHeaderProps {
33
33
  onPressAvatar?: () => void;
34
34
  }
35
35
 
36
+ export interface ChatQuickActionsRenderParams {
37
+ actions: DChatQuickAction[];
38
+ onActionPress: (action: DChatQuickAction) => void;
39
+ }
40
+
36
41
  export interface ChatQuickActionsProps {
42
+ visible?: boolean;
37
43
  actions?: DChatQuickAction[];
38
44
  onActionPress?: (action: DChatQuickAction) => void;
39
45
  renderAction?: (action: DChatQuickAction, onPress: () => void) => ReactNode;
46
+ renderQuickActions?: (params: ChatQuickActionsRenderParams) => ReactNode;
40
47
  }
41
48
 
42
49
  export interface ChatAttachmentPanelProps {
@@ -56,11 +63,13 @@ export interface ChatComposerProps {
56
63
  onSend?: () => void;
57
64
  onPressAttach?: () => void;
58
65
  onPressEmoji?: () => void;
66
+ showQuickActions?: boolean;
59
67
  quickActions?: DChatQuickAction[];
60
68
  attachmentActions?: DChatAttachmentAction[];
61
69
  onQuickActionPress?: (action: DChatQuickAction) => void;
62
70
  onAttachmentAction?: (action: DChatAttachmentAction) => void;
63
71
  renderQuickAction?: ChatQuickActionsProps['renderAction'];
72
+ renderQuickActions?: ChatQuickActionsProps['renderQuickActions'];
64
73
  renderAttachmentAction?: ChatAttachmentPanelProps['renderAction'];
65
74
  attachmentColumns?: number;
66
75
  }
@@ -73,17 +82,47 @@ export interface ChatListProps {
73
82
  isLoading?: boolean;
74
83
  isLoadingEarlier?: boolean;
75
84
  hasMoreEarlier?: boolean;
85
+ onLoadNewer?: () => void;
86
+ isLoadingNewer?: boolean;
87
+ hasMoreNewer?: boolean;
76
88
  }
77
89
 
78
- export interface ChatDetailProps extends ChatDetailHeaderProps {
79
- messages?: DMessageItem[];
80
- currentUserId?: string;
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;
81
112
  renderChat?: (item: DMessageItem) => ReactNode;
113
+ showQuickActions?: boolean;
114
+ showAttachmentActions?: boolean;
82
115
  onLoadEarlier?: () => void;
83
116
  isLoading?: boolean;
84
117
  isLoadingEarlier?: boolean;
85
118
  hasMoreEarlier?: boolean;
119
+ onLoadNewer?: () => void;
120
+ isLoadingNewer?: boolean;
121
+ hasMoreNewer?: boolean;
86
122
  quickActions?: DChatQuickAction[];
123
+ getQuickActions?: (
124
+ conversation: DConversationItem
125
+ ) => DChatQuickAction[] | undefined;
87
126
  attachmentActions?: DChatAttachmentAction[];
88
127
  inputValue?: string;
89
128
  inputPlaceholder?: string;
@@ -94,6 +133,7 @@ export interface ChatDetailProps extends ChatDetailHeaderProps {
94
133
  onQuickActionPress?: (action: DChatQuickAction) => void;
95
134
  onAttachmentAction?: (action: DChatAttachmentAction) => void;
96
135
  renderQuickAction?: ChatQuickActionsProps['renderAction'];
136
+ renderQuickActions?: ChatQuickActionsProps['renderQuickActions'];
97
137
  renderAttachmentAction?: ChatAttachmentPanelProps['renderAction'];
98
138
  attachmentColumns?: number;
99
139
  }
@@ -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,5 @@
1
+ export const ENDPOINTS = {
2
+ chatService: {
3
+ urlMetadata: '/chat-service/v1/utils/url-metadata',
4
+ },
5
+ };
@@ -0,0 +1,5 @@
1
+ export interface IUrlMetadata {
2
+ title?: string;
3
+ description?: string;
4
+ image?: string;
5
+ }
@@ -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
+ };
@@ -0,0 +1,5 @@
1
+ const URL_REGEX = /https?:\/\/[^\s]+/i;
2
+
3
+ export function extractFirstUrl(text: string): string | undefined {
4
+ return text.match(URL_REGEX)?.[0];
5
+ }