@droppii-org/chat-mobile 0.2.0

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 (294) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +101 -0
  3. package/lib/module/assets/images/icon_bot.png +0 -0
  4. package/lib/module/assets/images/index.js +9 -0
  5. package/lib/module/assets/images/index.js.map +1 -0
  6. package/lib/module/assets/images/tag_bot.png +0 -0
  7. package/lib/module/assets/images/tag_mall.png +0 -0
  8. package/lib/module/build-ignore.d.js +2 -0
  9. package/lib/module/build-ignore.d.js.map +1 -0
  10. package/lib/module/components/Avatar/Avatar.js +81 -0
  11. package/lib/module/components/Avatar/Avatar.js.map +1 -0
  12. package/lib/module/components/Avatar/Avatar.types.js +2 -0
  13. package/lib/module/components/Avatar/Avatar.types.js.map +1 -0
  14. package/lib/module/components/Avatar/Avatar.utils.js +44 -0
  15. package/lib/module/components/Avatar/Avatar.utils.js.map +1 -0
  16. package/lib/module/components/Avatar/AvatarBadge.js +28 -0
  17. package/lib/module/components/Avatar/AvatarBadge.js.map +1 -0
  18. package/lib/module/components/Avatar/DoubleAvatar.js +74 -0
  19. package/lib/module/components/Avatar/DoubleAvatar.js.map +1 -0
  20. package/lib/module/components/Avatar/SingleAvatar.js +53 -0
  21. package/lib/module/components/Avatar/SingleAvatar.js.map +1 -0
  22. package/lib/module/components/Avatar/index.js +7 -0
  23. package/lib/module/components/Avatar/index.js.map +1 -0
  24. package/lib/module/components/ThreadCard/AvatarSection.js +37 -0
  25. package/lib/module/components/ThreadCard/AvatarSection.js.map +1 -0
  26. package/lib/module/components/ThreadCard/NamePrefixIcon.js +36 -0
  27. package/lib/module/components/ThreadCard/NamePrefixIcon.js.map +1 -0
  28. package/lib/module/components/ThreadCard/ThreadCard.js +132 -0
  29. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -0
  30. package/lib/module/components/ThreadCard/UnreadBadge.js +35 -0
  31. package/lib/module/components/ThreadCard/UnreadBadge.js.map +1 -0
  32. package/lib/module/components/ThreadCard/index.js +7 -0
  33. package/lib/module/components/ThreadCard/index.js.map +1 -0
  34. package/lib/module/components/ThreadCard/thread-card.utils.js +51 -0
  35. package/lib/module/components/ThreadCard/thread-card.utils.js.map +1 -0
  36. package/lib/module/core/index.js +11 -0
  37. package/lib/module/core/index.js.map +1 -0
  38. package/lib/module/core/useChatListener.js +62 -0
  39. package/lib/module/core/useChatListener.js.map +1 -0
  40. package/lib/module/core/useUserListener.js +72 -0
  41. package/lib/module/core/useUserListener.js.map +1 -0
  42. package/lib/module/hooks/query-keys.js +10 -0
  43. package/lib/module/hooks/query-keys.js.map +1 -0
  44. package/lib/module/hooks/useChatMessages.js +163 -0
  45. package/lib/module/hooks/useChatMessages.js.map +1 -0
  46. package/lib/module/hooks/useConversationList.js +51 -0
  47. package/lib/module/hooks/useConversationList.js.map +1 -0
  48. package/lib/module/index.js +14 -0
  49. package/lib/module/index.js.map +1 -0
  50. package/lib/module/package.json +1 -0
  51. package/lib/module/screens/chat-detail/ChatAttachmentPanel.js +106 -0
  52. package/lib/module/screens/chat-detail/ChatAttachmentPanel.js.map +1 -0
  53. package/lib/module/screens/chat-detail/ChatComposer.js +288 -0
  54. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -0
  55. package/lib/module/screens/chat-detail/ChatDay.js +65 -0
  56. package/lib/module/screens/chat-detail/ChatDay.js.map +1 -0
  57. package/lib/module/screens/chat-detail/ChatDetail.js +104 -0
  58. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -0
  59. package/lib/module/screens/chat-detail/ChatDetailHeader.js +92 -0
  60. package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -0
  61. package/lib/module/screens/chat-detail/ChatList.js +145 -0
  62. package/lib/module/screens/chat-detail/ChatList.js.map +1 -0
  63. package/lib/module/screens/chat-detail/ChatLoadEarlier.js +23 -0
  64. package/lib/module/screens/chat-detail/ChatLoadEarlier.js.map +1 -0
  65. package/lib/module/screens/chat-detail/ChatQuickActions.js +85 -0
  66. package/lib/module/screens/chat-detail/ChatQuickActions.js.map +1 -0
  67. package/lib/module/screens/chat-detail/ChatScrollToBottom.js +12 -0
  68. package/lib/module/screens/chat-detail/ChatScrollToBottom.js.map +1 -0
  69. package/lib/module/screens/chat-detail/ChatTextBubble.js +62 -0
  70. package/lib/module/screens/chat-detail/ChatTextBubble.js.map +1 -0
  71. package/lib/module/screens/chat-detail/constants.js +59 -0
  72. package/lib/module/screens/chat-detail/constants.js.map +1 -0
  73. package/lib/module/screens/chat-detail/index.js +12 -0
  74. package/lib/module/screens/chat-detail/index.js.map +1 -0
  75. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js +24 -0
  76. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js.map +1 -0
  77. package/lib/module/screens/chat-detail/messages/types.js +4 -0
  78. package/lib/module/screens/chat-detail/messages/types.js.map +1 -0
  79. package/lib/module/screens/chat-detail/types.js +4 -0
  80. package/lib/module/screens/chat-detail/types.js.map +1 -0
  81. package/lib/module/screens/chat-detail/useChatActionPress.js +11 -0
  82. package/lib/module/screens/chat-detail/useChatActionPress.js.map +1 -0
  83. package/lib/module/screens/inbox/Inbox.js +134 -0
  84. package/lib/module/screens/inbox/Inbox.js.map +1 -0
  85. package/lib/module/screens/inbox/MessagesTab.js +58 -0
  86. package/lib/module/screens/inbox/MessagesTab.js.map +1 -0
  87. package/lib/module/screens/inbox/index.js +5 -0
  88. package/lib/module/screens/inbox/index.js.map +1 -0
  89. package/lib/module/services/apis.js +41 -0
  90. package/lib/module/services/apis.js.map +1 -0
  91. package/lib/module/services/index.js +5 -0
  92. package/lib/module/services/index.js.map +1 -0
  93. package/lib/module/services/message.js +38 -0
  94. package/lib/module/services/message.js.map +1 -0
  95. package/lib/module/store/conversation.js +89 -0
  96. package/lib/module/store/conversation.js.map +1 -0
  97. package/lib/module/store/index.js +6 -0
  98. package/lib/module/store/index.js.map +1 -0
  99. package/lib/module/store/storeConfig.js +19 -0
  100. package/lib/module/store/storeConfig.js.map +1 -0
  101. package/lib/module/store/user.js +25 -0
  102. package/lib/module/store/user.js.map +1 -0
  103. package/lib/module/translation/index.js +24 -0
  104. package/lib/module/translation/index.js.map +1 -0
  105. package/lib/module/translation/resources/i18n.js +10 -0
  106. package/lib/module/translation/resources/i18n.js.map +1 -0
  107. package/lib/module/types/auth.js +9 -0
  108. package/lib/module/types/auth.js.map +1 -0
  109. package/lib/module/types/chat.js +26 -0
  110. package/lib/module/types/chat.js.map +1 -0
  111. package/lib/module/types/message.js +16 -0
  112. package/lib/module/types/message.js.map +1 -0
  113. package/lib/module/utils/conversation.js +61 -0
  114. package/lib/module/utils/conversation.js.map +1 -0
  115. package/lib/module/utils/giftedChatMessage.js +90 -0
  116. package/lib/module/utils/giftedChatMessage.js.map +1 -0
  117. package/lib/module/utils/message.js +68 -0
  118. package/lib/module/utils/message.js.map +1 -0
  119. package/lib/module/utils/resolveMessageType.js +44 -0
  120. package/lib/module/utils/resolveMessageType.js.map +1 -0
  121. package/lib/typescript/package.json +1 -0
  122. package/lib/typescript/src/assets/images/index.d.ts +7 -0
  123. package/lib/typescript/src/assets/images/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/Avatar/Avatar.d.ts +4 -0
  125. package/lib/typescript/src/components/Avatar/Avatar.d.ts.map +1 -0
  126. package/lib/typescript/src/components/Avatar/Avatar.types.d.ts +18 -0
  127. package/lib/typescript/src/components/Avatar/Avatar.types.d.ts.map +1 -0
  128. package/lib/typescript/src/components/Avatar/Avatar.utils.d.ts +14 -0
  129. package/lib/typescript/src/components/Avatar/Avatar.utils.d.ts.map +1 -0
  130. package/lib/typescript/src/components/Avatar/AvatarBadge.d.ts +7 -0
  131. package/lib/typescript/src/components/Avatar/AvatarBadge.d.ts.map +1 -0
  132. package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts +13 -0
  133. package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts.map +1 -0
  134. package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts +12 -0
  135. package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts.map +1 -0
  136. package/lib/typescript/src/components/Avatar/index.d.ts +6 -0
  137. package/lib/typescript/src/components/Avatar/index.d.ts.map +1 -0
  138. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +8 -0
  139. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -0
  140. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts +8 -0
  141. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -0
  142. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts +11 -0
  143. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -0
  144. package/lib/typescript/src/components/ThreadCard/UnreadBadge.d.ts +6 -0
  145. package/lib/typescript/src/components/ThreadCard/UnreadBadge.d.ts.map +1 -0
  146. package/lib/typescript/src/components/ThreadCard/index.d.ts +5 -0
  147. package/lib/typescript/src/components/ThreadCard/index.d.ts.map +1 -0
  148. package/lib/typescript/src/components/ThreadCard/thread-card.utils.d.ts +4 -0
  149. package/lib/typescript/src/components/ThreadCard/thread-card.utils.d.ts.map +1 -0
  150. package/lib/typescript/src/core/index.d.ts +3 -0
  151. package/lib/typescript/src/core/index.d.ts.map +1 -0
  152. package/lib/typescript/src/core/useChatListener.d.ts +2 -0
  153. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -0
  154. package/lib/typescript/src/core/useUserListener.d.ts +2 -0
  155. package/lib/typescript/src/core/useUserListener.d.ts.map +1 -0
  156. package/lib/typescript/src/hooks/query-keys.d.ts +9 -0
  157. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -0
  158. package/lib/typescript/src/hooks/useChatMessages.d.ts +21 -0
  159. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -0
  160. package/lib/typescript/src/hooks/useConversationList.d.ts +8 -0
  161. package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -0
  162. package/lib/typescript/src/index.d.ts +14 -0
  163. package/lib/typescript/src/index.d.ts.map +1 -0
  164. package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts +3 -0
  165. package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts.map +1 -0
  166. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +3 -0
  167. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -0
  168. package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts +3 -0
  169. package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts.map +1 -0
  170. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +4 -0
  171. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -0
  172. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +4 -0
  173. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -0
  174. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts +3 -0
  175. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts.map +1 -0
  176. package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts +3 -0
  177. package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts.map +1 -0
  178. package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts +3 -0
  179. package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts.map +1 -0
  180. package/lib/typescript/src/screens/chat-detail/ChatScrollToBottom.d.ts +2 -0
  181. package/lib/typescript/src/screens/chat-detail/ChatScrollToBottom.d.ts.map +1 -0
  182. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts +3 -0
  183. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts.map +1 -0
  184. package/lib/typescript/src/screens/chat-detail/constants.d.ts +17 -0
  185. package/lib/typescript/src/screens/chat-detail/constants.d.ts.map +1 -0
  186. package/lib/typescript/src/screens/chat-detail/index.d.ts +13 -0
  187. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -0
  188. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts +3 -0
  189. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts.map +1 -0
  190. package/lib/typescript/src/screens/chat-detail/messages/types.d.ts +13 -0
  191. package/lib/typescript/src/screens/chat-detail/messages/types.d.ts.map +1 -0
  192. package/lib/typescript/src/screens/chat-detail/types.d.ts +86 -0
  193. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -0
  194. package/lib/typescript/src/screens/chat-detail/useChatActionPress.d.ts +3 -0
  195. package/lib/typescript/src/screens/chat-detail/useChatActionPress.d.ts.map +1 -0
  196. package/lib/typescript/src/screens/inbox/Inbox.d.ts +10 -0
  197. package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -0
  198. package/lib/typescript/src/screens/inbox/MessagesTab.d.ts +7 -0
  199. package/lib/typescript/src/screens/inbox/MessagesTab.d.ts.map +1 -0
  200. package/lib/typescript/src/screens/inbox/index.d.ts +3 -0
  201. package/lib/typescript/src/screens/inbox/index.d.ts.map +1 -0
  202. package/lib/typescript/src/services/apis.d.ts +12 -0
  203. package/lib/typescript/src/services/apis.d.ts.map +1 -0
  204. package/lib/typescript/src/services/index.d.ts +3 -0
  205. package/lib/typescript/src/services/index.d.ts.map +1 -0
  206. package/lib/typescript/src/services/message.d.ts +21 -0
  207. package/lib/typescript/src/services/message.d.ts.map +1 -0
  208. package/lib/typescript/src/store/conversation.d.ts +24 -0
  209. package/lib/typescript/src/store/conversation.d.ts.map +1 -0
  210. package/lib/typescript/src/store/index.d.ts +4 -0
  211. package/lib/typescript/src/store/index.d.ts.map +1 -0
  212. package/lib/typescript/src/store/storeConfig.d.ts +4 -0
  213. package/lib/typescript/src/store/storeConfig.d.ts.map +1 -0
  214. package/lib/typescript/src/store/user.d.ts +12 -0
  215. package/lib/typescript/src/store/user.d.ts.map +1 -0
  216. package/lib/typescript/src/translation/index.d.ts +4 -0
  217. package/lib/typescript/src/translation/index.d.ts.map +1 -0
  218. package/lib/typescript/src/translation/resources/i18n.d.ts +5 -0
  219. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -0
  220. package/lib/typescript/src/types/auth.d.ts +24 -0
  221. package/lib/typescript/src/types/auth.d.ts.map +1 -0
  222. package/lib/typescript/src/types/chat.d.ts +99 -0
  223. package/lib/typescript/src/types/chat.d.ts.map +1 -0
  224. package/lib/typescript/src/types/message.d.ts +15 -0
  225. package/lib/typescript/src/types/message.d.ts.map +1 -0
  226. package/lib/typescript/src/utils/conversation.d.ts +10 -0
  227. package/lib/typescript/src/utils/conversation.d.ts.map +1 -0
  228. package/lib/typescript/src/utils/giftedChatMessage.d.ts +10 -0
  229. package/lib/typescript/src/utils/giftedChatMessage.d.ts.map +1 -0
  230. package/lib/typescript/src/utils/message.d.ts +15 -0
  231. package/lib/typescript/src/utils/message.d.ts.map +1 -0
  232. package/lib/typescript/src/utils/resolveMessageType.d.ts +4 -0
  233. package/lib/typescript/src/utils/resolveMessageType.d.ts.map +1 -0
  234. package/package.json +209 -0
  235. package/src/assets/images/icon_bot.png +0 -0
  236. package/src/assets/images/index.ts +7 -0
  237. package/src/assets/images/tag_bot.png +0 -0
  238. package/src/assets/images/tag_mall.png +0 -0
  239. package/src/build-ignore.d.ts +24 -0
  240. package/src/components/Avatar/Avatar.tsx +105 -0
  241. package/src/components/Avatar/Avatar.types.ts +17 -0
  242. package/src/components/Avatar/Avatar.utils.ts +49 -0
  243. package/src/components/Avatar/AvatarBadge.tsx +29 -0
  244. package/src/components/Avatar/DoubleAvatar.tsx +89 -0
  245. package/src/components/Avatar/SingleAvatar.tsx +74 -0
  246. package/src/components/Avatar/index.ts +5 -0
  247. package/src/components/ThreadCard/AvatarSection.tsx +42 -0
  248. package/src/components/ThreadCard/NamePrefixIcon.tsx +45 -0
  249. package/src/components/ThreadCard/ThreadCard.tsx +145 -0
  250. package/src/components/ThreadCard/UnreadBadge.tsx +35 -0
  251. package/src/components/ThreadCard/index.ts +4 -0
  252. package/src/components/ThreadCard/thread-card.utils.ts +68 -0
  253. package/src/core/index.ts +10 -0
  254. package/src/core/useChatListener.ts +85 -0
  255. package/src/core/useUserListener.ts +86 -0
  256. package/src/hooks/query-keys.ts +11 -0
  257. package/src/hooks/useChatMessages.ts +243 -0
  258. package/src/hooks/useConversationList.ts +57 -0
  259. package/src/index.tsx +36 -0
  260. package/src/screens/chat-detail/ChatAttachmentPanel.tsx +142 -0
  261. package/src/screens/chat-detail/ChatComposer.tsx +412 -0
  262. package/src/screens/chat-detail/ChatDay.tsx +73 -0
  263. package/src/screens/chat-detail/ChatDetail.tsx +118 -0
  264. package/src/screens/chat-detail/ChatDetailHeader.tsx +114 -0
  265. package/src/screens/chat-detail/ChatList.tsx +187 -0
  266. package/src/screens/chat-detail/ChatLoadEarlier.tsx +20 -0
  267. package/src/screens/chat-detail/ChatQuickActions.tsx +108 -0
  268. package/src/screens/chat-detail/ChatScrollToBottom.tsx +8 -0
  269. package/src/screens/chat-detail/ChatTextBubble.tsx +73 -0
  270. package/src/screens/chat-detail/constants.ts +76 -0
  271. package/src/screens/chat-detail/index.ts +33 -0
  272. package/src/screens/chat-detail/messages/ChatMessageBubble.tsx +24 -0
  273. package/src/screens/chat-detail/messages/types.ts +14 -0
  274. package/src/screens/chat-detail/types.ts +97 -0
  275. package/src/screens/chat-detail/useChatActionPress.ts +17 -0
  276. package/src/screens/inbox/Inbox.tsx +164 -0
  277. package/src/screens/inbox/MessagesTab.tsx +62 -0
  278. package/src/screens/inbox/index.ts +2 -0
  279. package/src/services/apis.ts +60 -0
  280. package/src/services/index.ts +2 -0
  281. package/src/services/message.ts +61 -0
  282. package/src/store/conversation.ts +116 -0
  283. package/src/store/index.ts +3 -0
  284. package/src/store/storeConfig.ts +19 -0
  285. package/src/store/user.ts +26 -0
  286. package/src/translation/index.ts +30 -0
  287. package/src/translation/resources/i18n.ts +8 -0
  288. package/src/types/auth.ts +25 -0
  289. package/src/types/chat.ts +118 -0
  290. package/src/types/message.ts +17 -0
  291. package/src/utils/conversation.ts +106 -0
  292. package/src/utils/giftedChatMessage.ts +137 -0
  293. package/src/utils/message.ts +136 -0
  294. package/src/utils/resolveMessageType.ts +49 -0
@@ -0,0 +1,89 @@
1
+ import { memo } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import { KContainer, KColors } from '@droppii/libs';
4
+ import { SingleAvatar } from './SingleAvatar';
5
+
6
+ interface DoubleAvatarProps {
7
+ primarySource: string | null | undefined;
8
+ secondarySource: string | null | undefined;
9
+ size: number;
10
+ borderColor: string;
11
+ borderWidth: number;
12
+ placeholderColor: string;
13
+ primaryFullName?: string;
14
+ secondaryFullName?: string;
15
+ }
16
+
17
+ export const DoubleAvatar = memo(
18
+ ({
19
+ primarySource,
20
+ secondarySource,
21
+ size,
22
+ borderColor,
23
+ borderWidth,
24
+ placeholderColor,
25
+ primaryFullName,
26
+ secondaryFullName,
27
+ }: DoubleAvatarProps) => {
28
+ const innerSize = Math.round(size * 0.6875); // 33/48 ratio
29
+ const offset = Math.round(size * 0.3125); // 15/48 ratio
30
+
31
+ return (
32
+ <KContainer.View
33
+ style={[styles.doubleAvatarContainer, { width: size, height: size }]}
34
+ >
35
+ <KContainer.View
36
+ style={[
37
+ styles.doubleAvatarChild,
38
+ styles.doubleAvatarTop,
39
+ { width: innerSize, height: innerSize, right: offset },
40
+ ]}
41
+ >
42
+ <SingleAvatar
43
+ source={primarySource}
44
+ size={innerSize}
45
+ borderColor={borderColor}
46
+ borderWidth={borderWidth}
47
+ placeholderColor={placeholderColor}
48
+ backgroundColor={KColors.palette.gray.w300}
49
+ fullName={primaryFullName}
50
+ />
51
+ </KContainer.View>
52
+ <KContainer.View
53
+ style={[
54
+ styles.doubleAvatarChild,
55
+ styles.doubleAvatarBottom,
56
+ { width: innerSize, height: innerSize, left: offset },
57
+ ]}
58
+ >
59
+ <SingleAvatar
60
+ source={secondarySource}
61
+ size={innerSize}
62
+ borderColor={borderColor}
63
+ borderWidth={borderWidth}
64
+ placeholderColor={placeholderColor}
65
+ backgroundColor={KColors.palette.gray.w300}
66
+ fullName={secondaryFullName}
67
+ />
68
+ </KContainer.View>
69
+ </KContainer.View>
70
+ );
71
+ }
72
+ );
73
+
74
+ DoubleAvatar.displayName = 'DoubleAvatar';
75
+
76
+ const styles = StyleSheet.create({
77
+ doubleAvatarContainer: {
78
+ position: 'relative',
79
+ },
80
+ doubleAvatarChild: {
81
+ position: 'absolute',
82
+ },
83
+ doubleAvatarTop: {
84
+ top: 0,
85
+ },
86
+ doubleAvatarBottom: {
87
+ bottom: 0,
88
+ },
89
+ });
@@ -0,0 +1,74 @@
1
+ import { memo } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
4
+ import { getInitials, TYPO_MAP } from './Avatar.utils';
5
+
6
+ interface SingleAvatarProps {
7
+ source: string | null | undefined;
8
+ size: number;
9
+ borderColor: string;
10
+ borderWidth: number;
11
+ placeholderColor: string;
12
+ backgroundColor: string;
13
+ fullName?: string;
14
+ }
15
+
16
+ export const SingleAvatar = memo(
17
+ ({
18
+ source,
19
+ size,
20
+ borderColor,
21
+ borderWidth,
22
+ placeholderColor,
23
+ backgroundColor,
24
+ fullName,
25
+ }: SingleAvatarProps) => {
26
+ if (source) {
27
+ return (
28
+ <KImage.Avatar
29
+ uri={source}
30
+ size={size === 24 ? 'sm' : size === 48 ? 'md' : 'lg'}
31
+ style={{
32
+ borderWidth,
33
+ borderColor,
34
+ width: size,
35
+ height: size,
36
+ }}
37
+ />
38
+ );
39
+ }
40
+
41
+ return (
42
+ <KContainer.View
43
+ center
44
+ style={[
45
+ styles.placeholderAvatar,
46
+ {
47
+ width: size,
48
+ height: size,
49
+ borderRadius: size / 2,
50
+ borderWidth,
51
+ borderColor,
52
+ backgroundColor: backgroundColor || placeholderColor,
53
+ },
54
+ ]}
55
+ >
56
+ <KLabel.Text
57
+ typo={TYPO_MAP[size as keyof typeof TYPO_MAP] || 'TextNmBold'}
58
+ color={KColors.palette.primary.w400}
59
+ >
60
+ {getInitials(fullName)}
61
+ </KLabel.Text>
62
+ </KContainer.View>
63
+ );
64
+ }
65
+ );
66
+
67
+ SingleAvatar.displayName = 'SingleAvatar';
68
+
69
+ const styles = StyleSheet.create({
70
+ placeholderAvatar: {
71
+ justifyContent: 'center',
72
+ alignItems: 'center',
73
+ },
74
+ });
@@ -0,0 +1,5 @@
1
+ export { default as Avatar } from './Avatar';
2
+ export { SingleAvatar } from './SingleAvatar';
3
+ export { DoubleAvatar } from './DoubleAvatar';
4
+ export { AvatarBadge } from './AvatarBadge';
5
+ export type { AvatarProps } from './Avatar.types';
@@ -0,0 +1,42 @@
1
+ import { memo } from 'react';
2
+ import { Avatar } from '../Avatar';
3
+ import { DChatCategory } from '../../types/chat';
4
+ import type { DConversationItem } from '../../types/chat';
5
+
6
+ interface AvatarSectionProps extends Partial<
7
+ Pick<DConversationItem, 'chatCategory' | 'applicationType' | 'chatType'>
8
+ > {
9
+ avatar: string | null;
10
+ fullName: string;
11
+ }
12
+
13
+ export const AvatarSection = memo(
14
+ ({ avatar, fullName, chatCategory, applicationType }: AvatarSectionProps) => {
15
+ if (
16
+ chatCategory === DChatCategory.BIZ_BOT_CRM ||
17
+ chatCategory === DChatCategory.BIZ_BOT_PDP
18
+ ) {
19
+ return (
20
+ <Avatar
21
+ source={avatar}
22
+ fullName={fullName}
23
+ badge={{ variant: 'bot' }}
24
+ />
25
+ );
26
+ }
27
+
28
+ if (applicationType === 'MALL') {
29
+ return (
30
+ <Avatar
31
+ source={avatar}
32
+ fullName={fullName}
33
+ badge={{ variant: 'mall' }}
34
+ />
35
+ );
36
+ }
37
+
38
+ return <Avatar source={avatar} fullName={fullName} />;
39
+ }
40
+ );
41
+
42
+ AvatarSection.displayName = 'AvatarSection';
@@ -0,0 +1,45 @@
1
+ import { memo } from 'react';
2
+ import { KContainer, KImage, KColors } from '@droppii/libs';
3
+ import { DChatCategory, DChatType } from '../../types/chat';
4
+
5
+ interface NamePrefixIconProps {
6
+ chatCategory?: DChatCategory;
7
+ chatType?: DChatType;
8
+ }
9
+
10
+ export const NamePrefixIcon = memo(
11
+ ({ chatCategory, chatType }: NamePrefixIconProps) => {
12
+ if (
13
+ chatCategory === DChatCategory.BIZ_BOT_CRM ||
14
+ chatCategory === DChatCategory.BIZ_BOT_PDP
15
+ ) {
16
+ return (
17
+ <KContainer.View marginR="0.25rem">
18
+ <KImage.VectorIcons
19
+ name="robot-outline"
20
+ provider="MaterialCommunityIcons"
21
+ size={14}
22
+ color={KColors.palette.primary.w400}
23
+ />
24
+ </KContainer.View>
25
+ );
26
+ }
27
+
28
+ if (chatType === DChatType.GROUP) {
29
+ return (
30
+ <KContainer.View marginR="0.25rem">
31
+ <KImage.VectorIcons
32
+ name="account-multiple-outline"
33
+ provider="MaterialCommunityIcons"
34
+ size={14}
35
+ color={KColors.palette.gray.w500}
36
+ />
37
+ </KContainer.View>
38
+ );
39
+ }
40
+
41
+ return null;
42
+ }
43
+ );
44
+
45
+ NamePrefixIcon.displayName = 'NamePrefixIcon';
@@ -0,0 +1,145 @@
1
+ import { memo, useCallback } from 'react';
2
+ import { StyleSheet, type GestureResponderEvent } from 'react-native';
3
+ import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
4
+ import { DChatCategory } from '../../types/chat';
5
+ import { AvatarSection } from './AvatarSection';
6
+ import { NamePrefixIcon } from './NamePrefixIcon';
7
+ import { UnreadBadge } from './UnreadBadge';
8
+ import { formatTimestamp, getLastMessageText } from './thread-card.utils';
9
+ import Images from '../../assets/images';
10
+ import { useConversation, useUserStore } from '../../store';
11
+
12
+ interface ThreadCardProps {
13
+ item: string;
14
+ onPress?: (params: { item: string; event: GestureResponderEvent }) => void;
15
+ }
16
+
17
+ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
18
+ const {
19
+ peer,
20
+ chatCategory,
21
+ lastMessage,
22
+ unreadCount = 0,
23
+ isMuted,
24
+ pinnedAt,
25
+ applicationType,
26
+ chatType,
27
+ latestMsgSendTime,
28
+ } = useConversation(item) ?? {};
29
+
30
+ const fullName = peer?.fullName ?? peer?.username ?? '';
31
+ const hasUnread = unreadCount > 0;
32
+ const timestamp = formatTimestamp(
33
+ latestMsgSendTime ?? lastMessage?.sendTime ?? null
34
+ );
35
+ const currentUserId = useUserStore((s) => s.user?.userID);
36
+ const lastMessageText = getLastMessageText(lastMessage, currentUserId);
37
+
38
+ const handlePress = useCallback(
39
+ (event: GestureResponderEvent) => {
40
+ onPress?.({ item, event });
41
+ },
42
+ [item, onPress]
43
+ );
44
+
45
+ return (
46
+ <KContainer.Touchable
47
+ row
48
+ alignItems="center"
49
+ paddingH="0.75rem"
50
+ paddingV="0.75rem"
51
+ onPress={handlePress}
52
+ activeOpacity={0.7}
53
+ >
54
+ <AvatarSection
55
+ avatar={peer?.avatar ?? null}
56
+ fullName={fullName}
57
+ chatCategory={chatCategory}
58
+ applicationType={applicationType}
59
+ chatType={chatType}
60
+ />
61
+
62
+ {/* Chat-Content */}
63
+ <KContainer.View flex marginL="0.5rem" paddingB={2}>
64
+ {/* Row-Title */}
65
+ <KContainer.View row alignItems="center">
66
+ <NamePrefixIcon chatCategory={chatCategory} chatType={chatType} />
67
+ <KContainer.VisibleView
68
+ visible={
69
+ chatCategory === DChatCategory.BIZ_BOT_CRM ||
70
+ chatCategory === DChatCategory.BIZ_BOT_PDP
71
+ }
72
+ >
73
+ <KImage.Base
74
+ uri={Images.ICON_BOT}
75
+ size={14}
76
+ style={styles.botIcon}
77
+ />
78
+ </KContainer.VisibleView>
79
+ <KLabel.Text
80
+ typo={hasUnread ? 'TextMdBold' : 'TextMdNormal'}
81
+ numberOfLines={1}
82
+ color={KColors.black}
83
+ flex
84
+ >
85
+ {fullName}
86
+ </KLabel.Text>
87
+ <KContainer.VisibleView visible={!!isMuted}>
88
+ <KContainer.View marginL="0.25rem">
89
+ <KImage.VectorIcons
90
+ name="bell-off"
91
+ provider="MaterialCommunityIcons"
92
+ size={16}
93
+ color={KColors.palette.gray.w500}
94
+ />
95
+ </KContainer.View>
96
+ </KContainer.VisibleView>
97
+ <KContainer.VisibleView visible={Boolean(timestamp)}>
98
+ <KLabel.Text
99
+ typo="TextXsNormal"
100
+ color={KColors.palette.gray.w400}
101
+ marginL="0.25rem"
102
+ >
103
+ {timestamp}
104
+ </KLabel.Text>
105
+ </KContainer.VisibleView>
106
+ </KContainer.View>
107
+
108
+ {/* In-Text */}
109
+ <KContainer.View row alignItems="center">
110
+ <KLabel.Text
111
+ typo={hasUnread ? 'TextNmMedium' : 'TextNmNormal'}
112
+ color={hasUnread ? KColors.black : KColors.gray.normal}
113
+ numberOfLines={1}
114
+ flex
115
+ >
116
+ {lastMessageText}
117
+ </KLabel.Text>
118
+ <KContainer.VisibleView visible={Boolean(pinnedAt)}>
119
+ <KContainer.View marginL="0.5rem">
120
+ <KImage.VectorIcons
121
+ name="pin-outline"
122
+ provider="MaterialCommunityIcons"
123
+ size={14}
124
+ color={KColors.palette.gray.w400}
125
+ />
126
+ </KContainer.View>
127
+ </KContainer.VisibleView>
128
+ <UnreadBadge count={unreadCount} />
129
+ </KContainer.View>
130
+ </KContainer.View>
131
+ </KContainer.Touchable>
132
+ );
133
+ });
134
+
135
+ ThreadCard.displayName = 'ThreadCard';
136
+
137
+ export default ThreadCard;
138
+
139
+ const styles = StyleSheet.create({
140
+ botIcon: {
141
+ width: 16,
142
+ height: 16,
143
+ marginRight: 4,
144
+ },
145
+ });
@@ -0,0 +1,35 @@
1
+ import { memo } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import { KContainer, KLabel, KColors } from '@droppii/libs';
4
+
5
+ interface UnreadBadgeProps {
6
+ count: number;
7
+ }
8
+
9
+ export const UnreadBadge = memo(({ count }: UnreadBadgeProps) => {
10
+ if (count <= 0) return null;
11
+
12
+ const label = count > 99 ? '99+' : String(count);
13
+
14
+ return (
15
+ <KContainer.View center marginL="0.5rem" style={styles.badge}>
16
+ <KLabel.Text typo="Text2XsBold" color={KColors.white}>
17
+ {label}
18
+ </KLabel.Text>
19
+ </KContainer.View>
20
+ );
21
+ });
22
+
23
+ UnreadBadge.displayName = 'UnreadBadge';
24
+
25
+ const styles = StyleSheet.create({
26
+ badge: {
27
+ minWidth: 19,
28
+ height: 19,
29
+ borderRadius: 32,
30
+ paddingHorizontal: 3,
31
+ backgroundColor: KColors.palette.danger.w400,
32
+ borderWidth: 2,
33
+ borderColor: KColors.white,
34
+ },
35
+ });
@@ -0,0 +1,4 @@
1
+ export { default as ThreadCard } from './ThreadCard';
2
+ export { AvatarSection } from './AvatarSection';
3
+ export { NamePrefixIcon } from './NamePrefixIcon';
4
+ export { UnreadBadge } from './UnreadBadge';
@@ -0,0 +1,68 @@
1
+ import { MessageType } from '@droppii/openim-rn-client-sdk';
2
+ import type { DMessageItem } from '../../types/chat';
3
+
4
+ const extractTextContent = (message: DMessageItem): string => {
5
+ if (message.textElem?.content) return message.textElem.content;
6
+ try {
7
+ const parsed = JSON.parse(message.content) as { content?: string };
8
+ return parsed.content ?? '';
9
+ } catch {
10
+ return message.content ?? '';
11
+ }
12
+ };
13
+
14
+ export const getLastMessageText = (
15
+ message: DMessageItem | undefined,
16
+ currentUserId?: string
17
+ ): string => {
18
+ if (!message) return '';
19
+ if (message.contentType !== MessageType.TextMessage) return '';
20
+
21
+ const text = extractTextContent(message);
22
+ if (currentUserId && message.sendID === currentUserId) {
23
+ return `Bạn: ${text}`;
24
+ }
25
+ return text;
26
+ };
27
+
28
+ export const formatTimestamp = (ts: number | string | null): string => {
29
+ if (!ts) return '';
30
+
31
+ try {
32
+ const date = new Date(ts);
33
+ if (isNaN(date.getTime())) return '';
34
+
35
+ const now = new Date();
36
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
37
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
38
+ const dateOnly = new Date(
39
+ date.getFullYear(),
40
+ date.getMonth(),
41
+ date.getDate()
42
+ );
43
+
44
+ if (dateOnly.getTime() === today.getTime()) {
45
+ const h = date.getHours().toString().padStart(2, '0');
46
+ const m = date.getMinutes().toString().padStart(2, '0');
47
+ return `${h}:${m}`;
48
+ }
49
+
50
+ if (dateOnly.getTime() === yesterday.getTime()) {
51
+ return 'Hôm qua';
52
+ }
53
+
54
+ const daysAgo = Math.floor(
55
+ (today.getTime() - dateOnly.getTime()) / (24 * 60 * 60 * 1000)
56
+ );
57
+ if (daysAgo > 0 && daysAgo < 7) {
58
+ const dayNames = ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'];
59
+ return dayNames[date.getDay()] ?? '';
60
+ }
61
+
62
+ const d = date.getDate().toString().padStart(2, '0');
63
+ const mo = (date.getMonth() + 1).toString().padStart(2, '0');
64
+ return `${d}/${mo}`;
65
+ } catch {
66
+ return '';
67
+ }
68
+ };
@@ -0,0 +1,10 @@
1
+ import OpenIMSDK from '@droppii/openim-rn-client-sdk';
2
+ import { useUserListener } from './useUserListener';
3
+ import { useChatListener } from './useChatListener';
4
+
5
+ export const initChatSdk = OpenIMSDK.initSDK.bind(OpenIMSDK);
6
+
7
+ export const useChatSdk = () => {
8
+ useUserListener();
9
+ useChatListener();
10
+ };
@@ -0,0 +1,85 @@
1
+ import OpenIMSDK, {
2
+ OpenIMEvent,
3
+ type ConversationItem,
4
+ type GroupMemberItem,
5
+ type MessageItem,
6
+ type RevokedInfo,
7
+ } from '@droppii/openim-rn-client-sdk';
8
+ import { useCallback, useEffect } from 'react';
9
+ import { useConversationStore } from '../store/conversation';
10
+ import { getConversationID } from '../utils/conversation';
11
+
12
+ export const useChatListener = () => {
13
+ const onRecvNewMessages = useCallback((messages: MessageItem[]) => {
14
+ console.log('onRecvNewMessages', messages);
15
+ messages.map((message) => {
16
+ const conversationID = getConversationID(
17
+ message.sessionType,
18
+ message.sendID,
19
+ message.recvID
20
+ );
21
+ useConversationStore
22
+ .getState()
23
+ .updateLastMessage(conversationID, message);
24
+ });
25
+ }, []);
26
+
27
+ const onNewRecvMessageRevoked = useCallback((revokedInfo: RevokedInfo) => {
28
+ console.log('onNewRecvMessageRevoked', revokedInfo);
29
+ }, []);
30
+
31
+ const onConversationChanged = useCallback(
32
+ (conversations: ConversationItem[]) => {
33
+ console.log('onConversationChanged', conversations);
34
+ useConversationStore.getState().mergeOpenIMConversations(conversations);
35
+ },
36
+ []
37
+ );
38
+
39
+ const onNewConversation = useCallback((conversations: ConversationItem[]) => {
40
+ console.log('onNewConversation', conversations);
41
+ // mergeOpenIMConversations increments newConversationSignal when a conversation
42
+ // isn't in the store yet, which triggers a full refetch in useConversationList.
43
+ useConversationStore.getState().mergeOpenIMConversations(conversations);
44
+ }, []);
45
+
46
+ const onGroupMemberAdded = useCallback((member: GroupMemberItem) => {
47
+ console.log('onGroupMemberAdded', member);
48
+ }, []);
49
+
50
+ const onGroupMemberDeleted = useCallback((member: GroupMemberItem) => {
51
+ console.log('onGroupMemberDeleted', member);
52
+ }, []);
53
+
54
+ useEffect(() => {
55
+ // Message
56
+ OpenIMSDK.on(OpenIMEvent.OnRecvNewMessages, onRecvNewMessages);
57
+ OpenIMSDK.on(OpenIMEvent.OnNewRecvMessageRevoked, onNewRecvMessageRevoked);
58
+
59
+ // Conversation
60
+ OpenIMSDK.on(OpenIMEvent.OnConversationChanged, onConversationChanged);
61
+ OpenIMSDK.on(OpenIMEvent.OnNewConversation, onNewConversation);
62
+
63
+ // Group members
64
+ OpenIMSDK.on(OpenIMEvent.OnGroupMemberAdded, onGroupMemberAdded);
65
+ OpenIMSDK.on(OpenIMEvent.OnGroupMemberDeleted, onGroupMemberDeleted);
66
+
67
+ return () => {
68
+ // Message
69
+ OpenIMSDK.off(OpenIMEvent.OnRecvNewMessages, onRecvNewMessages);
70
+ OpenIMSDK.off(
71
+ OpenIMEvent.OnNewRecvMessageRevoked,
72
+ onNewRecvMessageRevoked
73
+ );
74
+
75
+ // Conversation
76
+ OpenIMSDK.off(OpenIMEvent.OnConversationChanged, onConversationChanged);
77
+ OpenIMSDK.off(OpenIMEvent.OnNewConversation, onNewConversation);
78
+
79
+ // Group members
80
+ OpenIMSDK.off(OpenIMEvent.OnGroupMemberAdded, onGroupMemberAdded);
81
+ OpenIMSDK.off(OpenIMEvent.OnGroupMemberDeleted, onGroupMemberDeleted);
82
+ };
83
+ // eslint-disable-next-line react-hooks/exhaustive-deps
84
+ }, []);
85
+ };
@@ -0,0 +1,86 @@
1
+ import OpenIMSDK, {
2
+ LoginStatus,
3
+ OpenIMEvent,
4
+ } from '@droppii/openim-rn-client-sdk';
5
+ import { useCallback, useEffect } from 'react';
6
+ import { useUserStore } from '../store';
7
+ import { SyncStatus } from '../types/auth';
8
+ import { ChatAPI } from '../services';
9
+
10
+ export const useUserListener = () => {
11
+ const isLoggedIn = useUserStore(
12
+ (state) => state.connectStatus === LoginStatus.Logged
13
+ );
14
+
15
+ const fetchAndUpdateUser = useCallback(async () => {
16
+ try {
17
+ const user = await ChatAPI.getUser();
18
+ useUserStore.getState().setUser(user);
19
+ } catch (error) {
20
+ console.error('OpenIM get user failed', error);
21
+ }
22
+ }, []);
23
+
24
+ useEffect(() => {
25
+ ChatAPI.getLoginStatus().then((value) => {
26
+ if (value) {
27
+ useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
28
+ }
29
+ });
30
+ }, []);
31
+
32
+ useEffect(() => {
33
+ if (isLoggedIn) {
34
+ fetchAndUpdateUser();
35
+ }
36
+ }, [isLoggedIn, fetchAndUpdateUser]);
37
+
38
+ const onConnecting = useCallback(() => {
39
+ useUserStore.getState().updateConnectStatus(LoginStatus.Logging);
40
+ }, []);
41
+
42
+ const onConnectSuccess = useCallback(() => {
43
+ useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
44
+ }, []);
45
+
46
+ const onConnectFailed = useCallback(() => {
47
+ useUserStore.getState().updateConnectStatus(LoginStatus.Logout);
48
+ }, []);
49
+
50
+ const onSyncServerStart = useCallback(() => {
51
+ useUserStore.getState().updateSyncStatus(SyncStatus.Loading);
52
+ }, []);
53
+
54
+ const onSyncServerFinish = useCallback(() => {
55
+ useUserStore.getState().updateSyncStatus(SyncStatus.Success);
56
+ }, []);
57
+
58
+ const onSyncServerFailed = useCallback(() => {
59
+ useUserStore.getState().updateSyncStatus(SyncStatus.Failed);
60
+ }, []);
61
+
62
+ useEffect(() => {
63
+ // User
64
+ OpenIMSDK.on(OpenIMEvent.OnConnecting, onConnecting);
65
+ OpenIMSDK.on(OpenIMEvent.OnConnectSuccess, onConnectSuccess);
66
+ OpenIMSDK.on(OpenIMEvent.OnConnectFailed, onConnectFailed);
67
+
68
+ // Sync
69
+ OpenIMSDK.on(OpenIMEvent.OnSyncServerStart, onSyncServerStart);
70
+ OpenIMSDK.on(OpenIMEvent.OnSyncServerFinish, onSyncServerFinish);
71
+ OpenIMSDK.on(OpenIMEvent.OnSyncServerFailed, onSyncServerFailed);
72
+
73
+ return () => {
74
+ // User
75
+ OpenIMSDK.off(OpenIMEvent.OnConnecting, onConnecting);
76
+ OpenIMSDK.off(OpenIMEvent.OnConnectSuccess, onConnectSuccess);
77
+ OpenIMSDK.off(OpenIMEvent.OnConnectFailed, onConnectFailed);
78
+
79
+ // Sync
80
+ OpenIMSDK.off(OpenIMEvent.OnSyncServerStart, onSyncServerStart);
81
+ OpenIMSDK.off(OpenIMEvent.OnSyncServerFinish, onSyncServerFinish);
82
+ OpenIMSDK.off(OpenIMEvent.OnSyncServerFailed, onSyncServerFailed);
83
+ };
84
+ // eslint-disable-next-line react-hooks/exhaustive-deps
85
+ }, []);
86
+ };