@droppii-org/chat-mobile 0.2.37 → 0.2.39

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 (150) hide show
  1. package/lib/module/components/ThreadCard/ThreadCard.js +2 -1
  2. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  3. package/lib/module/components/messages/linkMessage/index.js +1 -10
  4. package/lib/module/components/messages/linkMessage/index.js.map +1 -1
  5. package/lib/module/components/messages/quotedMessage/index.js +177 -0
  6. package/lib/module/components/messages/quotedMessage/index.js.map +1 -0
  7. package/lib/module/context/ChatContext.js +26 -22
  8. package/lib/module/context/ChatContext.js.map +1 -1
  9. package/lib/module/context/ChatDetailContext.js +7 -4
  10. package/lib/module/context/ChatDetailContext.js.map +1 -1
  11. package/lib/module/core/useChatListener.js +15 -6
  12. package/lib/module/core/useChatListener.js.map +1 -1
  13. package/lib/module/core/useUserListener.js +12 -21
  14. package/lib/module/core/useUserListener.js.map +1 -1
  15. package/lib/module/hooks/message/useSendMessage.js +26 -4
  16. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  17. package/lib/module/hooks/useChatMessages.js.map +1 -1
  18. package/lib/module/index.js +0 -7
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/screens/chat-detail/ChatComposer.js +2 -1
  21. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  22. package/lib/module/screens/chat-detail/ChatCurrentQuotedMessage.js +144 -0
  23. package/lib/module/screens/chat-detail/ChatCurrentQuotedMessage.js.map +1 -0
  24. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  25. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  26. package/lib/module/screens/chat-detail/PinnedMessageBar.js +4 -2
  27. package/lib/module/screens/chat-detail/PinnedMessageBar.js.map +1 -1
  28. package/lib/module/screens/chat-detail/PinnedMessagesSheet.js.map +1 -1
  29. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +2 -1
  30. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  31. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js +9 -1
  32. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js.map +1 -1
  33. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js +11 -1
  34. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js.map +1 -1
  35. package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js +5 -2
  36. package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js.map +1 -1
  37. package/lib/module/screens/inbox/Inbox.js +1 -5
  38. package/lib/module/screens/inbox/Inbox.js.map +1 -1
  39. package/lib/module/services/apis.js +0 -3
  40. package/lib/module/services/apis.js.map +1 -1
  41. package/lib/module/services/message.js.map +1 -1
  42. package/lib/module/store/message.js.map +1 -1
  43. package/lib/module/store/pinnedMessage.js.map +1 -1
  44. package/lib/module/store/user.js +2 -3
  45. package/lib/module/store/user.js.map +1 -1
  46. package/lib/module/translation/resources/i18n.js +5 -1
  47. package/lib/module/translation/resources/i18n.js.map +1 -1
  48. package/lib/module/types/auth.js +0 -7
  49. package/lib/module/types/auth.js.map +1 -1
  50. package/lib/module/types/chat.js +12 -0
  51. package/lib/module/types/chat.js.map +1 -1
  52. package/lib/module/utils/legendListMessage.js +1 -0
  53. package/lib/module/utils/legendListMessage.js.map +1 -1
  54. package/lib/module/utils/message.js.map +1 -1
  55. package/lib/module/utils/messageUtils.js +47 -6
  56. package/lib/module/utils/messageUtils.js.map +1 -1
  57. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  58. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  59. package/lib/typescript/src/components/messages/quotedMessage/index.d.ts +7 -0
  60. package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -0
  61. package/lib/typescript/src/components/messages/types.d.ts +2 -2
  62. package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
  63. package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
  64. package/lib/typescript/src/context/ChatDetailContext.d.ts.map +1 -1
  65. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  66. package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
  67. package/lib/typescript/src/hooks/message/usePinMessage.d.ts +1 -1
  68. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -1
  69. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  70. package/lib/typescript/src/hooks/useChatMessages.d.ts +2 -2
  71. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  72. package/lib/typescript/src/index.d.ts +1 -1
  73. package/lib/typescript/src/index.d.ts.map +1 -1
  74. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  75. package/lib/typescript/src/screens/chat-detail/ChatCurrentQuotedMessage.d.ts +2 -0
  76. package/lib/typescript/src/screens/chat-detail/ChatCurrentQuotedMessage.d.ts.map +1 -0
  77. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  78. package/lib/typescript/src/screens/chat-detail/PinnedMessageBar.d.ts +2 -2
  79. package/lib/typescript/src/screens/chat-detail/PinnedMessageBar.d.ts.map +1 -1
  80. package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts +1 -2
  81. package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts.map +1 -1
  82. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +2 -3
  83. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  84. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts +6 -4
  85. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts.map +1 -1
  86. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts +4 -4
  87. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts.map +1 -1
  88. package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts +5 -3
  89. package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts.map +1 -1
  90. package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts +2 -2
  91. package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts.map +1 -1
  92. package/lib/typescript/src/screens/chat-detail/types.d.ts +5 -5
  93. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  94. package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
  95. package/lib/typescript/src/services/apis.d.ts +0 -1
  96. package/lib/typescript/src/services/apis.d.ts.map +1 -1
  97. package/lib/typescript/src/services/message.d.ts +1 -2
  98. package/lib/typescript/src/services/message.d.ts.map +1 -1
  99. package/lib/typescript/src/store/message.d.ts.map +1 -1
  100. package/lib/typescript/src/store/pinnedMessage.d.ts +4 -4
  101. package/lib/typescript/src/store/pinnedMessage.d.ts.map +1 -1
  102. package/lib/typescript/src/store/user.d.ts +4 -4
  103. package/lib/typescript/src/store/user.d.ts.map +1 -1
  104. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  105. package/lib/typescript/src/types/auth.d.ts +0 -5
  106. package/lib/typescript/src/types/auth.d.ts.map +1 -1
  107. package/lib/typescript/src/types/chat.d.ts +16 -36
  108. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  109. package/lib/typescript/src/utils/legendListMessage.d.ts +4 -4
  110. package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
  111. package/lib/typescript/src/utils/message.d.ts +4 -5
  112. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  113. package/lib/typescript/src/utils/messageUtils.d.ts +16 -7
  114. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  115. package/package.json +2 -2
  116. package/src/components/ThreadCard/ThreadCard.tsx +5 -1
  117. package/src/components/messages/linkMessage/index.tsx +1 -11
  118. package/src/components/messages/quotedMessage/index.tsx +201 -0
  119. package/src/components/messages/types.ts +2 -2
  120. package/src/context/ChatContext.tsx +30 -25
  121. package/src/context/ChatDetailContext.tsx +9 -3
  122. package/src/core/useChatListener.ts +29 -7
  123. package/src/core/useUserListener.ts +9 -25
  124. package/src/hooks/message/useSendMessage.ts +25 -4
  125. package/src/hooks/useChatMessages.ts +4 -6
  126. package/src/index.tsx +5 -6
  127. package/src/screens/chat-detail/ChatComposer.tsx +2 -0
  128. package/src/screens/chat-detail/ChatCurrentQuotedMessage.tsx +153 -0
  129. package/src/screens/chat-detail/ChatDetail.tsx +3 -2
  130. package/src/screens/chat-detail/ChatListLegend.tsx +3 -3
  131. package/src/screens/chat-detail/PinnedMessageBar.tsx +52 -49
  132. package/src/screens/chat-detail/PinnedMessagesSheet.tsx +7 -8
  133. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +4 -4
  134. package/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.tsx +15 -3
  135. package/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.tsx +15 -6
  136. package/src/screens/chat-detail/messageToolbox/messageToolbox.utils.ts +21 -12
  137. package/src/screens/chat-detail/messageToolbox/types.ts +2 -2
  138. package/src/screens/chat-detail/types.ts +5 -5
  139. package/src/screens/inbox/Inbox.tsx +1 -5
  140. package/src/services/apis.ts +0 -4
  141. package/src/services/message.ts +1 -2
  142. package/src/store/message.ts +3 -3
  143. package/src/store/pinnedMessage.ts +4 -4
  144. package/src/store/user.ts +5 -5
  145. package/src/translation/resources/i18n.ts +4 -0
  146. package/src/types/auth.ts +0 -6
  147. package/src/types/chat.ts +18 -40
  148. package/src/utils/legendListMessage.ts +4 -5
  149. package/src/utils/message.ts +10 -11
  150. package/src/utils/messageUtils.ts +76 -15
@@ -0,0 +1,201 @@
1
+ import { memo, useCallback, useMemo } from 'react';
2
+ import { messageStyles } from '../styles';
3
+ import { formatMessageTime, type BaseLegendMessageProps } from '../types';
4
+ import {
5
+ KColors,
6
+ KContainer,
7
+ KImage,
8
+ KLabel,
9
+ KRadiusValue,
10
+ KSpacingValue,
11
+ } from '@droppii/libs';
12
+ import { StyleSheet } from 'react-native';
13
+ import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
14
+ import { MessageType } from '@droppii/openim-rn-client-sdk';
15
+ import {
16
+ getMessagePreviewParts,
17
+ getReplySenderName,
18
+ parseUrlMetadata,
19
+ } from '../../../utils/messageUtils';
20
+ import { getFileIcon, getMimeType } from '../../../utils/imageUtils';
21
+ import { useUserStore } from '../../../store';
22
+ import { useChatDetailContext } from '../../../context';
23
+
24
+ interface QuotedMessageProps extends BaseLegendMessageProps {
25
+ onPressQuotedMessage?: (clientMsgId: string) => void;
26
+ }
27
+
28
+ const QuotedMessage = memo((props: QuotedMessageProps) => {
29
+ const { message, isOutgoing, onPressQuotedMessage, createdAtTime } =
30
+ props || {};
31
+
32
+ const { conversation } = useChatDetailContext();
33
+
34
+ const currentUserId = useUserStore((s) => s.user?.userID);
35
+
36
+ const quotedMessage = message?.quoteElem?.quoteMessage;
37
+
38
+ const messagePreview = getMessagePreviewParts(quotedMessage);
39
+
40
+ const timeLabel = formatMessageTime(createdAtTime);
41
+
42
+ const imageUrl = useMemo(() => {
43
+ switch (quotedMessage?.contentType) {
44
+ case MessageType.PictureMessage:
45
+ return quotedMessage?.pictureElem?.snapshotPicture?.url || '';
46
+ case MessageType.VideoMessage:
47
+ return quotedMessage?.videoElem?.snapshotUrl || '';
48
+ case MessageType.UrlTextMessage:
49
+ const metadata = parseUrlMetadata(quotedMessage?.ex);
50
+ return metadata?.image || '';
51
+ default:
52
+ return '';
53
+ }
54
+ }, [quotedMessage]);
55
+
56
+ const iconSource = useMemo(() => {
57
+ if (quotedMessage?.contentType !== MessageType.FileMessage) return '';
58
+ const mimeType = getMimeType(quotedMessage?.fileElem?.fileName || '');
59
+ return getFileIcon(mimeType);
60
+ }, [quotedMessage]);
61
+
62
+ const handlePressQuotedMessage = useCallback(() => {
63
+ onPressQuotedMessage?.(quotedMessage?.clientMsgID || '');
64
+ }, [quotedMessage, onPressQuotedMessage]);
65
+
66
+ return (
67
+ <>
68
+ <KContainer.View
69
+ style={[
70
+ messageStyles.bubble,
71
+ styles.container,
72
+ isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
73
+ ]}
74
+ >
75
+ <KContainer.Touchable
76
+ style={styles.quotedContainer}
77
+ onPress={handlePressQuotedMessage}
78
+ >
79
+ <KContainer.View style={styles.dot} />
80
+ <KContainer.VisibleView visible={!!imageUrl}>
81
+ <KContainer.View style={styles.imageContainer}>
82
+ <KImage.Base uri={imageUrl} style={styles.imageContainer} />
83
+ <KContainer.VisibleView
84
+ visible={
85
+ quotedMessage?.contentType === MessageType.VideoMessage
86
+ }
87
+ >
88
+ <KContainer.View style={styles.playIconContainer}>
89
+ <KContainer.View style={styles.playIcon}>
90
+ <KImage.VectorIcons
91
+ name="play-b"
92
+ size={12}
93
+ color={KColors.white}
94
+ />
95
+ </KContainer.View>
96
+ </KContainer.View>
97
+ </KContainer.VisibleView>
98
+ </KContainer.View>
99
+ </KContainer.VisibleView>
100
+ <KContainer.VisibleView visible={!!iconSource}>
101
+ <KImage.Base
102
+ uri={iconSource}
103
+ style={styles.icon}
104
+ resizeMode="contain"
105
+ />
106
+ </KContainer.VisibleView>
107
+ <KContainer.View flexS={1} style={styles.content}>
108
+ <KLabel.Text typo="TextNmMedium" numberOfLines={1}>
109
+ {getReplySenderName(
110
+ quotedMessage,
111
+ currentUserId,
112
+ conversation,
113
+ true
114
+ )}
115
+ </KLabel.Text>
116
+ <KLabel.Text
117
+ typo="TextSmNormal"
118
+ color={KColors.gray.light}
119
+ numberOfLines={1}
120
+ {...messagePreview.textProps}
121
+ >
122
+ {messagePreview.text}
123
+ </KLabel.Text>
124
+ </KContainer.View>
125
+ </KContainer.Touchable>
126
+ <KLabel.Text typo="TextMdNormal" color={CHAT_BUBBLE_COLORS.text}>
127
+ {message?.quoteElem?.text || ''}
128
+ </KLabel.Text>
129
+ </KContainer.View>
130
+
131
+ <KContainer.View row alignItems style={styles.timeLabel}>
132
+ {!isOutgoing && timeLabel ? (
133
+ <KLabel.Text
134
+ typo="TextXsNormal"
135
+ color={CHAT_BUBBLE_COLORS.timestamp}
136
+ marginL="0.25rem"
137
+ >
138
+ {timeLabel}
139
+ </KLabel.Text>
140
+ ) : null}
141
+ </KContainer.View>
142
+ </>
143
+ );
144
+ });
145
+
146
+ QuotedMessage.displayName = 'QuotedMessage';
147
+
148
+ export default QuotedMessage;
149
+
150
+ const styles = StyleSheet.create({
151
+ container: {
152
+ gap: KSpacingValue['0.25rem'],
153
+ },
154
+ quotedContainer: {
155
+ backgroundColor: KColors.white,
156
+ flexDirection: 'row',
157
+ alignItems: 'center',
158
+ gap: KSpacingValue['0.5rem'],
159
+ borderTopRightRadius: KRadiusValue['3x'],
160
+ borderBottomRightRadius: KRadiusValue['3x'],
161
+ paddingRight: KSpacingValue['0.5rem'],
162
+ maxWidth: '100%',
163
+ },
164
+ dot: {
165
+ width: 3,
166
+ height: '100%',
167
+ backgroundColor: KColors.palette.grayCool.w200,
168
+ borderRadius: KRadiusValue.x,
169
+ marginRight: -2,
170
+ },
171
+ content: {
172
+ paddingHorizontal: KSpacingValue['0.25rem'],
173
+ paddingVertical: KSpacingValue['0.25rem'],
174
+ },
175
+ icon: {
176
+ width: 24,
177
+ height: 24,
178
+ },
179
+ imageContainer: {
180
+ width: 42,
181
+ height: 42,
182
+ borderRadius: KRadiusValue['2x'],
183
+ },
184
+ playIconContainer: {
185
+ position: 'absolute',
186
+ top: 0,
187
+ left: 0,
188
+ right: 0,
189
+ bottom: 0,
190
+ justifyContent: 'center',
191
+ alignItems: 'center',
192
+ },
193
+ playIcon: {
194
+ borderRadius: 8,
195
+ padding: KSpacingValue['0.25rem'],
196
+ backgroundColor: KColors.hexToRgba(KColors.black, 0.6),
197
+ justifyContent: 'center',
198
+ alignItems: 'center',
199
+ },
200
+ timeLabel: { gap: 4, marginTop: 2 },
201
+ });
@@ -1,8 +1,8 @@
1
- import type { DMessageItem } from '../../types/chat';
2
1
  import type { MediaItem } from '../../hooks/useMediaViewer';
2
+ import type { MessageItem } from '@droppii/openim-rn-client-sdk';
3
3
 
4
4
  export interface BaseLegendMessageProps {
5
- message: DMessageItem;
5
+ message: MessageItem;
6
6
  isOutgoing: boolean;
7
7
  createdAtTime: number;
8
8
  onMediaPress?: (items: MediaItem[], index?: number) => void;
@@ -12,9 +12,10 @@ import OpenIMSDK, {
12
12
  type ConversationItem,
13
13
  } from '@droppii/openim-rn-client-sdk';
14
14
  import Toast from 'react-native-toast-message';
15
- import { useUserStore } from '../store';
16
15
  import { useChatSdk } from '../core';
17
16
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
17
+ import uuid from 'react-native-uuid';
18
+ import { useUserStore } from '../store';
18
19
 
19
20
  export const ChatContext = createContext<ChatContextType | undefined>(
20
21
  undefined
@@ -35,30 +36,35 @@ const ChatProviderInner = ({
35
36
  getChatToken,
36
37
  onFlashMessagePress,
37
38
  }: ChatProviderProps) => {
38
- const connectStatus = useUserStore((state) => state.connectStatus);
39
39
  const [isShowFLashMessage, setShow] = useState(true);
40
40
 
41
- const handleSdkLogin = useCallback(async () => {
42
- if (!!getChatToken && connectStatus === LoginStatus.Logout) {
43
- const res = await getChatToken();
44
- if (!!res?.id && !!res?.token) {
45
- OpenIMSDK.login({
46
- token: res.token,
47
- userID: res.id,
48
- })
49
- .then(() => {
50
- useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
51
- })
52
- .catch((error) => {
53
- console.error('Failed to login', error);
54
- });
41
+ const handleLoginSdk = useCallback(async () => {
42
+ if (getChatToken) {
43
+ try {
44
+ const res = await getChatToken();
45
+ const loginStatus = await OpenIMSDK.getLoginStatus();
46
+ if (!!res?.id && !!res?.token && loginStatus === LoginStatus.Logout) {
47
+ await OpenIMSDK.login(
48
+ {
49
+ token: res.token,
50
+ userID: res.id,
51
+ },
52
+ uuid.v4()
53
+ );
54
+ const userInfo = await OpenIMSDK.getSelfUserInfo();
55
+ if (userInfo) {
56
+ useUserStore.getState().setUser(userInfo);
57
+ }
58
+ }
59
+ } catch (error) {
60
+ console.error('Failed to login chat sdk', error);
55
61
  }
56
62
  }
57
- }, [getChatToken, connectStatus]);
63
+ }, [getChatToken]);
58
64
 
59
65
  useEffect(() => {
60
- handleSdkLogin();
61
- }, [handleSdkLogin]);
66
+ handleLoginSdk();
67
+ }, [handleLoginSdk]);
62
68
 
63
69
  const onPressFlashMessage = useCallback(
64
70
  (c: ConversationItem) => {
@@ -68,26 +74,25 @@ const ChatProviderInner = ({
68
74
  );
69
75
 
70
76
  const setIsShowFLashMessage = useCallback((v: boolean) => {
71
- console.log('setIsShowFLashMessage');
72
77
  setShow(v);
73
78
  }, []);
74
79
 
75
- const value = useMemo(
80
+ const value: ChatContextType = useMemo(
76
81
  () => ({
77
82
  logGA,
78
83
  applicationType,
79
- getChatToken,
80
84
  onPressFlashMessage,
81
- isShowFLashMessage: isShowFLashMessage,
82
- setIsShowFLashMessage: setIsShowFLashMessage,
85
+ isShowFLashMessage,
86
+ setIsShowFLashMessage,
87
+ handleLoginSdk,
83
88
  }),
84
89
  [
85
90
  logGA,
86
91
  applicationType,
87
- getChatToken,
88
92
  onPressFlashMessage,
89
93
  isShowFLashMessage,
90
94
  setIsShowFLashMessage,
95
+ handleLoginSdk,
91
96
  ]
92
97
  );
93
98
 
@@ -13,7 +13,7 @@ import {
13
13
  } from '../types/chat';
14
14
  import { fechtCurrentConversationFlow } from '../hooks/flows/useCurrentConversationFlow';
15
15
  import { useConversationStore } from '../store';
16
- import { PeerType } from '@droppii/openim-rn-client-sdk';
16
+ import { PeerType, type MessageItem } from '@droppii/openim-rn-client-sdk';
17
17
  import {
18
18
  DFlowContext,
19
19
  NodeTypes,
@@ -27,11 +27,12 @@ import { CONTEXT_BUTTON_PREFIX, isContextButton } from '../utils/flows';
27
27
  export const ChatDetailContext = createContext<ChatDetailContextType>({
28
28
  isBotConversation: false,
29
29
  conversationFlow: undefined,
30
- refetchConversationFlow: () => {},
31
30
  setConverationFlowStep: () => {},
32
31
  flowState: undefined,
33
32
  runButtonContext: () => Promise.resolve(false),
34
33
  conversation: undefined,
34
+ quotedMessage: undefined,
35
+ setQuotedMessage: () => {},
35
36
  });
36
37
 
37
38
  export const useChatDetailContext = (): ChatDetailContextType => {
@@ -55,6 +56,10 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
55
56
  IConversationFlow | undefined
56
57
  >(undefined);
57
58
 
59
+ const [quotedMessage, setQuotedMessage] = useState<MessageItem | undefined>(
60
+ undefined
61
+ );
62
+
58
63
  const flowState = useMemo(() => {
59
64
  return conversationFlow
60
65
  ? conversationFlow?.flowCurrentStep?.data?.children?.some(
@@ -124,12 +129,13 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
124
129
  <ChatDetailContext.Provider
125
130
  value={{
126
131
  conversationFlow,
127
- refetchConversationFlow: () => {},
128
132
  setConverationFlowStep,
129
133
  isBotConversation,
130
134
  flowState,
131
135
  runButtonContext,
132
136
  conversation,
137
+ quotedMessage,
138
+ setQuotedMessage,
133
139
  }}
134
140
  >
135
141
  {children}
@@ -1,5 +1,6 @@
1
1
  import OpenIMSDK, {
2
2
  MessageReceiveOptType,
3
+ MessageType,
3
4
  OpenIMEvent,
4
5
  type ConversationItem,
5
6
  type GroupMemberItem,
@@ -11,12 +12,14 @@ import { useCallback, useEffect } from 'react';
11
12
  import { useConversationStore } from '../store/conversation';
12
13
  import { ToastFlashMessage } from '../utils/toastFlashMessage';
13
14
  import { useUserStore } from '../store/user';
14
- import { getLastMessageText } from '../utils/messageUtils';
15
+ import { getLastMessageText, truncateMiddle } from '../utils/messageUtils';
15
16
  import { useChatContext } from '../context';
16
17
  import isEmpty from 'lodash/isEmpty';
17
18
  import { markMessageRevoked, useMessageStore } from '../store/message';
18
19
  import { syncPinnedMessagesOnEvent } from '../hooks/message/usePinMessage';
19
20
 
21
+ const MAX_TOAST_FILE_NAME_LENGTH = 40;
22
+
20
23
  export const useChatListener = () => {
21
24
  const updateOneMessage = useMessageStore((state) => state.updateOneMessage);
22
25
  const currentUserId = useUserStore((s) => s.user?.userID);
@@ -37,19 +40,37 @@ export const useChatListener = () => {
37
40
 
38
41
  const onConversationChanged = useCallback(
39
42
  (conversations: ConversationItem[]) => {
40
- // console.log('isShowFLashMessage', isShowFLashMessage);
41
- useConversationStore.getState().updateConversations(conversations);
43
+ const oldMap = useConversationStore.getState().map;
42
44
  if (isShowFLashMessage) {
43
45
  for (const conversation of conversations) {
46
+ const oldConversation = oldMap[conversation.conversationID];
44
47
  if (
45
- conversation.recvMsgOpt === MessageReceiveOptType.NotNotify &&
46
- !isEmpty(conversation?.latestMsg)
48
+ conversation.recvMsgOpt === MessageReceiveOptType.NotNotify ||
49
+ isEmpty(conversation?.latestMsg) ||
50
+ oldConversation?.latestMsgSendTime ===
51
+ conversation?.latestMsgSendTime
47
52
  ) {
48
53
  continue;
49
54
  }
50
- const latestMsg = JSON.parse(conversation.latestMsg || '{}');
55
+ const latestMsg: MessageItem = JSON.parse(
56
+ conversation.latestMsg || '{}'
57
+ );
58
+ const messageForToast =
59
+ latestMsg.contentType === MessageType.FileMessage &&
60
+ latestMsg.fileElem?.fileName
61
+ ? {
62
+ ...latestMsg,
63
+ fileElem: {
64
+ ...latestMsg.fileElem,
65
+ fileName: truncateMiddle(
66
+ latestMsg.fileElem.fileName,
67
+ MAX_TOAST_FILE_NAME_LENGTH
68
+ ),
69
+ },
70
+ }
71
+ : latestMsg;
51
72
  const lastMessageText = getLastMessageText(
52
- latestMsg,
73
+ messageForToast,
53
74
  currentUserId,
54
75
  conversation
55
76
  );
@@ -63,6 +84,7 @@ export const useChatListener = () => {
63
84
  },
64
85
  });
65
86
  }
87
+ useConversationStore.getState().updateConversations(conversations);
66
88
  }
67
89
  },
68
90
  [currentUserId, isShowFLashMessage, onPressFlashMessage]
@@ -1,45 +1,28 @@
1
1
  import OpenIMSDK, {
2
- LoginStatus,
3
2
  OpenIMEvent,
4
3
  type OpenIMEventBaseErrorType,
5
4
  } from '@droppii/openim-rn-client-sdk';
6
5
  import { useCallback, useEffect } from 'react';
7
6
  import { useUserStore } from '../store';
8
- import { SyncStatus } from '../types/auth';
9
- import { ChatAPI } from '../services';
7
+ import { ConnectStatus, SyncStatus } from '../types/chat';
8
+ import { useChatContext } from '../context';
10
9
 
11
10
  export const useUserListener = () => {
12
- const isLoggedIn = useUserStore(
13
- (state) => state.connectStatus === LoginStatus.Logged
14
- );
15
-
16
- const fetchAndUpdateUser = useCallback(async () => {
17
- try {
18
- const user = await ChatAPI.getUser();
19
- useUserStore.getState().setUser(user);
20
- } catch (error) {
21
- console.error('OpenIM get user failed', error);
22
- }
23
- }, []);
24
-
25
- useEffect(() => {
26
- if (isLoggedIn) {
27
- fetchAndUpdateUser();
28
- }
29
- }, [isLoggedIn, fetchAndUpdateUser]);
11
+ const { handleLoginSdk } = useChatContext();
30
12
 
31
13
  const onConnecting = useCallback(() => {
32
14
  console.log('[Listener] OnConnecting');
33
- useUserStore.getState().updateConnectStatus(LoginStatus.Logging);
15
+ useUserStore.getState().updateConnectStatus(ConnectStatus.Connecting);
34
16
  }, []);
35
17
 
36
18
  const onConnectSuccess = useCallback(() => {
37
19
  console.log('[Listener] OnConnectSuccess');
38
- useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
20
+ useUserStore.getState().updateConnectStatus(ConnectStatus.Connected);
39
21
  }, []);
40
22
 
41
23
  const onConnectFailed = useCallback((data: OpenIMEventBaseErrorType) => {
42
24
  console.log('[Listener] OnConnectFailed', data);
25
+ useUserStore.getState().updateConnectStatus(ConnectStatus.Disconnected);
43
26
  }, []);
44
27
 
45
28
  const onSyncServerStart = useCallback((isSyncing: boolean) => {
@@ -59,7 +42,8 @@ export const useUserListener = () => {
59
42
 
60
43
  const onUserToken = useCallback(() => {
61
44
  console.log('[Listener] OnUserToken expired/invalid');
62
- }, []);
45
+ handleLoginSdk();
46
+ }, [handleLoginSdk]);
63
47
 
64
48
  useEffect(() => {
65
49
  // User
@@ -88,5 +72,5 @@ export const useUserListener = () => {
88
72
  OpenIMSDK.off(OpenIMEvent.OnSyncServerFailed, onSyncServerFailed);
89
73
  };
90
74
  // eslint-disable-next-line react-hooks/exhaustive-deps
91
- }, []);
75
+ }, [onUserToken]);
92
76
  };
@@ -5,6 +5,7 @@ import type {
5
5
  CustomMsgParams,
6
6
  MessageItem,
7
7
  OfflinePush,
8
+ QuoteMsgParams,
8
9
  } from '@droppii/openim-rn-client-sdk';
9
10
  import { MessageStatus, MessageType } from '@droppii/openim-rn-client-sdk';
10
11
  import { extractUrls } from '../../utils/url';
@@ -14,7 +15,7 @@ import {
14
15
  type CustomMessagePayload,
15
16
  type IMessageItemEx,
16
17
  } from '../../types/chat';
17
- import { useChatContext } from '../../context';
18
+ import { useChatContext, useChatDetailContext } from '../../context';
18
19
  import { generateContentBasedOnMessageType } from '../../utils/conversation';
19
20
  import { markMessageRevoked, useMessageStore } from '../../store/message';
20
21
  import uuid from 'react-native-uuid';
@@ -40,6 +41,8 @@ export const useSendMessage = () => {
40
41
  const pushNewMessage = useMessageStore((state) => state.pushNewMessage);
41
42
  const updateOneMessage = useMessageStore((state) => state.updateOneMessage);
42
43
  const deleteOneMessage = useMessageStore((state) => state.deleteOneMessage);
44
+ const { quotedMessage, setQuotedMessage } = useChatDetailContext();
45
+
43
46
  const dispatchSend = useCallback(
44
47
  async (
45
48
  message: MessageItem,
@@ -91,12 +94,18 @@ export const useSendMessage = () => {
91
94
  const hasPreview = !!urlMetadata?.url;
92
95
 
93
96
  //text msg
94
- if (hasText || hasPreview) {
97
+ if (hasText) {
95
98
  const urls = hasText ? extractUrls(plainText) : [];
96
99
  const isUrlMessage = urls.length > 0 || hasPreview;
97
100
  let message: MessageItem | null = null;
98
101
 
99
- if (isUrlMessage) {
102
+ if (quotedMessage) {
103
+ message = await createQuotedMessage({
104
+ text: plainText || '',
105
+ message: quotedMessage,
106
+ });
107
+ setQuotedMessage(undefined);
108
+ } else if (isUrlMessage) {
100
109
  message = await createUrlTextMessage(plainText || '', urls);
101
110
  } else if (hasText) {
102
111
  message = await createTextMessage(plainText);
@@ -137,7 +146,7 @@ export const useSendMessage = () => {
137
146
  );
138
147
  }
139
148
  },
140
- [applicationType, dispatchSend]
149
+ [applicationType, dispatchSend, quotedMessage, setQuotedMessage]
141
150
  );
142
151
 
143
152
  const retryMessage = useCallback(
@@ -274,3 +283,15 @@ export const createCustomMessage = async (params: CustomMsgParams) => {
274
283
  });
275
284
  return customMessage;
276
285
  };
286
+
287
+ export const createQuotedMessage = async (params: QuoteMsgParams) => {
288
+ let quotedMessage = await OpenIMSDK.createQuoteMessage(params, uuid.v4())
289
+ .then((data) => {
290
+ return data;
291
+ })
292
+ .catch(({ errCode, errMsg }) => {
293
+ console.error('createQuotedMessage', errCode, errMsg);
294
+ return null;
295
+ });
296
+ return quotedMessage;
297
+ };
@@ -4,7 +4,6 @@ import OpenIMSDK, {
4
4
  } from '@droppii/openim-rn-client-sdk';
5
5
  import { useCallback, useEffect, useRef, useState } from 'react';
6
6
  import { ChatMessageAPI } from '../services/message';
7
- import type { DMessageItem } from '../types/chat';
8
7
  import {
9
8
  belongsToConversation,
10
9
  hasNewHistoryMessages,
@@ -71,8 +70,7 @@ export function useChatMessages({
71
70
  }
72
71
 
73
72
  if (historyResult.status === 'fulfilled') {
74
- const initialMessages = historyResult.value
75
- .messageList as DMessageItem[];
73
+ const initialMessages = historyResult.value.messageList;
76
74
  setMessages(
77
75
  mergeMessages([], initialMessages),
78
76
  !historyResult.value.isEnd
@@ -92,7 +90,7 @@ export function useChatMessages({
92
90
  );
93
91
  const localMessages = (localResult?.searchResultItems ?? []).flatMap(
94
92
  (item: any) => item.messageList ?? []
95
- ) as DMessageItem[];
93
+ );
96
94
  console.log(
97
95
  '[useChatMessages] searchLocalMessages fallback result:',
98
96
  localMessages.length,
@@ -126,7 +124,7 @@ export function useChatMessages({
126
124
  }
127
125
  );
128
126
 
129
- const incoming = history.messageList as DMessageItem[];
127
+ const incoming = history.messageList;
130
128
  const hasNew = hasNewHistoryMessages(messages, incoming);
131
129
 
132
130
  if (hasNew) {
@@ -166,7 +164,7 @@ export function useChatMessages({
166
164
  }
167
165
  );
168
166
 
169
- const incoming = history.messageList as DMessageItem[];
167
+ const incoming = history.messageList;
170
168
  const hasNew = incoming.length > 0;
171
169
 
172
170
  if (hasNew) {
package/src/index.tsx CHANGED
@@ -31,12 +31,11 @@ export { Inbox } from './screens/inbox';
31
31
  export { default as MediaViewerScreen } from './screens/MediaView';
32
32
  export * from './screens/chat-detail';
33
33
 
34
- // export type {
35
- // PeerType,
36
- // MessageItem,
37
- // ConversationItem,
38
- // } from '@droppii/openim-rn-client-sdk';
39
- export type { ConversationItem } from '@droppii/openim-rn-client-sdk';
34
+ export type {
35
+ PeerType,
36
+ MessageItem,
37
+ ConversationItem,
38
+ } from '@droppii/openim-rn-client-sdk';
40
39
  export { LoginStatus, MessageType } from '@droppii/openim-rn-client-sdk';
41
40
  export * from './screens/chat-detail';
42
41
  export type {
@@ -32,6 +32,7 @@ import { ReactFlowsNodeTypes } from '../../types/flows';
32
32
  import { useAttachmentMapping } from './hooks/useAttachmentMapping';
33
33
  import type { AttachmentType } from '../../types/attachment';
34
34
  import { useChatCompose } from '../../hooks/useChatCompose';
35
+ import { ChatCurrentQuotedMessage } from './ChatCurrentQuotedMessage';
35
36
 
36
37
  interface ChatComposerInternalProps {
37
38
  isEmptyMessage?: boolean;
@@ -213,6 +214,7 @@ export const ChatComposer = memo(
213
214
 
214
215
  return (
215
216
  <KContainer.View background={'rgba(131, 137, 157, 0.05)'}>
217
+ <ChatCurrentQuotedMessage />
216
218
  <ChatQuickActions
217
219
  visible
218
220
  actions={DEFAULT_QUICK_ACTIONS}