@droppii-org/chat-mobile 0.2.24 → 0.2.26

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 (178) hide show
  1. package/lib/module/assets/images/btn_resend.png +0 -0
  2. package/lib/module/assets/images/index.js +2 -1
  3. package/lib/module/assets/images/index.js.map +1 -1
  4. package/lib/module/components/AttachmentPreview/FilePreview.js +1 -1
  5. package/lib/module/components/AttachmentPreview/FilePreview.js.map +1 -1
  6. package/lib/module/components/flows/inputButtons/index.js +3 -6
  7. package/lib/module/components/flows/inputButtons/index.js.map +1 -1
  8. package/lib/module/components/messages/MessageStatusIndicator.js +70 -0
  9. package/lib/module/components/messages/MessageStatusIndicator.js.map +1 -0
  10. package/lib/module/components/messages/fileMessage/index.js +1 -1
  11. package/lib/module/components/messages/fileMessage/index.js.map +1 -1
  12. package/lib/module/components/messages/imageMessage/index.js +60 -51
  13. package/lib/module/components/messages/imageMessage/index.js.map +1 -1
  14. package/lib/module/components/messages/linkMessage/index.js +17 -9
  15. package/lib/module/components/messages/linkMessage/index.js.map +1 -1
  16. package/lib/module/components/messages/mergedMessage/index.js +5 -8
  17. package/lib/module/components/messages/mergedMessage/index.js.map +1 -1
  18. package/lib/module/components/messages/styles.js +12 -0
  19. package/lib/module/components/messages/styles.js.map +1 -1
  20. package/lib/module/components/messages/textMessage/index.js +20 -9
  21. package/lib/module/components/messages/textMessage/index.js.map +1 -1
  22. package/lib/module/components/messages/videoMessage/index.js +7 -11
  23. package/lib/module/components/messages/videoMessage/index.js.map +1 -1
  24. package/lib/module/config/uploadLimits.js +1 -1
  25. package/lib/module/config/uploadLimits.js.map +1 -1
  26. package/lib/module/core/useChatListener.js +35 -4
  27. package/lib/module/core/useChatListener.js.map +1 -1
  28. package/lib/module/core/useUserListener.js +17 -7
  29. package/lib/module/core/useUserListener.js.map +1 -1
  30. package/lib/module/hooks/message/useSendMessage.js +62 -15
  31. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  32. package/lib/module/hooks/useChatCompose.js +39 -28
  33. package/lib/module/hooks/useChatCompose.js.map +1 -1
  34. package/lib/module/hooks/useChatMessages.js +39 -8
  35. package/lib/module/hooks/useChatMessages.js.map +1 -1
  36. package/lib/module/hooks/useFileAttachment.js +1 -1
  37. package/lib/module/hooks/useFileAttachment.js.map +1 -1
  38. package/lib/module/hooks/useFileDownload.js +1 -1
  39. package/lib/module/hooks/useFileDownload.js.map +1 -1
  40. package/lib/module/hooks/useImageAttachment.js +12 -3
  41. package/lib/module/hooks/useImageAttachment.js.map +1 -1
  42. package/lib/module/index.js +1 -0
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/screens/chat-detail/ChatDetail.js +24 -1
  45. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  46. package/lib/module/screens/chat-detail/ChatListLegend.js +40 -28
  47. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  48. package/lib/module/screens/chat-detail/constants.js +3 -2
  49. package/lib/module/screens/chat-detail/constants.js.map +1 -1
  50. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +48 -15
  51. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  52. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js +98 -0
  53. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js.map +1 -0
  54. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js +110 -0
  55. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js.map +1 -0
  56. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +64 -0
  57. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -0
  58. package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js +37 -0
  59. package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js.map +1 -0
  60. package/lib/module/screens/chat-detail/messageToolbox/types.js +4 -0
  61. package/lib/module/screens/chat-detail/messageToolbox/types.js.map +1 -0
  62. package/lib/module/services/attachmentOrchestrator.js +34 -12
  63. package/lib/module/services/attachmentOrchestrator.js.map +1 -1
  64. package/lib/module/services/message.js +13 -5
  65. package/lib/module/services/message.js.map +1 -1
  66. package/lib/module/translation/resources/i18n.js +9 -3
  67. package/lib/module/translation/resources/i18n.js.map +1 -1
  68. package/lib/module/types/chat.js.map +1 -1
  69. package/lib/module/types/global.d.js +2 -0
  70. package/lib/module/types/global.d.js.map +1 -0
  71. package/lib/module/types/index.d.js +3 -0
  72. package/lib/module/types/index.d.js.map +1 -0
  73. package/lib/module/utils/UIUtils.js +37 -0
  74. package/lib/module/utils/UIUtils.js.map +1 -0
  75. package/lib/module/utils/ui.js +15 -13
  76. package/lib/module/utils/ui.js.map +1 -1
  77. package/lib/module/utils/videoThumbnailUpload.js +1 -1
  78. package/lib/module/utils/videoThumbnailUpload.js.map +1 -1
  79. package/lib/typescript/src/assets/images/index.d.ts +1 -0
  80. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  81. package/lib/typescript/src/components/flows/inputButtons/index.d.ts.map +1 -1
  82. package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts +9 -0
  83. package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts.map +1 -0
  84. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
  85. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -1
  86. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +3 -8
  87. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  88. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -1
  89. package/lib/typescript/src/components/messages/styles.d.ts +15 -3
  90. package/lib/typescript/src/components/messages/styles.d.ts.map +1 -1
  91. package/lib/typescript/src/components/messages/textMessage/index.d.ts +2 -8
  92. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
  93. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -1
  94. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  95. package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
  96. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -0
  97. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  98. package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -1
  99. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  100. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
  101. package/lib/typescript/src/index.d.ts +1 -0
  102. package/lib/typescript/src/index.d.ts.map +1 -1
  103. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  104. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
  105. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  106. package/lib/typescript/src/screens/chat-detail/constants.d.ts +2 -2
  107. package/lib/typescript/src/screens/chat-detail/constants.d.ts.map +1 -1
  108. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +4 -5
  109. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  110. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts +11 -0
  111. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts.map +1 -0
  112. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts +10 -0
  113. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts.map +1 -0
  114. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts +11 -0
  115. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -0
  116. package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts +18 -0
  117. package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts.map +1 -0
  118. package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts +12 -0
  119. package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts.map +1 -0
  120. package/lib/typescript/src/screens/chat-detail/types.d.ts +2 -0
  121. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  122. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
  123. package/lib/typescript/src/services/message.d.ts +7 -0
  124. package/lib/typescript/src/services/message.d.ts.map +1 -1
  125. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  126. package/lib/typescript/src/types/chat.d.ts +11 -0
  127. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  128. package/lib/typescript/src/utils/UIUtils.d.ts +4 -0
  129. package/lib/typescript/src/utils/UIUtils.d.ts.map +1 -0
  130. package/lib/typescript/src/utils/ui.d.ts +1 -12
  131. package/lib/typescript/src/utils/ui.d.ts.map +1 -1
  132. package/package.json +4 -2
  133. package/src/assets/images/btn_resend.png +0 -0
  134. package/src/assets/images/index.ts +1 -0
  135. package/src/components/AttachmentPreview/FilePreview.tsx +1 -1
  136. package/src/components/flows/inputButtons/index.tsx +3 -5
  137. package/src/components/messages/MessageStatusIndicator.tsx +82 -0
  138. package/src/components/messages/fileMessage/index.tsx +0 -2
  139. package/src/components/messages/imageMessage/index.tsx +72 -58
  140. package/src/components/messages/linkMessage/index.tsx +16 -21
  141. package/src/components/messages/mergedMessage/index.tsx +13 -18
  142. package/src/components/messages/styles.ts +12 -0
  143. package/src/components/messages/textMessage/index.tsx +20 -20
  144. package/src/components/messages/videoMessage/index.tsx +8 -11
  145. package/src/config/uploadLimits.ts +1 -1
  146. package/src/core/useChatListener.ts +38 -4
  147. package/src/core/useUserListener.ts +17 -7
  148. package/src/hooks/message/useSendMessage.ts +103 -24
  149. package/src/hooks/useChatCompose.ts +42 -33
  150. package/src/hooks/useChatMessages.ts +54 -10
  151. package/src/hooks/useFileAttachment.ts +1 -1
  152. package/src/hooks/useFileDownload.ts +1 -1
  153. package/src/hooks/useImageAttachment.ts +13 -5
  154. package/src/index.tsx +2 -0
  155. package/src/screens/chat-detail/ChatDetail.tsx +29 -0
  156. package/src/screens/chat-detail/ChatListLegend.tsx +45 -28
  157. package/src/screens/chat-detail/constants.ts +3 -2
  158. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +60 -15
  159. package/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.tsx +114 -0
  160. package/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.tsx +130 -0
  161. package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +99 -0
  162. package/src/screens/chat-detail/messageToolbox/messageToolbox.utils.ts +56 -0
  163. package/src/screens/chat-detail/messageToolbox/types.ts +13 -0
  164. package/src/screens/chat-detail/types.ts +2 -0
  165. package/src/services/attachmentOrchestrator.ts +45 -21
  166. package/src/services/message.ts +21 -4
  167. package/src/translation/resources/i18n.ts +11 -3
  168. package/src/types/chat.ts +6 -0
  169. package/src/types/global.d.ts +299 -0
  170. package/src/types/index.d.ts +1 -0
  171. package/src/utils/UIUtils.ts +31 -0
  172. package/src/utils/ui.ts +20 -16
  173. package/src/utils/videoThumbnailUpload.ts +1 -1
  174. package/lib/module/hooks/useVideoAttachment.js +0 -251
  175. package/lib/module/hooks/useVideoAttachment.js.map +0 -1
  176. package/lib/typescript/src/hooks/useVideoAttachment.d.ts +0 -29
  177. package/lib/typescript/src/hooks/useVideoAttachment.d.ts.map +0 -1
  178. package/src/hooks/useVideoAttachment.ts +0 -334
@@ -7,18 +7,10 @@ import {
7
7
  KColors,
8
8
  KSpacingValue,
9
9
  } from '@droppii/libs';
10
- import type { DMessageItem } from '../../../types/chat';
11
10
  import type { IUrlMetadata } from '../../../types/common';
12
11
  import { formatMessageTime } from '../types';
12
+ import type { BaseLegendMessageProps } from '../types';
13
13
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
14
-
15
- interface BaseLegendMessageProps {
16
- message: DMessageItem;
17
- isOutgoing: boolean;
18
- createdAtTime: number;
19
- onPressUrl?: (url: string) => void;
20
- }
21
-
22
14
  const parseUrlMetadata = (ex?: string): IUrlMetadata | undefined => {
23
15
  if (!ex) return undefined;
24
16
  try {
@@ -80,14 +72,14 @@ export const LinkMessage = memo(
80
72
  isOutgoing,
81
73
  createdAtTime,
82
74
  onPressUrl,
83
- }: BaseLegendMessageProps) => {
75
+ }: BaseLegendMessageProps & { onPressUrl?: (url: string) => void }) => {
84
76
  const content = message?.urlTextElem?.content;
85
77
  const urls = message?.urlTextElem?.urls ?? [];
86
78
  const metadata = parseUrlMetadata(message.ex);
87
79
  const timeLabel = formatMessageTime(createdAtTime);
88
80
 
89
81
  return (
90
- <KContainer.View style={styles.wrapper}>
82
+ <>
91
83
  <KContainer.View
92
84
  style={[
93
85
  styles.bubble,
@@ -147,16 +139,18 @@ export const LinkMessage = memo(
147
139
  )}
148
140
  </KContainer.View>
149
141
 
150
- {!isOutgoing && timeLabel ? (
151
- <KLabel.Text
152
- typo="TextXsNormal"
153
- color={CHAT_BUBBLE_COLORS.timestamp}
154
- marginL="0.25rem"
155
- >
156
- {timeLabel}
157
- </KLabel.Text>
158
- ) : null}
159
- </KContainer.View>
142
+ <KContainer.View row alignItems style={styles.timeLabel}>
143
+ {!isOutgoing && timeLabel ? (
144
+ <KLabel.Text
145
+ typo="TextXsNormal"
146
+ color={CHAT_BUBBLE_COLORS.timestamp}
147
+ marginL="0.25rem"
148
+ >
149
+ {timeLabel}
150
+ </KLabel.Text>
151
+ ) : null}
152
+ </KContainer.View>
153
+ </>
160
154
  );
161
155
  }
162
156
  );
@@ -197,4 +191,5 @@ const styles = StyleSheet.create({
197
191
  paddingVertical: KSpacingValue['0.5rem'],
198
192
  gap: 2,
199
193
  },
194
+ timeLabel: { gap: 4, marginTop: 2 },
200
195
  });
@@ -3,7 +3,6 @@ import { KContainer } from '@droppii/libs';
3
3
  import { MergedImageGrid } from '../../MergedImageGrid';
4
4
  import { messageStyles } from '../styles';
5
5
  import type { BaseLegendMessageProps } from '../types';
6
-
7
6
  export const MergedMessage = memo(
8
7
  ({ message, isOutgoing, onMediaPress }: BaseLegendMessageProps) => {
9
8
  const mergeElem = (message as any).mergeElem;
@@ -18,23 +17,19 @@ export const MergedMessage = memo(
18
17
  );
19
18
 
20
19
  return (
21
- <KContainer.View style={messageStyles.wrapper}>
22
- <KContainer.View
23
- style={[
24
- messageStyles.bubble,
25
- isOutgoing
26
- ? messageStyles.bubbleSent
27
- : messageStyles.bubbleReceived,
28
- ]}
29
- >
30
- {imageMessages.length > 0 && (
31
- <MergedImageGrid
32
- images={imageMessages}
33
- maxDisplay={3}
34
- onMediaPress={onMediaPress}
35
- />
36
- )}
37
- </KContainer.View>
20
+ <KContainer.View
21
+ style={[
22
+ messageStyles.bubble,
23
+ isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
24
+ ]}
25
+ >
26
+ {imageMessages.length > 0 && (
27
+ <MergedImageGrid
28
+ images={imageMessages}
29
+ maxDisplay={3}
30
+ onMediaPress={onMediaPress}
31
+ />
32
+ )}
38
33
  </KContainer.View>
39
34
  );
40
35
  }
@@ -36,4 +36,16 @@ export const messageStyles = StyleSheet.create({
36
36
  bottom: 0,
37
37
  backgroundColor: 'rgba(0, 0, 0, 0.3)',
38
38
  },
39
+ messageRowLeft: {
40
+ width: '100%',
41
+ paddingHorizontal: KSpacingValue['0.75rem'],
42
+ marginBottom: KSpacingValue['0.25rem'],
43
+ alignItems: 'flex-start',
44
+ },
45
+ messageRowRight: {
46
+ width: '100%',
47
+ paddingHorizontal: KSpacingValue['0.75rem'],
48
+ marginBottom: KSpacingValue['0.25rem'],
49
+ alignItems: 'flex-end',
50
+ },
39
51
  });
@@ -1,19 +1,13 @@
1
1
  import { memo } from 'react';
2
2
  import { KContainer, KLabel } from '@droppii/libs';
3
3
  import { getMessageText } from '../../../utils/legendListMessage';
4
- import type { DMessageItem } from '../../../types/chat';
5
4
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
6
5
  import { messageStyles } from '../styles';
7
6
  import { formatMessageTime } from '../types';
8
-
9
- interface TextMessageProps {
10
- message: DMessageItem;
11
- isOutgoing: boolean;
12
- createdAtTime: number;
13
- }
14
-
7
+ import type { BaseLegendMessageProps } from '../types';
8
+ import { StyleSheet } from 'react-native';
15
9
  export const TextMessage = memo(
16
- ({ message, isOutgoing, createdAtTime }: TextMessageProps) => {
10
+ ({ message, isOutgoing, createdAtTime }: BaseLegendMessageProps) => {
17
11
  const messageText = getMessageText(message);
18
12
  const timeLabel = formatMessageTime(createdAtTime);
19
13
 
@@ -22,7 +16,7 @@ export const TextMessage = memo(
22
16
  }
23
17
 
24
18
  return (
25
- <KContainer.View style={messageStyles.wrapper}>
19
+ <>
26
20
  <KContainer.View
27
21
  style={[
28
22
  messageStyles.bubble,
@@ -36,18 +30,24 @@ export const TextMessage = memo(
36
30
  </KLabel.Text>
37
31
  </KContainer.View>
38
32
 
39
- {!isOutgoing && timeLabel ? (
40
- <KLabel.Text
41
- typo="TextXsNormal"
42
- color={CHAT_BUBBLE_COLORS.timestamp}
43
- marginL={'0.25rem'}
44
- >
45
- {timeLabel}
46
- </KLabel.Text>
47
- ) : null}
48
- </KContainer.View>
33
+ <KContainer.View row alignItems style={styles.timeLabel}>
34
+ {!isOutgoing && timeLabel ? (
35
+ <KLabel.Text
36
+ typo="TextXsNormal"
37
+ color={CHAT_BUBBLE_COLORS.timestamp}
38
+ marginL={'0.25rem'}
39
+ >
40
+ {timeLabel}
41
+ </KLabel.Text>
42
+ ) : null}
43
+ </KContainer.View>
44
+ </>
49
45
  );
50
46
  }
51
47
  );
52
48
 
53
49
  TextMessage.displayName = 'TextMessage';
50
+
51
+ const styles = StyleSheet.create({
52
+ timeLabel: { gap: 4, marginTop: 2 },
53
+ });
@@ -7,7 +7,6 @@ import ChatImageDimens, {
7
7
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
8
8
  import { messageStyles } from '../styles';
9
9
  import type { BaseLegendMessageProps } from '../types';
10
-
11
10
  const formatDuration = (durationMs: number): string => {
12
11
  const totalSeconds = Math.floor(durationMs / 1000);
13
12
  const minutes = Math.floor(totalSeconds / 60);
@@ -30,14 +29,12 @@ export const VideoMessage = memo(
30
29
 
31
30
  if (!videoUrl) {
32
31
  return (
33
- <KContainer.View style={messageStyles.wrapper}>
34
- <KContainer.View
35
- style={[messageStyles.bubble, messageStyles.videoPlaceholder]}
36
- >
37
- <KLabel.Text typo="TextSmNormal" color={CHAT_BUBBLE_COLORS.text}>
38
- 📹 [Video unavailable]
39
- </KLabel.Text>
40
- </KContainer.View>
32
+ <KContainer.View
33
+ style={[messageStyles.bubble, messageStyles.videoPlaceholder]}
34
+ >
35
+ <KLabel.Text typo="TextSmNormal" color={CHAT_BUBBLE_COLORS.text}>
36
+ 📹 [Video unavailable]
37
+ </KLabel.Text>
41
38
  </KContainer.View>
42
39
  );
43
40
  }
@@ -47,7 +44,7 @@ export const VideoMessage = memo(
47
44
  );
48
45
 
49
46
  return (
50
- <KContainer.View style={messageStyles.wrapper}>
47
+ <>
51
48
  <KContainer.Touchable
52
49
  onPress={handlePress}
53
50
  style={[
@@ -83,7 +80,7 @@ export const VideoMessage = memo(
83
80
  </KContainer.View>
84
81
  )}
85
82
  </KContainer.Touchable>
86
- </KContainer.View>
83
+ </>
87
84
  );
88
85
  }
89
86
  );
@@ -1,7 +1,7 @@
1
1
  const MB = 1024 * 1024;
2
2
 
3
3
  export const UPLOAD_LIMITS = {
4
- image: 5 * MB,
4
+ image: 15 * MB,
5
5
  video: 200 * MB,
6
6
  file: 50 * MB,
7
7
  } as const;
@@ -2,30 +2,58 @@ import OpenIMSDK, {
2
2
  OpenIMEvent,
3
3
  type ConversationItem,
4
4
  type GroupMemberItem,
5
+ type MessageItem,
5
6
  } from '@droppii/openim-rn-client-sdk';
6
7
  import { useCallback, useEffect } from 'react';
7
8
  import { useConversationStore } from '../store/conversation';
9
+ import { useMessageStore } from '../store/message';
8
10
 
9
11
  export const useChatListener = () => {
12
+ const updateOneMessage = useMessageStore((state) => state.updateOneMessage);
13
+
14
+ const onSendMessageProgress = useCallback(
15
+ ({ progress, message }: { progress: number; message: MessageItem }) => {
16
+ console.log('[Listener] SendMessageProgress', {
17
+ clientMsgID: message.clientMsgID,
18
+ status: message.status,
19
+ progress,
20
+ });
21
+ updateOneMessage(message);
22
+ },
23
+ [updateOneMessage]
24
+ );
25
+
10
26
  const onConversationChanged = useCallback(
11
27
  (conversations: ConversationItem[]) => {
12
- console.log('onConversationChanged', conversations);
28
+ console.log('[Listener] OnConversationChanged', {
29
+ count: conversations.length,
30
+ ids: conversations.map((c) => c.conversationID),
31
+ });
13
32
  useConversationStore.getState().updateConversations(conversations);
14
33
  },
15
34
  []
16
35
  );
17
36
 
18
37
  const onNewConversation = useCallback((conversations: ConversationItem[]) => {
19
- console.log('onNewConversation', conversations);
38
+ console.log('[Listener] OnNewConversation', {
39
+ count: conversations.length,
40
+ ids: conversations.map((c) => c.conversationID),
41
+ });
20
42
  useConversationStore.getState().updateConversations(conversations);
21
43
  }, []);
22
44
 
23
45
  const onGroupMemberAdded = useCallback((member: GroupMemberItem) => {
24
- console.log('onGroupMemberAdded', member);
46
+ console.log('[Listener] OnGroupMemberAdded', {
47
+ userID: member.userID,
48
+ groupID: member.groupID,
49
+ });
25
50
  }, []);
26
51
 
27
52
  const onGroupMemberDeleted = useCallback((member: GroupMemberItem) => {
28
- console.log('onGroupMemberDeleted', member);
53
+ console.log('[Listener] OnGroupMemberDeleted', {
54
+ userID: member.userID,
55
+ groupID: member.groupID,
56
+ });
29
57
  }, []);
30
58
 
31
59
  useEffect(() => {
@@ -37,6 +65,9 @@ export const useChatListener = () => {
37
65
  OpenIMSDK.on(OpenIMEvent.OnGroupMemberAdded, onGroupMemberAdded);
38
66
  OpenIMSDK.on(OpenIMEvent.OnGroupMemberDeleted, onGroupMemberDeleted);
39
67
 
68
+ // Message status
69
+ OpenIMSDK.on(OpenIMEvent.SendMessageProgress, onSendMessageProgress);
70
+
40
71
  return () => {
41
72
  // Conversation
42
73
  OpenIMSDK.off(OpenIMEvent.OnConversationChanged, onConversationChanged);
@@ -45,6 +76,9 @@ export const useChatListener = () => {
45
76
  // Group members
46
77
  OpenIMSDK.off(OpenIMEvent.OnGroupMemberAdded, onGroupMemberAdded);
47
78
  OpenIMSDK.off(OpenIMEvent.OnGroupMemberDeleted, onGroupMemberDeleted);
79
+
80
+ // Message status
81
+ OpenIMSDK.off(OpenIMEvent.SendMessageProgress, onSendMessageProgress);
48
82
  };
49
83
  // eslint-disable-next-line react-hooks/exhaustive-deps
50
84
  }, []);
@@ -1,6 +1,7 @@
1
1
  import OpenIMSDK, {
2
2
  LoginStatus,
3
3
  OpenIMEvent,
4
+ type OpenIMEventBaseErrorType,
4
5
  } from '@droppii/openim-rn-client-sdk';
5
6
  import { useCallback, useEffect } from 'react';
6
7
  import { useUserStore } from '../store';
@@ -28,36 +29,45 @@ export const useUserListener = () => {
28
29
  }, [isLoggedIn, fetchAndUpdateUser]);
29
30
 
30
31
  const onConnecting = useCallback(() => {
32
+ console.log('[Listener] OnConnecting');
31
33
  useUserStore.getState().updateConnectStatus(LoginStatus.Logging);
32
34
  }, []);
33
35
 
34
36
  const onConnectSuccess = useCallback(() => {
37
+ console.log('[Listener] OnConnectSuccess');
35
38
  useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
36
39
  }, []);
37
40
 
38
- const onConnectFailed = useCallback(() => {
39
- useUserStore.getState().updateConnectStatus(LoginStatus.Logout);
41
+ const onConnectFailed = useCallback((data: OpenIMEventBaseErrorType) => {
42
+ console.log('[Listener] OnConnectFailed', data);
40
43
  }, []);
41
44
 
42
- const onSyncServerStart = useCallback(() => {
45
+ const onSyncServerStart = useCallback((isSyncing: boolean) => {
46
+ console.log('[Listener] OnSyncServerStart', { isSyncing });
43
47
  useUserStore.getState().updateSyncStatus(SyncStatus.Loading);
44
48
  }, []);
45
49
 
46
50
  const onSyncServerFinish = useCallback(() => {
51
+ console.log('[Listener] OnSyncServerFinish');
47
52
  useUserStore.getState().updateSyncStatus(SyncStatus.Success);
48
53
  }, []);
49
54
 
50
55
  const onSyncServerFailed = useCallback(() => {
56
+ console.log('[Listener] OnSyncServerFailed');
51
57
  useUserStore.getState().updateSyncStatus(SyncStatus.Failed);
52
58
  }, []);
53
59
 
60
+ const onUserToken = useCallback(() => {
61
+ console.log('[Listener] OnUserToken expired/invalid');
62
+ }, []);
63
+
54
64
  useEffect(() => {
55
65
  // User
56
66
  OpenIMSDK.on(OpenIMEvent.OnConnecting, onConnecting);
57
67
  OpenIMSDK.on(OpenIMEvent.OnConnectSuccess, onConnectSuccess);
58
68
  OpenIMSDK.on(OpenIMEvent.OnConnectFailed, onConnectFailed);
59
- OpenIMSDK.on(OpenIMEvent.OnUserTokenExpired, onConnectFailed);
60
- OpenIMSDK.on(OpenIMEvent.OnUserTokenInvalid, onConnectFailed);
69
+ OpenIMSDK.on(OpenIMEvent.OnUserTokenExpired, onUserToken);
70
+ OpenIMSDK.on(OpenIMEvent.OnUserTokenInvalid, onUserToken);
61
71
 
62
72
  // Sync
63
73
  OpenIMSDK.on(OpenIMEvent.OnSyncServerStart, onSyncServerStart);
@@ -69,8 +79,8 @@ export const useUserListener = () => {
69
79
  OpenIMSDK.off(OpenIMEvent.OnConnecting, onConnecting);
70
80
  OpenIMSDK.off(OpenIMEvent.OnConnectSuccess, onConnectSuccess);
71
81
  OpenIMSDK.off(OpenIMEvent.OnConnectFailed, onConnectFailed);
72
- OpenIMSDK.on(OpenIMEvent.OnUserTokenExpired, onConnectFailed);
73
- OpenIMSDK.on(OpenIMEvent.OnUserTokenInvalid, onConnectFailed);
82
+ OpenIMSDK.off(OpenIMEvent.OnUserTokenExpired, onUserToken);
83
+ OpenIMSDK.off(OpenIMEvent.OnUserTokenInvalid, onUserToken);
74
84
 
75
85
  // Sync
76
86
  OpenIMSDK.off(OpenIMEvent.OnSyncServerStart, onSyncServerStart);
@@ -1,23 +1,64 @@
1
1
  import { useCallback } from 'react';
2
2
  import type { UploadFileItem } from 'react-native-fs';
3
- import { useConversationStore } from '../../store';
4
- import type {
5
- MessageItem,
6
- MessageType,
7
- OfflinePush,
8
- } from '@droppii/openim-rn-client-sdk';
3
+ import { useConversationStore } from '../../store/conversation';
4
+ import type { MessageItem, OfflinePush } from '@droppii/openim-rn-client-sdk';
5
+ import { MessageStatus, MessageType } from '@droppii/openim-rn-client-sdk';
9
6
  import { extractUrls } from '../../utils/url';
10
7
  import OpenIMSDK from '@droppii/openim-rn-client-sdk';
11
8
  import type { IMessageItemEx } from '../../types/chat';
12
9
  import { useChatContext } from '../../context';
13
10
  import { generateContentBasedOnMessageType } from '../../utils/conversation';
14
11
  import { useMessageStore } from '../../store/message';
12
+
15
13
  import type { IUrlMetadata } from '../../types/common';
16
14
  import type { IRunConversationFlowPayload } from '../../types/flows';
17
15
 
16
+ const buildSendParams = (
17
+ message: MessageItem,
18
+ recvID: string,
19
+ groupID: string,
20
+ extendMessageInfo: IMessageItemEx,
21
+ plainText: string
22
+ ) => ({
23
+ groupID,
24
+ recvID,
25
+ message: { ...message, ex: JSON.stringify(extendMessageInfo) || '{}' },
26
+ offlinePushInfo: generateOfflinePushInfo(message.contentType, plainText),
27
+ });
28
+
18
29
  export const useSendMessage = () => {
19
30
  const applicationType = useChatContext().applicationType;
20
31
  const pushNewMessage = useMessageStore((state) => state.pushNewMessage);
32
+ const updateOneMessage = useMessageStore((state) => state.updateOneMessage);
33
+ const deleteOneMessage = useMessageStore((state) => state.deleteOneMessage);
34
+ const dispatchSend = useCallback(
35
+ async (
36
+ message: MessageItem,
37
+ recvID: string,
38
+ groupID: string,
39
+ extendMessageInfo: IMessageItemEx,
40
+ plainText: string
41
+ ) => {
42
+ pushNewMessage({ ...message, status: MessageStatus.Sending });
43
+
44
+ try {
45
+ const sent = await OpenIMSDK.sendMessage(
46
+ buildSendParams(
47
+ message,
48
+ recvID,
49
+ groupID,
50
+ extendMessageInfo,
51
+ plainText
52
+ )
53
+ );
54
+ deleteOneMessage(message.clientMsgID);
55
+ pushNewMessage(sent);
56
+ } catch {
57
+ updateOneMessage({ ...message, status: MessageStatus.Failed });
58
+ }
59
+ },
60
+ [pushNewMessage, updateOneMessage, deleteOneMessage]
61
+ );
21
62
 
22
63
  const sendMessage = useCallback(
23
64
  async ({
@@ -27,7 +68,7 @@ export const useSendMessage = () => {
27
68
  flow,
28
69
  }: {
29
70
  plainText?: string;
30
- files?: UploadFileItem[]; //TODO
71
+ files?: UploadFileItem[];
31
72
  urlMetadata?: IUrlMetadata;
32
73
  flow?: IRunConversationFlowPayload;
33
74
  }) => {
@@ -41,7 +82,6 @@ export const useSendMessage = () => {
41
82
  if (!file) continue;
42
83
  }
43
84
 
44
- // Allow empty text if urlMetadata (preview link) is present
45
85
  const hasText = !!plainText && plainText.trim() !== '';
46
86
  const hasPreview = !!urlMetadata?.url;
47
87
 
@@ -51,7 +91,6 @@ export const useSendMessage = () => {
51
91
  let message: MessageItem | null = null;
52
92
 
53
93
  if (isUrlMessage) {
54
- // Create URL message even with empty text if preview exists
55
94
  message = await createUrlTextMessage(plainText || '', urls);
56
95
  } else if (hasText) {
57
96
  message = await createTextMessage(plainText);
@@ -68,27 +107,67 @@ export const useSendMessage = () => {
68
107
  flow,
69
108
  };
70
109
 
71
- const successMessage = await OpenIMSDK.sendMessage({
72
- groupID: groupID || '',
73
- recvID: recvID || '',
74
- message: {
75
- ...message,
76
- ex: JSON.stringify(extendMessageInfo) || '{}',
77
- },
78
- offlinePushInfo: generateOfflinePushInfo(
79
- message.contentType,
80
- plainText || ''
81
- ),
82
- });
83
-
84
- pushNewMessage(successMessage);
110
+ await dispatchSend(
111
+ message,
112
+ recvID || '',
113
+ groupID || '',
114
+ extendMessageInfo,
115
+ plainText || ''
116
+ );
117
+ }
118
+ },
119
+ [applicationType, dispatchSend]
120
+ );
121
+
122
+ const retryMessage = useCallback(
123
+ async (message: MessageItem) => {
124
+ const { userID: recvID, groupID } =
125
+ useConversationStore.getState().getCurrentConversation() || {};
126
+ if (!recvID && !groupID) return;
127
+
128
+ updateOneMessage({ ...message, status: MessageStatus.Sending });
129
+
130
+ // Attachment messages were sent via sendMessageNotOss (URL already uploaded).
131
+ // Text/URL messages use sendMessage (SDK handles upload).
132
+ const isAttachment = (
133
+ [
134
+ MessageType.PictureMessage,
135
+ MessageType.VideoMessage,
136
+ MessageType.FileMessage,
137
+ MessageType.MergeMessage,
138
+ ] as MessageType[]
139
+ ).includes(message.contentType);
140
+
141
+ const params = {
142
+ recvID: recvID || '',
143
+ groupID: groupID || '',
144
+ message,
145
+ };
146
+
147
+ try {
148
+ const sent = isAttachment
149
+ ? await OpenIMSDK.sendMessageNotOss(params)
150
+ : await OpenIMSDK.sendMessage(
151
+ buildSendParams(
152
+ message,
153
+ recvID || '',
154
+ groupID || '',
155
+ JSON.parse(message.ex || '{}') as IMessageItemEx,
156
+ ''
157
+ )
158
+ );
159
+ deleteOneMessage(message.clientMsgID);
160
+ pushNewMessage(sent);
161
+ } catch {
162
+ updateOneMessage({ ...message, status: MessageStatus.Failed });
85
163
  }
86
164
  },
87
- [applicationType, pushNewMessage]
165
+ [pushNewMessage, updateOneMessage, deleteOneMessage]
88
166
  );
89
167
 
90
168
  return {
91
169
  sendMessage,
170
+ retryMessage,
92
171
  };
93
172
  };
94
173