@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
@@ -0,0 +1,137 @@
1
+ import { memo, useCallback } from 'react';
2
+ import { ActivityIndicator, StyleSheet } from 'react-native';
3
+ import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
4
+ import FileViewer from 'react-native-file-viewer';
5
+ import UIUtils from '../../utils/ui';
6
+ import { trans } from '../../translation';
7
+ import { formatFileSize, getFileIcon } from '../../utils/imageUtils';
8
+ import type { AttachmentItem } from '../../types/attachment';
9
+
10
+ export const FilePreview = memo(
11
+ ({
12
+ file,
13
+ onRemove,
14
+ }: {
15
+ file: AttachmentItem;
16
+ onRemove: (id: string) => void;
17
+ }) => {
18
+ const handlePress = useCallback(async () => {
19
+ if (file.isUploading || file.error) return;
20
+ try {
21
+ const path = file.metadata.uri?.startsWith('file://')
22
+ ? file.metadata.uri.slice(7)
23
+ : file.metadata.uri;
24
+ await FileViewer.open(path ?? '', {
25
+ showOpenWithDialog: true,
26
+ showAppsSuggestions: true,
27
+ });
28
+ } catch {
29
+ UIUtils.toast.open({
30
+ title: trans('chat:file_preview_unsupported'),
31
+ theme: 'warning',
32
+ });
33
+ }
34
+ }, [file]);
35
+
36
+ const handleRemove = useCallback(() => {
37
+ onRemove(file.metadata.id ?? '');
38
+ }, [file.metadata?.id, onRemove]);
39
+
40
+ const iconSource = getFileIcon(file.metadata.mimeType);
41
+ const hasError = !!file.error;
42
+
43
+ return (
44
+ <KContainer.Touchable
45
+ row
46
+ alignItems
47
+ width={160}
48
+ onPress={handlePress}
49
+ br="3x"
50
+ background={
51
+ !hasError
52
+ ? KColors.hexToRgba(KColors.palette.gray.w400, 0.1)
53
+ : KColors.hexToRgba(KColors.danger.light, 0.2)
54
+ }
55
+ paddingL={'0.5rem'}
56
+ paddingR={'1rem'}
57
+ paddingV={'0.25rem'}
58
+ >
59
+ {/* File icon */}
60
+ <KContainer.VisibleView visible={file.isUploading}>
61
+ <ActivityIndicator
62
+ size="small"
63
+ color={KColors.palette.primary.w400}
64
+ style={styles.loader}
65
+ />
66
+ </KContainer.VisibleView>
67
+ <KContainer.VisibleView visible={!file.isUploading}>
68
+ <KImage.Base
69
+ uri={iconSource}
70
+ style={styles.icon}
71
+ resizeMode="contain"
72
+ />
73
+ </KContainer.VisibleView>
74
+
75
+ {/* File info */}
76
+ <KContainer.View flex>
77
+ <KLabel.Text
78
+ typo="TextSmMedium"
79
+ color={KColors.gray.dark}
80
+ numberOfLines={1}
81
+ ellipsizeMode="middle"
82
+ >
83
+ {file.metadata.filename}
84
+ </KLabel.Text>
85
+ <KLabel.Text
86
+ typo="TextXsNormal"
87
+ color={KColors.palette.gray.w500}
88
+ numberOfLines={1}
89
+ >
90
+ {hasError
91
+ ? file.error
92
+ : file.isUploading
93
+ ? `${file.uploadProgress}%`
94
+ : formatFileSize(file.metadata.size ?? 0)}
95
+ </KLabel.Text>
96
+ </KContainer.View>
97
+
98
+ {/* Remove button */}
99
+ <KContainer.Touchable
100
+ onPress={handleRemove}
101
+ center
102
+ width={20}
103
+ height={20}
104
+ br={'round'}
105
+ background={KColors.gray.dark}
106
+ hitSlop={8}
107
+ style={styles.removeBtn}
108
+ >
109
+ <KImage.VectorIcons
110
+ name="close"
111
+ provider="MaterialCommunityIcons"
112
+ size={18}
113
+ color={KColors.white}
114
+ />
115
+ </KContainer.Touchable>
116
+ </KContainer.Touchable>
117
+ );
118
+ }
119
+ );
120
+
121
+ FilePreview.displayName = 'FilePreview';
122
+
123
+ const styles = StyleSheet.create({
124
+ icon: {
125
+ width: 20,
126
+ height: 20,
127
+ marginRight: 7,
128
+ },
129
+ loader: {
130
+ marginRight: 8,
131
+ },
132
+ removeBtn: {
133
+ position: 'absolute',
134
+ right: -4,
135
+ top: -4,
136
+ },
137
+ });
@@ -0,0 +1,238 @@
1
+ import { memo } from 'react';
2
+ import { KColors, KContainer, KImage, KLabel } from '@droppii/libs';
3
+ import type { AttachmentItem, AttachmentType } from '../../types/attachment';
4
+ import { StyleSheet } from 'react-native';
5
+
6
+ type Props = {
7
+ item: AttachmentItem;
8
+ index: number;
9
+ onRemove(type: AttachmentType, index: number): void;
10
+ };
11
+
12
+ const formatDuration = (seconds: number): string => {
13
+ const totalSecs = Math.floor(seconds);
14
+ const mins = Math.floor(totalSecs / 60);
15
+ const secs = totalSecs % 60;
16
+ return `${mins.toString().padStart(2, '0')}:${secs
17
+ .toString()
18
+ .padStart(2, '0')}`;
19
+ };
20
+
21
+ const getAttachmentIcon = (type: AttachmentType): string => {
22
+ switch (type) {
23
+ case 'image':
24
+ return 'image';
25
+ case 'video':
26
+ return 'play-circle';
27
+ case 'audio':
28
+ return 'music';
29
+ case 'document':
30
+ return 'file-document';
31
+ case 'file':
32
+ default:
33
+ return 'file';
34
+ }
35
+ };
36
+
37
+ export const MediaPreview = memo(({ item, index, onRemove }: Props) => {
38
+ const { type, metadata, uploadProgress, error, isUploading, thumbnailUrl } =
39
+ item;
40
+
41
+ const isImage = type === 'image';
42
+ const isVideo = type === 'video';
43
+ const hasPreview = isImage || isVideo;
44
+
45
+ return (
46
+ <KContainer.View>
47
+ <KContainer.View style={styles.thumbnail} center>
48
+ {/* Video thumbnail */}
49
+ {isVideo && thumbnailUrl && (
50
+ <KImage.Base
51
+ uri={thumbnailUrl}
52
+ width={48}
53
+ height={48}
54
+ resizeMode="cover"
55
+ />
56
+ )}
57
+
58
+ {/* Video placeholder (no thumbnail yet) */}
59
+ {isVideo && !thumbnailUrl && (
60
+ <KContainer.View style={[styles.videoBg, styles.videoThumbnail]} />
61
+ )}
62
+
63
+ {/* Image preview */}
64
+ {isImage && (
65
+ <KImage.Base uri={`file://${metadata.path}`} width={48} height={48} />
66
+ )}
67
+
68
+ {/* Show icon overlay for non-media files */}
69
+ {!hasPreview && (
70
+ <KContainer.View
71
+ style={styles.iconOverlay}
72
+ center
73
+ background={KColors.palette.gray.w100}
74
+ >
75
+ <KImage.VectorIcons
76
+ name={getAttachmentIcon(type)}
77
+ provider="MaterialCommunityIcons"
78
+ size={24}
79
+ color={KColors.palette.primary.w400}
80
+ />
81
+ </KContainer.View>
82
+ )}
83
+
84
+ {/* Video duration badge */}
85
+ {isVideo && !!metadata.duration && (
86
+ <KContainer.View style={styles.durationBadge} center>
87
+ <KLabel.Text
88
+ typo="TextXsNormal"
89
+ color={KColors.white}
90
+ style={styles.durationText}
91
+ >
92
+ {formatDuration(metadata.duration / 1000)}
93
+ </KLabel.Text>
94
+ </KContainer.View>
95
+ )}
96
+
97
+ {/* Type icon for non-preview items */}
98
+ {!hasPreview && (
99
+ <KContainer.View style={styles.typeIcon} center>
100
+ <KImage.VectorIcons
101
+ name={getAttachmentIcon(type)}
102
+ provider="MaterialCommunityIcons"
103
+ size={14}
104
+ color={KColors.white}
105
+ />
106
+ </KContainer.View>
107
+ )}
108
+
109
+ {/* Error state */}
110
+ {error && (
111
+ <KContainer.View style={styles.errorOverlay} center>
112
+ <KImage.VectorIcons
113
+ name="close-circle"
114
+ provider="MaterialCommunityIcons"
115
+ size={28}
116
+ color={KColors.white}
117
+ />
118
+ </KContainer.View>
119
+ )}
120
+
121
+ {/* Progress overlay */}
122
+ {isUploading && (
123
+ <KContainer.View style={styles.progressOverlay} center>
124
+ <KLabel.Text typo="TextSmNormal" color={KColors.white}>
125
+ {uploadProgress}%
126
+ </KLabel.Text>
127
+ </KContainer.View>
128
+ )}
129
+ </KContainer.View>
130
+ {/* Remove button */}
131
+ <KContainer.Touchable
132
+ style={styles.removeButton}
133
+ center
134
+ br="round"
135
+ background={KColors.black}
136
+ onPress={() => onRemove(type, index)}
137
+ disabled={isUploading}
138
+ hitSlop={10}
139
+ >
140
+ <KImage.VectorIcons
141
+ name="close"
142
+ provider="MaterialCommunityIcons"
143
+ size={18}
144
+ color={KColors.white}
145
+ />
146
+ </KContainer.Touchable>
147
+ </KContainer.View>
148
+ );
149
+ });
150
+
151
+ MediaPreview.displayName = 'MediaPreview';
152
+
153
+ const styles = StyleSheet.create({
154
+ thumbnail: {
155
+ position: 'relative',
156
+ width: 48,
157
+ height: 48,
158
+ borderRadius: 4,
159
+ overflow: 'hidden',
160
+ backgroundColor: KColors.palette.gray.w50,
161
+ },
162
+ durationBadge: {
163
+ position: 'absolute',
164
+ bottom: 4,
165
+ right: 4,
166
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
167
+ borderRadius: 3,
168
+ paddingVertical: 2,
169
+ paddingHorizontal: 4,
170
+ },
171
+ durationText: {
172
+ fontSize: 11,
173
+ fontWeight: '600',
174
+ },
175
+ iconOverlay: {
176
+ width: 48,
177
+ height: 48,
178
+ borderRadius: 4,
179
+ },
180
+ videoIcon: {
181
+ position: 'absolute',
182
+ top: 2,
183
+ right: 2,
184
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
185
+ borderRadius: 8,
186
+ width: 20,
187
+ height: 20,
188
+ },
189
+ typeIcon: {
190
+ position: 'absolute',
191
+ bottom: 2,
192
+ right: 2,
193
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
194
+ borderRadius: 6,
195
+ width: 16,
196
+ height: 16,
197
+ },
198
+ videoBg: {
199
+ width: 48,
200
+ height: 48,
201
+ borderRadius: 4,
202
+ },
203
+ videoThumbnail: {
204
+ backgroundColor: KColors.black,
205
+ },
206
+ progressOverlay: {
207
+ position: 'absolute',
208
+ top: 0,
209
+ left: 0,
210
+ right: 0,
211
+ bottom: 0,
212
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
213
+ },
214
+ errorOverlay: {
215
+ position: 'absolute',
216
+ top: 0,
217
+ left: 0,
218
+ right: 0,
219
+ bottom: 0,
220
+ backgroundColor: 'rgba(255, 59, 48, 0.8)',
221
+ },
222
+ removeButton: {
223
+ position: 'absolute',
224
+ right: -4,
225
+ top: -4,
226
+ width: 22,
227
+ height: 22,
228
+ shadowColor: '#000',
229
+ shadowOffset: { width: 0, height: 1 },
230
+ shadowOpacity: 0.2,
231
+ shadowRadius: 2,
232
+ elevation: 3,
233
+ },
234
+ sendButton: {
235
+ width: 48,
236
+ height: 48,
237
+ },
238
+ });
@@ -0,0 +1,68 @@
1
+ import { memo, useCallback } from 'react';
2
+ import { ScrollView, StyleSheet } from 'react-native';
3
+ import type { AttachmentItem, AttachmentType } from '../../types/attachment';
4
+ import { MediaPreview } from './MediaPreview';
5
+ import { FilePreview } from './FilePreview';
6
+
7
+ interface AttachmentPreviewProps {
8
+ visible: boolean;
9
+ attachments?: Array<AttachmentItem>;
10
+ onRemove: (type: AttachmentType, data: unknown) => void;
11
+ }
12
+
13
+ export const AttachmentPreview = memo(
14
+ ({ visible, attachments, onRemove }: AttachmentPreviewProps) => {
15
+ const onFileRemove = useCallback(
16
+ (id: string) => {
17
+ onRemove('file', id);
18
+ },
19
+ [onRemove]
20
+ );
21
+
22
+ if (!visible || !attachments?.length) {
23
+ return null;
24
+ }
25
+
26
+ return (
27
+ <ScrollView
28
+ horizontal
29
+ showsHorizontalScrollIndicator={false}
30
+ scrollEventThrottle={16}
31
+ contentContainerStyle={styles.scrollContent}
32
+ >
33
+ {attachments?.map((item, index) => {
34
+ switch (item.type) {
35
+ case 'file':
36
+ return (
37
+ <FilePreview
38
+ key={`${item.type}-${index}`}
39
+ file={item}
40
+ onRemove={onFileRemove}
41
+ />
42
+ );
43
+
44
+ default:
45
+ return (
46
+ <MediaPreview
47
+ key={`${item.type}-${index}`}
48
+ index={index}
49
+ item={item}
50
+ onRemove={onRemove}
51
+ />
52
+ );
53
+ }
54
+ })}
55
+ </ScrollView>
56
+ );
57
+ }
58
+ );
59
+
60
+ AttachmentPreview.displayName = 'AttachmentPreview';
61
+
62
+ const styles = StyleSheet.create({
63
+ scrollContent: {
64
+ padding: 8,
65
+ paddingTop: 12,
66
+ gap: 8,
67
+ },
68
+ });
@@ -1,10 +1,9 @@
1
1
  import { memo } from 'react';
2
2
  import { Avatar } from '../Avatar';
3
- import type { DConversationItem } from '../../types/chat';
4
- import { PeerType } from '@droppii/openim-rn-client-sdk';
3
+ import { PeerType, type ConversationItem } from '@droppii/openim-rn-client-sdk';
5
4
 
6
5
  interface AvatarSectionProps extends Partial<
7
- Pick<DConversationItem, 'peerType' | 'applicationType'>
6
+ Pick<ConversationItem, 'peerType'>
8
7
  > {
9
8
  avatar: string | null;
10
9
  fullName: string;
@@ -1,4 +1,4 @@
1
- import { memo, useCallback } from 'react';
1
+ import { memo, useCallback, useMemo } from 'react';
2
2
  import { type GestureResponderEvent } from 'react-native';
3
3
  import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
4
4
  import { AvatarSection } from './AvatarSection';
@@ -7,6 +7,7 @@ import { UnreadBadge } from './UnreadBadge';
7
7
  import { formatTimestamp } from '../../utils/dateTimeUtils';
8
8
  import { getLastMessageText } from '../../utils/messageUtils';
9
9
  import { useConversation, useUserStore } from '../../store';
10
+ import type { MessageItem } from '@droppii/openim-rn-client-sdk';
10
11
 
11
12
  interface ThreadCardProps {
12
13
  item: string;
@@ -14,26 +15,41 @@ interface ThreadCardProps {
14
15
  }
15
16
 
16
17
  const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
18
+ const consversation = useConversation(item);
19
+
17
20
  const {
18
21
  peerType,
19
22
  showName,
20
23
  faceURL,
21
- lastMessage,
22
24
  unreadCount = 0,
23
25
  recvMsgOpt,
24
26
  isPinned,
25
- applicationType,
26
27
  latestMsgSendTime,
27
- } = useConversation(item) ?? {};
28
+ latestMsg,
29
+ } = consversation || {};
30
+
31
+ const latestMsgData: MessageItem = useMemo(() => {
32
+ let result = null;
33
+ try {
34
+ result = JSON.parse(latestMsg || '{}');
35
+ } catch (error) {
36
+ console.error('Failed to parse ex conversation', error);
37
+ }
38
+ return result;
39
+ }, [latestMsg]);
28
40
 
29
41
  const fullName = showName ?? '';
30
42
  const hasUnread = unreadCount > 0;
31
43
  const isMuted = (recvMsgOpt ?? 0) !== 0;
32
44
  const timestamp = formatTimestamp(
33
- latestMsgSendTime ?? lastMessage?.sendTime ?? null
45
+ latestMsgSendTime ?? latestMsgData?.sendTime ?? null
34
46
  );
35
47
  const currentUserId = useUserStore((s) => s.user?.userID);
36
- const lastMessageText = getLastMessageText(lastMessage, currentUserId);
48
+ const lastMessageText = getLastMessageText(
49
+ latestMsgData,
50
+ currentUserId,
51
+ consversation
52
+ );
37
53
 
38
54
  const handlePress = useCallback(
39
55
  (event: GestureResponderEvent) => {
@@ -55,7 +71,6 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
55
71
  avatar={faceURL ?? null}
56
72
  fullName={fullName}
57
73
  peerType={peerType}
58
- applicationType={applicationType}
59
74
  />
60
75
 
61
76
  {/* Chat-Content */}
@@ -63,14 +78,6 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
63
78
  {/* Row-Title */}
64
79
  <KContainer.View row alignItems="center">
65
80
  <NamePrefixIcon peerType={peerType} />
66
- {/* For verified PDP (configuration details to be defined later) */}
67
- {/* <KContainer.VisibleView visible={peerType === DChatPeerType.BOT}>
68
- <KImage.Base
69
- uri={Images.ICON_BOT}
70
- size={14}
71
- style={styles.botIcon}
72
- />
73
- </KContainer.VisibleView> */}
74
81
  <KLabel.Text
75
82
  typo={hasUnread ? 'TextMdBold' : 'TextMdNormal'}
76
83
  numberOfLines={1}
@@ -50,7 +50,7 @@ export const InputButtonFlow = memo((props: IInputButtonFlow) => {
50
50
  style={styles.buttonItem}
51
51
  onPress={() => onPressItem(item)}
52
52
  >
53
- <KLabel.Text typo="TextMdMedium">
53
+ <KLabel.Text typo="TextMdMedium" flex>
54
54
  {item?.id === DEFAULT_BUTTON_ID
55
55
  ? trans('restart')
56
56
  : item?.label || ''}
@@ -87,11 +87,13 @@ const styles = StyleSheet.create({
87
87
  borderRadius: KSpacingValue['1.25rem'],
88
88
  ...messageStyles.bubbleReceived,
89
89
  marginVertical: KSpacingValue['0.5rem'],
90
+ width: '100%',
90
91
  },
91
92
  buttonsContainer: {
92
93
  backgroundColor: KColors.white,
93
94
  borderRadius: KRadiusValue['3x'],
94
95
  paddingHorizontal: KSpacingValue['0.75rem'],
96
+ width: '100%',
95
97
  },
96
98
  buttonItem: {
97
99
  flexDirection: 'row',
@@ -101,5 +103,6 @@ const styles = StyleSheet.create({
101
103
  borderBottomWidth: 1,
102
104
  borderBottomColor: KColors.border.light,
103
105
  paddingVertical: KSpacingValue['0.5rem'],
106
+ width: '100%',
104
107
  },
105
108
  });