@droppii-org/chat-mobile 0.2.17 → 0.2.19

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 (199) hide show
  1. package/lib/module/assets/images/icon_excel.png +0 -0
  2. package/lib/module/assets/images/icon_img.png +0 -0
  3. package/lib/module/assets/images/icon_pdf.png +0 -0
  4. package/lib/module/assets/images/icon_txt.png +0 -0
  5. package/lib/module/assets/images/icon_word.png +0 -0
  6. package/lib/module/assets/images/index.js +5 -0
  7. package/lib/module/assets/images/index.js.map +1 -1
  8. package/lib/module/components/AttachmentPreview/FilePreview.js +107 -0
  9. package/lib/module/components/AttachmentPreview/FilePreview.js.map +1 -0
  10. package/lib/module/components/AttachmentPreview/MediaPreview.js +212 -0
  11. package/lib/module/components/AttachmentPreview/MediaPreview.js.map +1 -0
  12. package/lib/module/components/AttachmentPreview/index.js +49 -0
  13. package/lib/module/components/AttachmentPreview/index.js.map +1 -0
  14. package/lib/module/components/ThreadCard/AvatarSection.js.map +1 -1
  15. package/lib/module/components/ThreadCard/ThreadCard.js +17 -9
  16. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  17. package/lib/module/components/flows/inputButtons/index.js +7 -3
  18. package/lib/module/components/flows/inputButtons/index.js.map +1 -1
  19. package/lib/module/components/messages/fileMessage/index.js +122 -12
  20. package/lib/module/components/messages/fileMessage/index.js.map +1 -1
  21. package/lib/module/context/ChatContext.js +27 -4
  22. package/lib/module/context/ChatContext.js.map +1 -1
  23. package/lib/module/context/ChatDetailContext.js.map +1 -1
  24. package/lib/module/core/useChatListener.js +2 -4
  25. package/lib/module/core/useChatListener.js.map +1 -1
  26. package/lib/module/core/useUserListener.js +0 -7
  27. package/lib/module/core/useUserListener.js.map +1 -1
  28. package/lib/module/hooks/flows/useFlow.js +14 -3
  29. package/lib/module/hooks/flows/useFlow.js.map +1 -1
  30. package/lib/module/hooks/message/useSendMessage.js +1 -1
  31. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  32. package/lib/module/hooks/useConversationList.js +3 -27
  33. package/lib/module/hooks/useConversationList.js.map +1 -1
  34. package/lib/module/hooks/useFileAttachment.js +116 -0
  35. package/lib/module/hooks/useFileAttachment.js.map +1 -0
  36. package/lib/module/hooks/useFileDownload.js +175 -0
  37. package/lib/module/hooks/useFileDownload.js.map +1 -0
  38. package/lib/module/hooks/useLinkPreview/useLinkPreview.js +1 -1
  39. package/lib/module/hooks/useLinkPreview/useLinkPreview.js.map +1 -1
  40. package/lib/module/index.js +1 -1
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/screens/chat-detail/ChatComposer.js +44 -36
  43. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  44. package/lib/module/screens/chat-detail/ChatDetail.js +1 -6
  45. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  46. package/lib/module/screens/chat-detail/ChatDetailHeader.js +6 -6
  47. package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
  48. package/lib/module/screens/chat-detail/ChatLinkPreview.js +3 -1
  49. package/lib/module/screens/chat-detail/ChatLinkPreview.js.map +1 -1
  50. package/lib/module/screens/chat-detail/ChatListLegend.js +2 -2
  51. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  52. package/lib/module/screens/chat-detail/conversationHeader.utils.js +9 -0
  53. package/lib/module/screens/chat-detail/conversationHeader.utils.js.map +1 -1
  54. package/lib/module/screens/chat-detail/hooks/useAttachmentMapping.js +51 -0
  55. package/lib/module/screens/chat-detail/hooks/useAttachmentMapping.js.map +1 -0
  56. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js +39 -13
  57. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js.map +1 -1
  58. package/lib/module/screens/inbox/Inbox.js +5 -1
  59. package/lib/module/screens/inbox/Inbox.js.map +1 -1
  60. package/lib/module/services/apis.js +0 -20
  61. package/lib/module/services/apis.js.map +1 -1
  62. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js +6 -6
  63. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js.map +1 -1
  64. package/lib/module/services/attachmentHandlers/index.js +1 -1
  65. package/lib/module/services/attachmentHandlers/index.js.map +1 -1
  66. package/lib/module/store/conversation.js +1 -38
  67. package/lib/module/store/conversation.js.map +1 -1
  68. package/lib/module/translation/resources/i18n.js +9 -0
  69. package/lib/module/translation/resources/i18n.js.map +1 -1
  70. package/lib/module/types/auth.js.map +1 -1
  71. package/lib/module/types/chat.js.map +1 -1
  72. package/lib/module/utils/conversation.js +1 -28
  73. package/lib/module/utils/conversation.js.map +1 -1
  74. package/lib/module/utils/device.js +32 -0
  75. package/lib/module/utils/device.js.map +1 -1
  76. package/lib/module/utils/imageUtils.js +44 -28
  77. package/lib/module/utils/imageUtils.js.map +1 -1
  78. package/lib/module/utils/message.js.map +1 -1
  79. package/lib/module/utils/messageUtils.js +5 -3
  80. package/lib/module/utils/messageUtils.js.map +1 -1
  81. package/lib/typescript/src/assets/images/index.d.ts +5 -0
  82. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/AttachmentPreview/FilePreview.d.ts +6 -0
  84. package/lib/typescript/src/components/AttachmentPreview/FilePreview.d.ts.map +1 -0
  85. package/lib/typescript/src/components/AttachmentPreview/MediaPreview.d.ts +9 -0
  86. package/lib/typescript/src/components/AttachmentPreview/MediaPreview.d.ts.map +1 -0
  87. package/lib/typescript/src/components/AttachmentPreview/index.d.ts +9 -0
  88. package/lib/typescript/src/components/AttachmentPreview/index.d.ts.map +1 -0
  89. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +2 -2
  90. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -1
  91. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  92. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
  93. package/lib/typescript/src/context/ChatContext.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/flows/useFlow.d.ts.map +1 -1
  97. package/lib/typescript/src/hooks/useConversationList.d.ts +2 -2
  98. package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -1
  99. package/lib/typescript/src/hooks/useFileAttachment.d.ts +20 -0
  100. package/lib/typescript/src/hooks/useFileAttachment.d.ts.map +1 -0
  101. package/lib/typescript/src/hooks/useFileDownload.d.ts +15 -0
  102. package/lib/typescript/src/hooks/useFileDownload.d.ts.map +1 -0
  103. package/lib/typescript/src/hooks/useImageAttachment.d.ts +1 -2
  104. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
  105. package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts.map +1 -1
  106. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  107. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  108. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  109. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
  110. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
  111. package/lib/typescript/src/screens/chat-detail/ChatLinkPreview.d.ts.map +1 -1
  112. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  113. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts +7 -5
  114. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts.map +1 -1
  115. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentMapping.d.ts +10 -0
  116. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentMapping.d.ts.map +1 -0
  117. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts +4 -1
  118. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts.map +1 -1
  119. package/lib/typescript/src/screens/chat-detail/types.d.ts +6 -9
  120. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  121. package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
  122. package/lib/typescript/src/services/apis.d.ts +0 -4
  123. package/lib/typescript/src/services/apis.d.ts.map +1 -1
  124. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts +2 -2
  125. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts.map +1 -1
  126. package/lib/typescript/src/services/attachmentHandlers/index.d.ts +1 -1
  127. package/lib/typescript/src/services/attachmentHandlers/index.d.ts.map +1 -1
  128. package/lib/typescript/src/store/conversation.d.ts +5 -8
  129. package/lib/typescript/src/store/conversation.d.ts.map +1 -1
  130. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  131. package/lib/typescript/src/types/attachment.d.ts +22 -1
  132. package/lib/typescript/src/types/attachment.d.ts.map +1 -1
  133. package/lib/typescript/src/types/auth.d.ts +1 -0
  134. package/lib/typescript/src/types/auth.d.ts.map +1 -1
  135. package/lib/typescript/src/types/chat.d.ts +15 -13
  136. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  137. package/lib/typescript/src/utils/conversation.d.ts +2 -5
  138. package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
  139. package/lib/typescript/src/utils/device.d.ts +7 -0
  140. package/lib/typescript/src/utils/device.d.ts.map +1 -1
  141. package/lib/typescript/src/utils/imageUtils.d.ts +1 -0
  142. package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -1
  143. package/lib/typescript/src/utils/message.d.ts +3 -3
  144. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  145. package/lib/typescript/src/utils/messageUtils.d.ts +2 -1
  146. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  147. package/package.json +6 -2
  148. package/src/assets/images/icon_excel.png +0 -0
  149. package/src/assets/images/icon_img.png +0 -0
  150. package/src/assets/images/icon_pdf.png +0 -0
  151. package/src/assets/images/icon_txt.png +0 -0
  152. package/src/assets/images/icon_word.png +0 -0
  153. package/src/assets/images/index.ts +5 -0
  154. package/src/components/AttachmentPreview/FilePreview.tsx +137 -0
  155. package/src/components/AttachmentPreview/MediaPreview.tsx +238 -0
  156. package/src/components/AttachmentPreview/index.tsx +68 -0
  157. package/src/components/ThreadCard/AvatarSection.tsx +2 -3
  158. package/src/components/ThreadCard/ThreadCard.tsx +22 -15
  159. package/src/components/flows/inputButtons/index.tsx +4 -1
  160. package/src/components/messages/fileMessage/index.tsx +156 -13
  161. package/src/context/ChatContext.tsx +35 -3
  162. package/src/context/ChatDetailContext.tsx +1 -1
  163. package/src/core/useChatListener.ts +2 -4
  164. package/src/core/useUserListener.ts +0 -8
  165. package/src/hooks/flows/useFlow.ts +12 -3
  166. package/src/hooks/message/useSendMessage.ts +1 -1
  167. package/src/hooks/useConversationList.ts +6 -40
  168. package/src/hooks/useFileAttachment.ts +163 -0
  169. package/src/hooks/useFileDownload.ts +200 -0
  170. package/src/hooks/useImageAttachment.ts +1 -1
  171. package/src/hooks/useLinkPreview/useLinkPreview.ts +6 -1
  172. package/src/screens/chat-detail/ChatComposer.tsx +61 -30
  173. package/src/screens/chat-detail/ChatDetail.tsx +1 -7
  174. package/src/screens/chat-detail/ChatDetailHeader.tsx +6 -7
  175. package/src/screens/chat-detail/ChatLinkPreview.tsx +16 -12
  176. package/src/screens/chat-detail/ChatListLegend.tsx +2 -6
  177. package/src/screens/chat-detail/conversationHeader.utils.ts +16 -6
  178. package/src/screens/chat-detail/hooks/useAttachmentMapping.ts +58 -0
  179. package/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +50 -11
  180. package/src/screens/chat-detail/types.ts +6 -13
  181. package/src/screens/inbox/Inbox.tsx +5 -1
  182. package/src/services/apis.ts +0 -38
  183. package/src/services/attachmentHandlers/fileAttachmentHandler.ts +8 -7
  184. package/src/services/attachmentHandlers/index.ts +4 -1
  185. package/src/store/conversation.ts +6 -55
  186. package/src/translation/resources/i18n.ts +9 -0
  187. package/src/types/attachment.ts +23 -1
  188. package/src/types/auth.ts +1 -0
  189. package/src/types/chat.ts +16 -20
  190. package/src/utils/conversation.ts +2 -40
  191. package/src/utils/device.ts +58 -0
  192. package/src/utils/imageUtils.ts +50 -28
  193. package/src/utils/message.ts +3 -2
  194. package/src/utils/messageUtils.ts +11 -3
  195. package/lib/module/components/AttachmentPreview.js +0 -252
  196. package/lib/module/components/AttachmentPreview.js.map +0 -1
  197. package/lib/typescript/src/components/AttachmentPreview.d.ts +0 -28
  198. package/lib/typescript/src/components/AttachmentPreview.d.ts.map +0 -1
  199. package/src/components/AttachmentPreview.tsx +0 -310
@@ -1,26 +1,145 @@
1
- import { memo } from 'react';
2
- import { KContainer, KLabel } from '@droppii/libs';
1
+ import { memo, useMemo } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
3
4
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
4
5
  import { messageStyles } from '../styles';
5
6
  import type { BaseLegendMessageProps } from '../types';
7
+ import { useFileDownload } from '../../../hooks/useFileDownload';
8
+ import {
9
+ getFileIcon,
10
+ getMimeType,
11
+ formatFileSize,
12
+ } from '../../../utils/imageUtils';
13
+ import { trans } from '../../../translation';
6
14
 
7
15
  export const FileMessage = memo(
8
16
  ({ message, isOutgoing }: BaseLegendMessageProps) => {
9
- const fileName = message.fileElem?.fileName || message.content || '[File]';
17
+ const fileName = message.fileElem?.fileName || '[File]';
18
+ const fileSize = message.fileElem?.fileSize ?? 0;
19
+ const url =
20
+ (message.fileElem as any)?.sourceUrl ||
21
+ (message.fileElem as any)?.url ||
22
+ '';
23
+
24
+ const { state, handleView, handleDownload } = useFileDownload(
25
+ fileName,
26
+ url
27
+ );
28
+
29
+ const mimeType = useMemo(() => getMimeType(fileName), [fileName]);
30
+ const iconSource = useMemo(() => getFileIcon(mimeType), [mimeType]);
31
+
32
+ const subtitle = useMemo(() => {
33
+ if (state.status === 'downloading') {
34
+ return trans('chat:file_downloading', {
35
+ received: state.receivedMB ?? 0,
36
+ total: state.totalMB ?? 0,
37
+ });
38
+ }
39
+ if (state.status === 'error') {
40
+ return state.error ?? trans('chat:file_download_failed');
41
+ }
42
+ return formatFileSize(fileSize);
43
+ }, [state, fileSize]);
44
+
45
+ const subtitleColor =
46
+ state.status === 'error'
47
+ ? (KColors.palette.danger?.w400 ?? '#FF3B30')
48
+ : KColors.palette.gray.w500;
49
+
50
+ const isDownloading = state.status === 'downloading';
10
51
 
11
52
  return (
12
- <KContainer.View style={messageStyles.wrapper}>
53
+ <KContainer.View
54
+ padding={'0.5rem'}
55
+ style={[
56
+ styles.wrapper,
57
+ messageStyles.wrapper,
58
+ isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
59
+ ]}
60
+ >
13
61
  <KContainer.View
14
- style={[
15
- messageStyles.bubble,
16
- isOutgoing
17
- ? messageStyles.bubbleSent
18
- : messageStyles.bubbleReceived,
19
- ]}
62
+ row
63
+ alignItems
64
+ br={'3x'}
65
+ paddingR={'0.75rem'}
66
+ paddingV={'0.25rem'}
67
+ background={KColors.white}
20
68
  >
21
- <KLabel.Text typo="TextSmNormal" color={CHAT_BUBBLE_COLORS.text}>
22
- 📎 {fileName}
23
- </KLabel.Text>
69
+ <KContainer.VisibleView visible={isDownloading}>
70
+ <KContainer.Touchable
71
+ center
72
+ br="round"
73
+ style={styles.cancelBtn}
74
+ width={32}
75
+ height={32}
76
+ onPress={handleDownload}
77
+ >
78
+ <KContainer.View
79
+ center
80
+ br="round"
81
+ style={styles.cancelBtnIconContainer}
82
+ width={26}
83
+ height={26}
84
+ >
85
+ <KImage.VectorIcons
86
+ name="close"
87
+ provider="MaterialCommunityIcons"
88
+ size={24}
89
+ color={KColors.white}
90
+ />
91
+ </KContainer.View>
92
+ </KContainer.Touchable>
93
+ </KContainer.VisibleView>
94
+ <KContainer.VisibleView visible={!isDownloading}>
95
+ <KImage.Base
96
+ uri={iconSource}
97
+ style={styles.fileIcon}
98
+ resizeMode="contain"
99
+ />
100
+ </KContainer.VisibleView>
101
+
102
+ {/* File name + size/progress */}
103
+ <KContainer.View flexS={1}>
104
+ <KLabel.Text
105
+ typo="TextSmMedium"
106
+ color={CHAT_BUBBLE_COLORS.text}
107
+ numberOfLines={1}
108
+ ellipsizeMode="middle"
109
+ >
110
+ {fileName}
111
+ </KLabel.Text>
112
+ <KLabel.Text
113
+ typo="TextXsNormal"
114
+ color={subtitleColor}
115
+ numberOfLines={1}
116
+ >
117
+ {subtitle}
118
+ </KLabel.Text>
119
+ </KContainer.View>
120
+
121
+ {/* Eye (view) button */}
122
+ <KImage.VectorIcons
123
+ name="eye-outline"
124
+ provider="MaterialCommunityIcons"
125
+ onPress={handleView}
126
+ size={20}
127
+ color={KColors.palette.gray.w600}
128
+ containerStyle={styles.viewIcon}
129
+ />
130
+
131
+ <KImage.VectorIcons
132
+ name={'download-o'}
133
+ provider="DroppiiNew"
134
+ onPress={isDownloading ? undefined : handleDownload}
135
+ size={20}
136
+ color={
137
+ state.status === 'error'
138
+ ? (KColors.palette.danger?.w400 ?? '#FF3B30')
139
+ : KColors.palette.gray.w600
140
+ }
141
+ containerStyle={isDownloading ? styles.disabledIcon : undefined}
142
+ />
24
143
  </KContainer.View>
25
144
  </KContainer.View>
26
145
  );
@@ -28,3 +147,27 @@ export const FileMessage = memo(
28
147
  );
29
148
 
30
149
  FileMessage.displayName = 'FileMessage';
150
+
151
+ const styles = StyleSheet.create({
152
+ wrapper: {
153
+ borderRadius: 20,
154
+ },
155
+ fileIcon: {
156
+ width: 24,
157
+ height: 24,
158
+ marginHorizontal: 12,
159
+ },
160
+ cancelBtn: {
161
+ backgroundColor: KColors.hexToRgba('#6355CA', 0.3),
162
+ marginHorizontal: 6,
163
+ },
164
+ cancelBtnIconContainer: {
165
+ backgroundColor: '#6355CA',
166
+ },
167
+ viewIcon: {
168
+ marginHorizontal: 12,
169
+ },
170
+ disabledIcon: {
171
+ opacity: 0.3,
172
+ },
173
+ });
@@ -1,5 +1,13 @@
1
- import { createContext, useContext, useMemo } from 'react';
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ } from 'react';
2
8
  import type { ChatContextType, ChatProviderProps } from '../types/chat';
9
+ import OpenIMSDK, { LoginStatus } from '@droppii/openim-rn-client-sdk';
10
+ import { useUserStore } from '../store';
3
11
 
4
12
  export const ChatContext = createContext<ChatContextType | undefined>(
5
13
  undefined
@@ -17,12 +25,36 @@ const ChatProviderInner = ({
17
25
  logGA,
18
26
  children,
19
27
  applicationType,
28
+ getChatToken,
20
29
  }: ChatProviderProps) => {
30
+ const connectStatus = useUserStore((state) => state.connectStatus);
21
31
  const value = useMemo(
22
- () => ({ logGA, applicationType }),
23
- [logGA, applicationType]
32
+ () => ({ logGA, applicationType, getChatToken }),
33
+ [logGA, applicationType, getChatToken]
24
34
  );
25
35
 
36
+ const handleSdkLogin = useCallback(async () => {
37
+ if (!!getChatToken && connectStatus === LoginStatus.Logout) {
38
+ const res = await getChatToken();
39
+ if (!!res?.id && !!res?.token) {
40
+ OpenIMSDK.login({
41
+ token: res.token,
42
+ userID: res.id,
43
+ })
44
+ .then(() => {
45
+ useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
46
+ })
47
+ .catch((error) => {
48
+ console.error('Failed to login', error);
49
+ });
50
+ }
51
+ }
52
+ }, [getChatToken, connectStatus]);
53
+
54
+ useEffect(() => {
55
+ handleSdkLogin();
56
+ }, [handleSdkLogin]);
57
+
26
58
  return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
27
59
  };
28
60
 
@@ -47,7 +47,7 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
47
47
  return conversation?.peerType === PeerType.Bot;
48
48
  }, [conversation]);
49
49
 
50
- const setConverationFlowStep = useCallback((step: IConversationFlowStep) => {
50
+ const setConverationFlowStep = useCallback((step?: IConversationFlowStep) => {
51
51
  setConverationFlow(
52
52
  (prev) =>
53
53
  ({
@@ -10,16 +10,14 @@ export const useChatListener = () => {
10
10
  const onConversationChanged = useCallback(
11
11
  (conversations: ConversationItem[]) => {
12
12
  console.log('onConversationChanged', conversations);
13
- useConversationStore.getState().mergeOpenIMConversations(conversations);
13
+ useConversationStore.getState().updateConversations(conversations);
14
14
  },
15
15
  []
16
16
  );
17
17
 
18
18
  const onNewConversation = useCallback((conversations: ConversationItem[]) => {
19
19
  console.log('onNewConversation', conversations);
20
- // mergeOpenIMConversations increments newConversationSignal when a conversation
21
- // isn't in the store yet, which triggers a full refetch in useConversationList.
22
- useConversationStore.getState().mergeOpenIMConversations(conversations);
20
+ useConversationStore.getState().updateConversations(conversations);
23
21
  }, []);
24
22
 
25
23
  const onGroupMemberAdded = useCallback((member: GroupMemberItem) => {
@@ -21,14 +21,6 @@ export const useUserListener = () => {
21
21
  }
22
22
  }, []);
23
23
 
24
- useEffect(() => {
25
- ChatAPI.getLoginStatus().then((value) => {
26
- if (value) {
27
- useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
28
- }
29
- });
30
- }, []);
31
-
32
24
  useEffect(() => {
33
25
  if (isLoggedIn) {
34
26
  fetchAndUpdateUser();
@@ -11,14 +11,25 @@ import type {
11
11
  } from '../../types/flows';
12
12
  import { useSendMessage } from '../message/useSendMessage';
13
13
  import { useChatDetailContext } from '../../context/ChatDetailContext';
14
+ import { trans } from '../../translation';
14
15
 
15
16
  export const useResetFlow = () => {
16
17
  const conversationId =
17
18
  useConversationStore((state) => state.currentConversationId) || '';
18
19
  const { mutate: runFlow } = useRunFlow();
20
+ const { sendMessage } = useSendMessage();
21
+ const conversationFlow = useChatDetailContext().conversationFlow;
19
22
  return useMutation({
20
23
  mutationKey: conversationQueryKeys.flow(conversationId),
21
24
  mutationFn: async () => {
25
+ await sendMessage({
26
+ plainText: trans('restart'),
27
+ flow: {
28
+ groupId: conversationFlow?.flowCurrentStep?.id || '',
29
+ childId: null,
30
+ actionId: null,
31
+ },
32
+ });
22
33
  const res = await apiInstance?.post<BaseResponse<IConversationFlow>>(
23
34
  ENDPOINTS.chatService.resetConversationFlow(conversationId),
24
35
  {}
@@ -55,9 +66,7 @@ export const useRunFlow = () => {
55
66
  return res?.data?.data || undefined;
56
67
  },
57
68
  onSuccess: (data) => {
58
- if (data?.flowNextStep) {
59
- setConverationFlowStep(data?.flowNextStep);
60
- }
69
+ setConverationFlowStep(data?.flowNextStep || undefined);
61
70
  },
62
71
  });
63
72
  };
@@ -106,7 +106,7 @@ const generateOfflinePushInfo = (
106
106
  desc,
107
107
  ex: JSON.stringify({
108
108
  icon: conversationData?.faceURL || '',
109
- conversationId: conversationData?.conversationId || '',
109
+ conversationId: conversationData?.conversationID || '',
110
110
  title,
111
111
  desc,
112
112
  }),
@@ -1,15 +1,9 @@
1
1
  import { useEffect } from 'react';
2
2
  import { useQuery } from '@tanstack/react-query';
3
- import OpenIMSDK, {
4
- type ConversationItem,
5
- } from '@droppii/openim-rn-client-sdk';
3
+ import OpenIMSDK from '@droppii/openim-rn-client-sdk';
6
4
  import { conversationQueryKeys } from './query-keys';
7
5
  import { useConversationStore } from '../store';
8
- import type {
9
- DChatApplicationType,
10
- DConversationItem,
11
- DMessageItem,
12
- } from '../types/chat';
6
+ import type { DChatApplicationType } from '../types/chat';
13
7
 
14
8
  type Params = {
15
9
  applicationType: DChatApplicationType;
@@ -17,34 +11,6 @@ type Params = {
17
11
  enabled?: boolean;
18
12
  };
19
13
 
20
- function toConversationItem(raw: ConversationItem): DConversationItem {
21
- let lastMessage: DMessageItem | undefined;
22
- if (raw.latestMsg) {
23
- try {
24
- lastMessage = JSON.parse(raw.latestMsg) as DMessageItem;
25
- } catch {
26
- // leave undefined
27
- }
28
- }
29
-
30
- let applicationType: DChatApplicationType | undefined;
31
- if (raw.ex) {
32
- try {
33
- applicationType = (
34
- JSON.parse(raw.ex) as { applicationType?: DChatApplicationType }
35
- ).applicationType;
36
- } catch {
37
- // leave undefined
38
- }
39
- }
40
-
41
- return {
42
- ...(raw as DConversationItem),
43
- lastMessage,
44
- applicationType: applicationType as DChatApplicationType,
45
- };
46
- }
47
-
48
14
  export function useConversationList({
49
15
  applicationType,
50
16
  page = 1,
@@ -58,13 +24,13 @@ export function useConversationList({
58
24
  queryKey: [...conversationQueryKeys.list(applicationType), page],
59
25
  enabled: enabled !== false,
60
26
  queryFn: async () => {
61
- const offset = (page - 1) * 20;
62
- const rawList = await OpenIMSDK.getConversationListSplitApp({
27
+ const offset = (page - 1) * 500;
28
+ const conversations = await OpenIMSDK.getConversationListSplitApp({
63
29
  offset,
64
- count: 20,
30
+ count: 500,
65
31
  applicationType,
66
32
  });
67
- const conversations = rawList.map(toConversationItem);
33
+
68
34
  useConversationStore.getState().updateConversations(conversations);
69
35
  return conversations;
70
36
  },
@@ -0,0 +1,163 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { ImageUploadAPI } from '../services/imageUpload';
3
+ import DeviceUtils from '../utils/device';
4
+ import UIUtils from '../utils/ui';
5
+ import { trans } from '../translation';
6
+ import { MAX_FILE_SIZE } from '../services/attachmentHandlers';
7
+
8
+ const MAX_FILE_COUNT = 3;
9
+
10
+ export interface FileWithUploadStatus {
11
+ id: string;
12
+ uri: string;
13
+ name: string;
14
+ size: number;
15
+ mimeType: string;
16
+ uploadProgress: number;
17
+ error: string | null;
18
+ isUploading: boolean;
19
+ uploadedUrl?: string;
20
+ }
21
+
22
+ export interface UseFileAttachmentReturn {
23
+ selectedFiles: FileWithUploadStatus[];
24
+ isFilePreviewVisible: boolean;
25
+ handleFileAttach: () => Promise<void>;
26
+ handleFileRemove: (id: string) => void;
27
+ clearAllFiles: () => void;
28
+ }
29
+
30
+ export const useFileAttachment = (): UseFileAttachmentReturn => {
31
+ const [selectedFiles, setSelectedFiles] = useState<FileWithUploadStatus[]>(
32
+ []
33
+ );
34
+
35
+ const updateFileStatus = useCallback(
36
+ (id: string, updates: Partial<Omit<FileWithUploadStatus, 'id'>>) => {
37
+ setSelectedFiles((prev) =>
38
+ prev.map((f) => (f.id === id ? { ...f, ...updates } : f))
39
+ );
40
+ },
41
+ []
42
+ );
43
+
44
+ const uploadFile = useCallback(
45
+ async (file: FileWithUploadStatus) => {
46
+ try {
47
+ updateFileStatus(file.id, {
48
+ isUploading: true,
49
+ error: null,
50
+ uploadProgress: 0,
51
+ });
52
+
53
+ const uploaded = await ImageUploadAPI.uploadImageFile(
54
+ {
55
+ path: file.uri.startsWith('file://') ? file.uri.slice(7) : file.uri,
56
+ filename: file.name,
57
+ size: file.size,
58
+ width: 0,
59
+ height: 0,
60
+ mime: file.mimeType,
61
+ },
62
+ (progress) => updateFileStatus(file.id, { uploadProgress: progress })
63
+ );
64
+
65
+ if (!uploaded?.url) throw new Error('No URL returned from server');
66
+
67
+ updateFileStatus(file.id, {
68
+ isUploading: false,
69
+ uploadProgress: 100,
70
+ uploadedUrl: uploaded.url,
71
+ });
72
+ } catch (error: any) {
73
+ // TODO: handle upload failure
74
+ console.error('[FileAttach] Upload failed:', error);
75
+ updateFileStatus(file.id, {
76
+ isUploading: false,
77
+ error: error?.message ?? trans('chat:file_upload_failed'),
78
+ });
79
+ }
80
+ },
81
+ [updateFileStatus]
82
+ );
83
+
84
+ const handleFileAttach = useCallback(async () => {
85
+ try {
86
+ const currentCount = selectedFiles.length;
87
+ if (currentCount >= MAX_FILE_COUNT) {
88
+ UIUtils.toast.open({
89
+ title: trans('chat:file_max_count'),
90
+ theme: 'danger',
91
+ });
92
+ return;
93
+ }
94
+
95
+ const picked = await DeviceUtils.openFilePicker(true);
96
+ if (!picked || picked.length === 0) return;
97
+
98
+ const oversized = picked.filter((f) => f.size > MAX_FILE_SIZE);
99
+ let candidates = picked;
100
+ if (oversized.length > 0) {
101
+ UIUtils.toast.open({
102
+ title: trans('chat:file_too_large'),
103
+ theme: 'danger',
104
+ });
105
+ candidates = picked.filter((f) => f.size <= MAX_FILE_SIZE);
106
+ if (candidates.length === 0) return;
107
+ }
108
+
109
+ const deduplicated = candidates.filter(
110
+ (f) =>
111
+ !selectedFiles.some((s) => s.name === f.name && s.size === f.size)
112
+ );
113
+ if (deduplicated.length === 0) return;
114
+
115
+ const remaining = MAX_FILE_COUNT - currentCount;
116
+ const toAdd = deduplicated.slice(0, remaining);
117
+
118
+ if (toAdd.length < deduplicated.length) {
119
+ UIUtils.toast.open({
120
+ title: trans('chat:file_max_count'),
121
+ theme: 'danger',
122
+ });
123
+ }
124
+
125
+ const newFiles: FileWithUploadStatus[] = toAdd.map((f) => ({
126
+ id: `file-${Date.now()}-${Math.random().toString(36).slice(2)}`,
127
+ uri: f.uri,
128
+ name: f.name,
129
+ size: f.size,
130
+ mimeType: f.mimeType,
131
+ uploadProgress: 0,
132
+ error: null,
133
+ isUploading: true,
134
+ }));
135
+
136
+ setSelectedFiles((prev) => [...prev, ...newFiles]);
137
+
138
+ await Promise.all(newFiles.map((f) => uploadFile(f)));
139
+ } catch (error: any) {
140
+ UIUtils.toast.open({
141
+ title: trans('chat:file_pick_failed'),
142
+ message: error instanceof Error ? error.message : 'Unknown error',
143
+ theme: 'danger',
144
+ });
145
+ }
146
+ }, [selectedFiles, uploadFile]);
147
+
148
+ const handleFileRemove = useCallback((id: string) => {
149
+ setSelectedFiles((prev) => prev.filter((f) => f.id !== id));
150
+ }, []);
151
+
152
+ const clearAllFiles = useCallback(() => {
153
+ setSelectedFiles([]);
154
+ }, []);
155
+
156
+ return {
157
+ selectedFiles,
158
+ isFilePreviewVisible: selectedFiles.length > 0,
159
+ handleFileAttach,
160
+ handleFileRemove,
161
+ clearAllFiles,
162
+ };
163
+ };