@droppii-org/chat-mobile 0.2.23 → 0.2.25

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 (194) 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/messages/MessageStatusIndicator.js +70 -0
  7. package/lib/module/components/messages/MessageStatusIndicator.js.map +1 -0
  8. package/lib/module/components/messages/fileMessage/index.js.map +1 -1
  9. package/lib/module/components/messages/imageMessage/index.js +59 -50
  10. package/lib/module/components/messages/imageMessage/index.js.map +1 -1
  11. package/lib/module/components/messages/linkMessage/index.js +14 -6
  12. package/lib/module/components/messages/linkMessage/index.js.map +1 -1
  13. package/lib/module/components/messages/mergedMessage/index.js.map +1 -1
  14. package/lib/module/components/messages/textMessage/index.js +14 -6
  15. package/lib/module/components/messages/textMessage/index.js.map +1 -1
  16. package/lib/module/components/messages/videoMessage/index.js.map +1 -1
  17. package/lib/module/config/uploadLimits.js +13 -0
  18. package/lib/module/config/uploadLimits.js.map +1 -0
  19. package/lib/module/core/useChatListener.js +35 -4
  20. package/lib/module/core/useChatListener.js.map +1 -1
  21. package/lib/module/core/useUserListener.js +17 -7
  22. package/lib/module/core/useUserListener.js.map +1 -1
  23. package/lib/module/hooks/message/useSendMessage.js +62 -15
  24. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  25. package/lib/module/hooks/useChatCompose.js +153 -0
  26. package/lib/module/hooks/useChatCompose.js.map +1 -0
  27. package/lib/module/hooks/useChatMessages.js +39 -8
  28. package/lib/module/hooks/useChatMessages.js.map +1 -1
  29. package/lib/module/hooks/useFileAttachment.js +1 -1
  30. package/lib/module/hooks/useFileAttachment.js.map +1 -1
  31. package/lib/module/hooks/useFileDownload.js +1 -1
  32. package/lib/module/hooks/useFileDownload.js.map +1 -1
  33. package/lib/module/hooks/useImageAttachment.js +81 -152
  34. package/lib/module/hooks/useImageAttachment.js.map +1 -1
  35. package/lib/module/hooks/useSendAttachment.js +2 -2
  36. package/lib/module/hooks/useSendAttachment.js.map +1 -1
  37. package/lib/module/index.js +1 -1
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/screens/chat-detail/ChatComposer.js +50 -185
  40. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  41. package/lib/module/screens/chat-detail/ChatDetail.js +29 -137
  42. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  43. package/lib/module/screens/chat-detail/ChatListLegend.js +20 -15
  44. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  45. package/lib/module/screens/chat-detail/index.js +0 -1
  46. package/lib/module/screens/chat-detail/index.js.map +1 -1
  47. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +18 -13
  48. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  49. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js +2 -2
  50. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js.map +1 -1
  51. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js +3 -3
  52. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js.map +1 -1
  53. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js +2 -1
  54. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js.map +1 -1
  55. package/lib/module/services/attachmentOrchestrator.js +40 -17
  56. package/lib/module/services/attachmentOrchestrator.js.map +1 -1
  57. package/lib/module/services/message.js +13 -5
  58. package/lib/module/services/message.js.map +1 -1
  59. package/lib/module/translation/resources/i18n.js +4 -3
  60. package/lib/module/translation/resources/i18n.js.map +1 -1
  61. package/lib/module/types/chat.js.map +1 -1
  62. package/lib/module/types/global.d.js +2 -0
  63. package/lib/module/types/global.d.js.map +1 -0
  64. package/lib/module/types/index.d.js +3 -0
  65. package/lib/module/types/index.d.js.map +1 -0
  66. package/lib/module/utils/UIUtils.js +37 -0
  67. package/lib/module/utils/UIUtils.js.map +1 -0
  68. package/lib/module/utils/imageUtils.js +3 -3
  69. package/lib/module/utils/imageUtils.js.map +1 -1
  70. package/lib/module/utils/pLimit.js +19 -0
  71. package/lib/module/utils/pLimit.js.map +1 -0
  72. package/lib/module/utils/videoThumbnail.js +8 -3
  73. package/lib/module/utils/videoThumbnail.js.map +1 -1
  74. package/lib/module/utils/videoThumbnailUpload.js +76 -0
  75. package/lib/module/utils/videoThumbnailUpload.js.map +1 -0
  76. package/lib/typescript/src/assets/images/index.d.ts +1 -0
  77. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  78. package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts +9 -0
  79. package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts.map +1 -0
  80. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
  81. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -1
  82. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +3 -8
  83. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  84. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -1
  85. package/lib/typescript/src/components/messages/textMessage/index.d.ts +2 -8
  86. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
  87. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -1
  88. package/lib/typescript/src/config/uploadLimits.d.ts +7 -0
  89. package/lib/typescript/src/config/uploadLimits.d.ts.map +1 -0
  90. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  91. package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
  92. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -0
  93. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  94. package/lib/typescript/src/hooks/useChatCompose.d.ts +27 -0
  95. package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -0
  96. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  97. package/lib/typescript/src/hooks/useImageAttachment.d.ts +2 -2
  98. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
  99. package/lib/typescript/src/hooks/useSendAttachment.d.ts +1 -1
  100. package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -1
  101. package/lib/typescript/src/index.d.ts +1 -2
  102. package/lib/typescript/src/index.d.ts.map +1 -1
  103. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +5 -2
  104. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  105. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  106. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  107. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
  108. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  109. package/lib/typescript/src/screens/chat-detail/index.d.ts +0 -2
  110. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
  111. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +3 -5
  112. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  113. package/lib/typescript/src/screens/chat-detail/types.d.ts +1 -35
  114. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  115. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts.map +1 -1
  116. package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts.map +1 -1
  117. package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts.map +1 -1
  118. package/lib/typescript/src/services/attachmentOrchestrator.d.ts +1 -1
  119. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
  120. package/lib/typescript/src/services/message.d.ts +7 -0
  121. package/lib/typescript/src/services/message.d.ts.map +1 -1
  122. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  123. package/lib/typescript/src/types/chat.d.ts +11 -0
  124. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  125. package/lib/typescript/src/utils/UIUtils.d.ts +4 -0
  126. package/lib/typescript/src/utils/UIUtils.d.ts.map +1 -0
  127. package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -1
  128. package/lib/typescript/src/utils/pLimit.d.ts +6 -0
  129. package/lib/typescript/src/utils/pLimit.d.ts.map +1 -0
  130. package/lib/typescript/src/utils/videoThumbnail.d.ts.map +1 -1
  131. package/lib/typescript/src/utils/videoThumbnailUpload.d.ts +11 -0
  132. package/lib/typescript/src/utils/videoThumbnailUpload.d.ts.map +1 -0
  133. package/package.json +2 -2
  134. package/src/assets/images/btn_resend.png +0 -0
  135. package/src/assets/images/index.ts +1 -0
  136. package/src/components/AttachmentPreview/FilePreview.tsx +1 -1
  137. package/src/components/messages/MessageStatusIndicator.tsx +82 -0
  138. package/src/components/messages/fileMessage/index.tsx +0 -1
  139. package/src/components/messages/imageMessage/index.tsx +71 -57
  140. package/src/components/messages/linkMessage/index.tsx +13 -19
  141. package/src/components/messages/mergedMessage/index.tsx +0 -1
  142. package/src/components/messages/textMessage/index.tsx +13 -18
  143. package/src/components/messages/videoMessage/index.tsx +0 -1
  144. package/src/config/uploadLimits.ts +11 -0
  145. package/src/core/useChatListener.ts +38 -4
  146. package/src/core/useUserListener.ts +17 -7
  147. package/src/hooks/message/useSendMessage.ts +103 -24
  148. package/src/hooks/useChatCompose.ts +195 -0
  149. package/src/hooks/useChatMessages.ts +54 -10
  150. package/src/hooks/useFileAttachment.ts +1 -1
  151. package/src/hooks/useFileDownload.ts +1 -1
  152. package/src/hooks/useImageAttachment.ts +120 -199
  153. package/src/hooks/useSendAttachment.ts +3 -6
  154. package/src/index.tsx +3 -5
  155. package/src/screens/chat-detail/ChatComposer.tsx +88 -240
  156. package/src/screens/chat-detail/ChatDetail.tsx +36 -195
  157. package/src/screens/chat-detail/ChatListLegend.tsx +16 -13
  158. package/src/screens/chat-detail/index.ts +1 -5
  159. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +19 -12
  160. package/src/screens/chat-detail/types.ts +1 -38
  161. package/src/services/attachmentHandlers/fileAttachmentHandler.ts +3 -2
  162. package/src/services/attachmentHandlers/imageAttachmentHandler.ts +6 -3
  163. package/src/services/attachmentHandlers/videoAttachmentHandler.ts +2 -1
  164. package/src/services/attachmentOrchestrator.ts +49 -27
  165. package/src/services/message.ts +21 -4
  166. package/src/translation/resources/i18n.ts +6 -3
  167. package/src/types/chat.ts +6 -0
  168. package/src/types/global.d.ts +299 -0
  169. package/src/types/index.d.ts +1 -0
  170. package/src/utils/UIUtils.ts +31 -0
  171. package/src/utils/imageUtils.ts +3 -4
  172. package/src/utils/pLimit.ts +20 -0
  173. package/src/utils/videoThumbnail.ts +10 -3
  174. package/src/utils/videoThumbnailUpload.ts +103 -0
  175. package/lib/module/hooks/useVideoAttachment.js +0 -251
  176. package/lib/module/hooks/useVideoAttachment.js.map +0 -1
  177. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js +0 -288
  178. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js.map +0 -1
  179. package/lib/module/utils/giftedChatMessage.js +0 -90
  180. package/lib/module/utils/giftedChatMessage.js.map +0 -1
  181. package/lib/module/utils/ui.js +0 -17
  182. package/lib/module/utils/ui.js.map +0 -1
  183. package/lib/typescript/src/hooks/useVideoAttachment.d.ts +0 -29
  184. package/lib/typescript/src/hooks/useVideoAttachment.d.ts.map +0 -1
  185. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts +0 -26
  186. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts.map +0 -1
  187. package/lib/typescript/src/utils/giftedChatMessage.d.ts +0 -10
  188. package/lib/typescript/src/utils/giftedChatMessage.d.ts.map +0 -1
  189. package/lib/typescript/src/utils/ui.d.ts +0 -13
  190. package/lib/typescript/src/utils/ui.d.ts.map +0 -1
  191. package/src/hooks/useVideoAttachment.ts +0 -334
  192. package/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +0 -386
  193. package/src/utils/giftedChatMessage.ts +0 -137
  194. package/src/utils/ui.ts +0 -19
@@ -1,4 +1,8 @@
1
- import OpenIMSDK, { type MessageItem } from '@droppii/openim-rn-client-sdk';
1
+ import OpenIMSDK, {
2
+ MessageStatus,
3
+ type MessageItem,
4
+ } from '@droppii/openim-rn-client-sdk';
5
+
2
6
  import type {
3
7
  Attachment,
4
8
  AttachmentSendOptions,
@@ -6,6 +10,7 @@ import type {
6
10
  } from '../types/attachment';
7
11
  import { getAttachmentHandler } from '../types/attachmentHandler';
8
12
  import { useConversationStore } from '../store';
13
+ import { useMessageStore } from '../store/message';
9
14
  import {
10
15
  getAttachmentPriorityConfig,
11
16
  type AttachmentType,
@@ -15,8 +20,7 @@ export namespace AttachmentOrchestratorService {
15
20
  export async function sendPreparedAttachments(
16
21
  attachments: Attachment[],
17
22
  uploadedUrls: Record<string, string>,
18
- options: AttachmentSendOptions = { strategy: 'individual' },
19
- onMessageSent?: (message: any) => void
23
+ options: AttachmentSendOptions = { strategy: 'individual' }
20
24
  ): Promise<AttachmentSendResult> {
21
25
  const result: AttachmentSendResult = {
22
26
  success: false,
@@ -30,7 +34,6 @@ export namespace AttachmentOrchestratorService {
30
34
  }
31
35
 
32
36
  // 1. Validate all attachments
33
- console.log('[AttachmentOrchestrator] Validating prepared attachments...');
34
37
  for (const att of attachments) {
35
38
  try {
36
39
  const handler = getAttachmentHandler(att.type);
@@ -90,8 +93,7 @@ export namespace AttachmentOrchestratorService {
90
93
  try {
91
94
  const mergedMsgId = await sendMergedMessage(
92
95
  payloads.map((p) => p.payload),
93
- options.includeCaption,
94
- onMessageSent
96
+ options.includeCaption
95
97
  );
96
98
  console.log('[AttachmentOrchestrator] Messages sent via merger:', {
97
99
  messageId: mergedMsgId,
@@ -116,8 +118,7 @@ export namespace AttachmentOrchestratorService {
116
118
 
117
119
  async function sendMergedMessage(
118
120
  payloads: unknown[],
119
- _caption?: string,
120
- onMessageSent?: (message: any) => void
121
+ _caption?: string
121
122
  ): Promise<string> {
122
123
  const currentConversation = useConversationStore
123
124
  .getState()
@@ -269,32 +270,53 @@ export namespace AttachmentOrchestratorService {
269
270
  }
270
271
  );
271
272
 
272
- for (let i = 0; i < allMessages.length; i++) {
273
- const message = allMessages[i] as MessageItem;
274
- console.log(
275
- '[AttachmentOrchestrator] Sending message',
276
- i + 1,
277
- 'of',
278
- allMessages.length
279
- );
273
+ const recvID = currentConversation.userID || '';
274
+ const groupID = currentConversation.groupID || '';
280
275
 
281
- const result = await OpenIMSDK.sendMessageNotOss({
282
- recvID: currentConversation.userID || '',
283
- groupID: currentConversation.groupID || '',
284
- message,
276
+ // Push all messages as "Sending" immediately so the user sees every
277
+ // bubble right away, instead of waiting for each network call in turn.
278
+ for (const m of allMessages) {
279
+ useMessageStore.getState().pushNewMessage({
280
+ ...(m as MessageItem),
281
+ status: MessageStatus.Sending,
285
282
  });
283
+ }
286
284
 
287
- lastMsgId = result?.clientMsgID || result?.serverMsgID || '';
288
- console.log('[AttachmentOrchestrator] Message sent:', {
289
- messageId: lastMsgId,
290
- index: i + 1,
291
- });
285
+ const sendResults = await Promise.allSettled(
286
+ allMessages.map(async (m) => {
287
+ const message = m as MessageItem;
288
+ const result = await OpenIMSDK.sendMessageNotOss({
289
+ recvID,
290
+ groupID,
291
+ message,
292
+ });
292
293
 
293
- if (onMessageSent && result) {
294
- onMessageSent(result);
294
+ if (result) {
295
+ useMessageStore.getState().deleteOneMessage(message.clientMsgID);
296
+ useMessageStore.getState().pushNewMessage(result);
297
+ }
298
+ return result?.clientMsgID || result?.serverMsgID || '';
299
+ })
300
+ );
301
+
302
+ let firstSendError: unknown;
303
+ for (let i = 0; i < sendResults.length; i++) {
304
+ const settled = sendResults[i]!;
305
+ if (settled.status === 'fulfilled') {
306
+ lastMsgId = settled.value || lastMsgId;
307
+ } else {
308
+ firstSendError = firstSendError ?? settled.reason;
309
+ useMessageStore.getState().updateOneMessage({
310
+ ...(allMessages[i] as MessageItem),
311
+ status: MessageStatus.Failed,
312
+ });
295
313
  }
296
314
  }
297
315
 
316
+ if (!lastMsgId && firstSendError) {
317
+ throw firstSendError;
318
+ }
319
+
298
320
  return lastMsgId;
299
321
  }
300
322
  }
@@ -20,16 +20,15 @@ export namespace ChatMessageAPI {
20
20
  count?: number;
21
21
  startClientMsgID?: string;
22
22
  lastMinSeq?: number;
23
+ applicationType?: string;
23
24
  }
24
25
  ): Promise<HistoryMessageResult> {
25
- return OpenIMSDK.getAdvancedHistoryMessageList({
26
+ return OpenIMSDK.getAdvancedHistoryMessageListApp({
26
27
  conversationID,
27
28
  count: options?.count ?? DEFAULT_PAGE_SIZE,
28
29
  startClientMsgID: options?.startClientMsgID ?? '',
29
30
  viewType: ViewType.History,
30
- // ...(options?.startClientMsgID
31
- // ? { lastMinSeq: options.lastMinSeq ?? 0 }
32
- // : {}),
31
+ applicationType: options?.applicationType ?? '',
33
32
  }) as Promise<HistoryMessageResult>;
34
33
  }
35
34
 
@@ -50,6 +49,24 @@ export namespace ChatMessageAPI {
50
49
  return sentMessage;
51
50
  }
52
51
 
52
+ export async function searchLocalMessages(
53
+ conversationID: string,
54
+ options?: {
55
+ keywordList?: string[];
56
+ messageTypeList?: number[];
57
+ pageIndex?: number;
58
+ count?: number;
59
+ }
60
+ ) {
61
+ return OpenIMSDK.searchLocalMessages({
62
+ conversationID,
63
+ keywordList: options?.keywordList ?? ['a'],
64
+ messageTypeList: options?.messageTypeList,
65
+ pageIndex: options?.pageIndex ?? 1,
66
+ count: options?.count ?? 20,
67
+ });
68
+ }
69
+
53
70
  export async function markConversationAsRead(conversationID: string) {
54
71
  return OpenIMSDK.markConversationMessageAsRead(conversationID);
55
72
  }
@@ -11,10 +11,13 @@ const i18nKey: Record<string, string> = {
11
11
  'chat:file_download_failed': 'Tải tệp thất bại',
12
12
  'chat:file_open_failed': 'Không thể mở tệp này',
13
13
  'chat:image_preview': 'Xem trước ảnh',
14
- 'chat:image_too_large': 'Ảnh quá lớn. Tối đa 5MB',
15
- 'chat:video_too_large': 'Video quá lớn. Tối đa 200MB',
14
+ 'chat:image_too_large':
15
+ 'Hình ảnh tải lên không hợp lệ. Kích thước hình ảnh không được vượt quá {{maxSize}}MB',
16
+ 'chat:video_too_large':
17
+ 'Video tải lên không hợp lệ. Kích thước video không được vượt quá {{maxSize}}MB',
16
18
  'chat:image_sent': 'Đã gửi ảnh',
17
- 'chat:image_upload_failed': 'Lỗi tải ảnh',
19
+ 'chat:image_upload_failed': ' lỗi xảy ra vui lòng thử lại',
20
+ 'chat:generic_error': 'Có lỗi xảy ra vui lòng thử lại',
18
21
  'chat:failed_to_send_message': 'Không thể gửi tin nhắn. Thử lại?',
19
22
  'chat:filename': 'Tên tệp',
20
23
  'chat:file_size': 'Kích thước',
package/src/types/chat.ts CHANGED
@@ -20,6 +20,12 @@ type LogGA = (
20
20
  provider?: `${keyof typeof EventProvider}`
21
21
  ) => void;
22
22
 
23
+ export interface ChatUIAdapter {
24
+ toast?: { open: (params: any) => void };
25
+ alert?: { open: (params: any) => void };
26
+ popup?: { open: (params: any) => void };
27
+ }
28
+
23
29
  export interface ChatContextType {
24
30
  logGA?: LogGA;
25
31
  applicationType: DChatApplicationType;
@@ -0,0 +1,299 @@
1
+ declare module 'UIUtils' {
2
+ import type { ParamListBase, NavigationProp } from '@react-navigation/native';
3
+ import type { NativeStackScreenProps } from '@react-navigation/native-stack';
4
+ import type { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
5
+ import type { HeaderOptions, Layout } from '@react-navigation/elements';
6
+ import type {
7
+ KAlertProps,
8
+ KBottomSheetProps,
9
+ KHeaderNavigationProps,
10
+ KPopupProps,
11
+ KToastBarProps,
12
+ } from '@droppii/libs';
13
+ import type {
14
+ ActionSheetIOSOptions,
15
+ StyleProp,
16
+ ViewStyle,
17
+ } from 'react-native';
18
+
19
+ export interface KBottomSheetPropsEnhance extends KBottomSheetProps {
20
+ header: KBottomSheetProps['header'] & {
21
+ customRenderer?: (dismiss: () => void) => JSX.Element;
22
+ };
23
+ }
24
+
25
+ interface ScreenConfigProps {
26
+ __config: any;
27
+ }
28
+
29
+ export interface MonthPickerValue {
30
+ month?: number;
31
+ year?: number;
32
+ }
33
+
34
+ export interface KActionSheetProps extends ActionSheetIOSOptions {
35
+ callback: (index: number) => void;
36
+ }
37
+
38
+ export interface WithActionSheetProps {
39
+ open: (payload: KActionSheetProps) => void;
40
+ dismiss: () => void;
41
+ }
42
+
43
+ export interface WithAlertDialogProps {
44
+ open: (payload: KAlertProps) => void;
45
+ dismiss: () => void;
46
+ dismissAll: () => void;
47
+ }
48
+
49
+ export interface WithPopupProps {
50
+ open: (payload: KPopupProps) => void;
51
+ dismiss: () => void;
52
+ dismissAll: () => void;
53
+ }
54
+
55
+ export interface WithBottomSheetProps {
56
+ open: (payload: KBottomSheetPropsEnhance) => void;
57
+ dismiss: () => void;
58
+ }
59
+
60
+ export interface WithToastProps {
61
+ open: (
62
+ payload: KToastBarProps & { contentContainerStyle?: StyleProp<ViewStyle> }
63
+ ) => void;
64
+ dismiss: () => void;
65
+ }
66
+
67
+ export interface WithImageViewer {
68
+ open: (payload: ImageViewerParams) => void;
69
+ dismiss: () => void;
70
+ }
71
+
72
+ export interface WithModal {
73
+ open: (payload: JSX.Element) => void;
74
+ dismiss: () => void;
75
+ }
76
+
77
+ export interface IButton {
78
+ text: string;
79
+ onPress?: () => void;
80
+ skipUI?: boolean;
81
+ }
82
+
83
+ export interface IDialogParams {
84
+ title: string;
85
+ message?: string;
86
+ buttons?: Array<IButton>;
87
+ cancelIndex?: number;
88
+ icon?: any;
89
+ renderContent?: any;
90
+ ignoreStack?: boolean;
91
+ waitForDismiss?: boolean;
92
+ touchOutsideToDismiss?: boolean;
93
+ }
94
+
95
+ export type AddressLevel = 'province' | 'district' | 'ward';
96
+
97
+ export type CameraType = 'avatar' | 'identity-front' | 'identity-back';
98
+
99
+ export interface RequestCameraParams {
100
+ cameraType: CameraType;
101
+ onCaptured: (path: string) => void;
102
+ }
103
+
104
+ export interface IHeaderParams extends KHeaderNavigationProps {
105
+ onBack?: () => void;
106
+ isNested?: boolean;
107
+ }
108
+
109
+ export type Appearance = 'dark' | 'light';
110
+
111
+ export interface IModalParams {
112
+ Screen: React.ComponentType<any>;
113
+ screenProps?: any;
114
+ headerProps?: HeaderOptions & {
115
+ title?: string;
116
+ modal?: boolean;
117
+ layout?: Layout;
118
+ tintColor?: string;
119
+ noHeader?: boolean;
120
+ };
121
+ options?: {
122
+ disableGesture?: boolean;
123
+ };
124
+ }
125
+
126
+ export type NavigationBase = NavigationProp<ParamListBase, string, undefined>;
127
+
128
+ export interface StackScreenProps
129
+ extends
130
+ NativeStackScreenProps<ParamListBase, string, undefined>,
131
+ ScreenConfigProps {}
132
+
133
+ export interface TabScreenProps
134
+ extends
135
+ BottomTabScreenProps<ParamListBase, string, undefined>,
136
+ ScreenConfigProps {}
137
+
138
+ export interface TextModifiers {
139
+ fontSize?: number;
140
+ color?: string;
141
+ textCenter?: boolean;
142
+ fontFamily?: string;
143
+ fontStyle?: 'normal' | 'italic';
144
+ bold?: boolean;
145
+ letterSpacing?: number;
146
+ underline?: true;
147
+ underlineColor?: string;
148
+ textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase';
149
+ darkModeColor?: string;
150
+ typo?: TypographyModifiers;
151
+ textAlign?: 'center' | 'left' | 'right';
152
+ }
153
+
154
+ export interface LayoutModifiers {
155
+ flex?: number | true;
156
+ flexS?: number;
157
+ flexG?: number;
158
+ flexD?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
159
+ flexW?: 'wrap' | 'nowrap' | 'wrap-reverse';
160
+ alignItems?:
161
+ | true
162
+ | 'flex-start'
163
+ | 'flex-end'
164
+ | 'center'
165
+ | 'stretch'
166
+ | 'baseline';
167
+ alignSelf?:
168
+ | true
169
+ | 'auto'
170
+ | 'flex-start'
171
+ | 'flex-end'
172
+ | 'center'
173
+ | 'stretch'
174
+ | 'baseline';
175
+ justifyContent?:
176
+ | true
177
+ | 'flex-start'
178
+ | 'flex-end'
179
+ | 'center'
180
+ | 'space-between'
181
+ | 'space-around'
182
+ | 'space-evenly';
183
+ center?: boolean;
184
+ }
185
+
186
+ export interface SpacingModifiers {
187
+ padding?: number | string;
188
+ paddingL?: number | string;
189
+ paddingR?: number | string;
190
+ paddingT?: number | string;
191
+ paddingB?: number | string;
192
+ paddingH?: number | string;
193
+ paddingV?: number | string;
194
+ margin?: number;
195
+ marginL?: number | string | 'auto';
196
+ marginR?: number | string | 'auto';
197
+ marginT?: number | string | 'auto';
198
+ marginB?: number | string | 'auto';
199
+ marginH?: number | string | 'auto';
200
+ marginV?: number | string | 'auto';
201
+ width?: number | string;
202
+ height?: number | string;
203
+ minW?: number | string;
204
+ maxW?: number | string;
205
+ minH?: number | string;
206
+ maxH?: number | string;
207
+ sizeWH?: number;
208
+ }
209
+
210
+ export interface StylingModifiers {
211
+ background?: string;
212
+ opacity?: number;
213
+ br?: number;
214
+ brW?: number;
215
+ brC?: string;
216
+ brTL?: number;
217
+ brTR?: number;
218
+ brBL?: number;
219
+ brBR?: number;
220
+ brBW?: number;
221
+ brTW?: number;
222
+ brLW?: number;
223
+ brRW?: number;
224
+ brBC?: string;
225
+ brTC?: string;
226
+ brLC?: string;
227
+ brRC?: string;
228
+ }
229
+
230
+ export interface TestModifiers {
231
+ testID?: string;
232
+ }
233
+
234
+ export type TypographyModifiers =
235
+ | 'H1'
236
+ | 'H2'
237
+ | 'H3'
238
+ | 'H4'
239
+ | 'H5'
240
+ | 'H6'
241
+ | 'H7'
242
+ | 'B1'
243
+ | 'P1'
244
+ | 'P2'
245
+ | 'P3'
246
+ | 'S1'
247
+ | 'S2'
248
+ | 'S3'
249
+ | 'C1'
250
+ | 'C2'
251
+ | 'C3'
252
+ | 'A1'
253
+ | 'A2'
254
+ | 'A3';
255
+
256
+ export interface IProductMedia {
257
+ url?: string;
258
+ isVideo?: boolean;
259
+ }
260
+
261
+ export interface ImageViewerParams {
262
+ images: IProductMedia[];
263
+ initIndex?: number;
264
+ download?: boolean;
265
+ pagination?: boolean;
266
+ logParams?: any;
267
+ }
268
+
269
+ export type PickerType = 'all' | 'issue-place';
270
+
271
+ export interface DistrictEnhance {
272
+ wards?: Record<string, { name?: string; code?: string }>;
273
+ }
274
+
275
+ export interface ProvinceEnhance {
276
+ districts?: Record<string, DistrictEnhance>;
277
+ wards?: Record<string, { name?: string; code?: string }>;
278
+ }
279
+
280
+ export interface PickedAddress {
281
+ province: { name?: string; code?: string } | undefined;
282
+ district?: { name?: string; code?: string } | undefined;
283
+ ward: { name?: string; code?: string } | undefined;
284
+ }
285
+
286
+ export interface ImperativeProgress {
287
+ setProgress: (p: number) => void;
288
+ }
289
+
290
+ export interface InternalWebviewScreenProps {
291
+ closeVisible?: boolean;
292
+ closeColor?: string;
293
+ uri: string;
294
+ showAsYoutubePlayer?: boolean;
295
+ showNavigation?: boolean;
296
+ swipeToDismiss?: boolean;
297
+ hasEncode?: boolean;
298
+ }
299
+ }
@@ -0,0 +1 @@
1
+ /// <reference path="./global.d.ts" />
@@ -0,0 +1,31 @@
1
+ import { Alert } from 'react-native';
2
+ import type { ChatUIAdapter } from '../types/chat';
3
+
4
+ const openWithAlert = (params: { title?: string; message?: string }) => {
5
+ const { title = 'Message', message = '' } = params || {};
6
+ Alert.alert(title, message);
7
+ };
8
+
9
+ const defaultAdapter: Required<ChatUIAdapter> = {
10
+ toast: { open: openWithAlert },
11
+ alert: { open: openWithAlert },
12
+ popup: { open: openWithAlert },
13
+ };
14
+
15
+ let _adapter: ChatUIAdapter = {};
16
+
17
+ export const registerUIUtils = (adapter: ChatUIAdapter): void => {
18
+ _adapter = adapter;
19
+ };
20
+
21
+ export const UIUtils: Required<ChatUIAdapter> = {
22
+ toast: {
23
+ open: (params) => (_adapter.toast ?? defaultAdapter.toast).open(params),
24
+ },
25
+ alert: {
26
+ open: (params) => (_adapter.alert ?? defaultAdapter.alert).open(params),
27
+ },
28
+ popup: {
29
+ open: (params) => (_adapter.popup ?? defaultAdapter.popup).open(params),
30
+ },
31
+ };
@@ -1,5 +1,6 @@
1
1
  import type { ImageFile } from '../types/imageUpload';
2
2
  import Images from '../assets/images';
3
+ import { UPLOAD_LIMITS } from '../config/uploadLimits';
3
4
 
4
5
  export const generateUUID = (): string => {
5
6
  return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
@@ -81,10 +82,8 @@ export const formatFileSize = (bytes: number): string => {
81
82
  };
82
83
 
83
84
  export const validateImageFile = (file: ImageFile): string | null => {
84
- const MAX_SIZE = 5 * 1024 * 1024;
85
-
86
- if (file.size > MAX_SIZE) {
87
- return `Image too large. Max ${formatFileSize(MAX_SIZE)}`;
85
+ if (file.size > UPLOAD_LIMITS.image) {
86
+ return `Image too large. Max ${formatFileSize(UPLOAD_LIMITS.image)}`;
88
87
  }
89
88
 
90
89
  const validMimes = [
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Run `items` through `fn` with at most `concurrency` items in-flight at once.
3
+ * Errors per-item are swallowed — wrap `fn` if you need to capture them.
4
+ */
5
+ export async function pLimit<T>(
6
+ items: T[],
7
+ fn: (item: T, index: number) => Promise<void>,
8
+ concurrency: number
9
+ ): Promise<void> {
10
+ let idx = 0;
11
+ const worker = async () => {
12
+ while (idx < items.length) {
13
+ const i = idx++;
14
+ await fn(items[i]!, i);
15
+ }
16
+ };
17
+ await Promise.all(
18
+ Array.from({ length: Math.min(concurrency, items.length) }, worker)
19
+ );
20
+ }
@@ -28,6 +28,9 @@ interface ThumbnailResult {
28
28
  * The app strips "file://" before calling, so local videos fall into the broken
29
29
  * branch. The fix is to KEEP the file:// scheme so the working overload is used.
30
30
  *
31
+ * iOS is the opposite: AVAsset / react-native-create-thumbnail requires a bare
32
+ * path without any scheme prefix. Strip "file://" on iOS.
33
+ *
31
34
  * For Android photo-picker synthetic paths we additionally copy the file into the
32
35
  * app cache first (a clean, app-private file the retriever can always open) and
33
36
  * still hand it back with a file:// prefix. Returns the path to pass to
@@ -36,9 +39,13 @@ interface ThumbnailResult {
36
39
  const ensureRetrievableVideoPath = async (
37
40
  videoPath: string
38
41
  ): Promise<{ path: string; cleanupPath: string | null }> => {
39
- // iOS (and any non-Android platform) is left completely untouched.
42
+ // iOS AVAsset / react-native-create-thumbnail requires a bare file path
43
+ // without the file:// scheme. Strip it if present.
40
44
  if (Platform.OS !== 'android') {
41
- return { path: videoPath, cleanupPath: null };
45
+ const barePath = videoPath.startsWith('file://')
46
+ ? videoPath.slice('file://'.length)
47
+ : videoPath;
48
+ return { path: barePath, cleanupPath: null };
42
49
  }
43
50
 
44
51
  // content:// is handled by a dedicated native branch — pass through unchanged.
@@ -112,7 +119,6 @@ export const VideoThumbnailUtil = {
112
119
  cacheName,
113
120
  });
114
121
 
115
- console.log('✅ [VideoThumbnail] THUMBNAIL PATH:', result.path);
116
122
  console.log('[VideoThumbnail] Thumbnail generated', {
117
123
  path: result.path,
118
124
  width: result.width || 0,
@@ -147,6 +153,7 @@ export const VideoThumbnailUtil = {
147
153
  }
148
154
  },
149
155
 
156
+ // Serial by design — reduces memory pressure when processing multiple videos.
150
157
  batchGenerateThumbnails: async (
151
158
  videoPaths: string[],
152
159
  options: ThumbnailOptions = {}