@droppii-org/chat-mobile 0.2.79 → 0.2.81

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 (187) hide show
  1. package/lib/module/components/messages/BubbleTail.js +31 -0
  2. package/lib/module/components/messages/BubbleTail.js.map +1 -0
  3. package/lib/module/components/messages/BubbleTailRow.js +46 -0
  4. package/lib/module/components/messages/BubbleTailRow.js.map +1 -0
  5. package/lib/module/components/messages/bubbleTail.utils.js +19 -0
  6. package/lib/module/components/messages/bubbleTail.utils.js.map +1 -0
  7. package/lib/module/components/messages/fileMessage/index.js +72 -65
  8. package/lib/module/components/messages/fileMessage/index.js.map +1 -1
  9. package/lib/module/components/messages/imageMessage/index.js +23 -17
  10. package/lib/module/components/messages/imageMessage/index.js.map +1 -1
  11. package/lib/module/components/messages/linkMessage/index.js +11 -23
  12. package/lib/module/components/messages/linkMessage/index.js.map +1 -1
  13. package/lib/module/components/messages/mergedMessage/index.js +14 -7
  14. package/lib/module/components/messages/mergedMessage/index.js.map +1 -1
  15. package/lib/module/components/messages/quotedMessage/index.js +11 -23
  16. package/lib/module/components/messages/quotedMessage/index.js.map +1 -1
  17. package/lib/module/components/messages/revokedMessage/index.js +14 -7
  18. package/lib/module/components/messages/revokedMessage/index.js.map +1 -1
  19. package/lib/module/components/messages/stickerMessage/index.js +13 -7
  20. package/lib/module/components/messages/stickerMessage/index.js.map +1 -1
  21. package/lib/module/components/messages/textMessage/index.js +11 -26
  22. package/lib/module/components/messages/textMessage/index.js.map +1 -1
  23. package/lib/module/components/messages/types.js.map +1 -1
  24. package/lib/module/components/messages/videoMessage/index.js +16 -8
  25. package/lib/module/components/messages/videoMessage/index.js.map +1 -1
  26. package/lib/module/config/api-endpoints.js +2 -1
  27. package/lib/module/config/api-endpoints.js.map +1 -1
  28. package/lib/module/hooks/contacts/useSearchContacts.js +50 -0
  29. package/lib/module/hooks/contacts/useSearchContacts.js.map +1 -0
  30. package/lib/module/hooks/groups/useDismissGroup.js +2 -1
  31. package/lib/module/hooks/groups/useDismissGroup.js.map +1 -1
  32. package/lib/module/hooks/query-keys.js +4 -0
  33. package/lib/module/hooks/query-keys.js.map +1 -1
  34. package/lib/module/screens/SearchConversation/ContactResultItem.js +119 -0
  35. package/lib/module/screens/SearchConversation/ContactResultItem.js.map +1 -0
  36. package/lib/module/screens/SearchConversation/SearchAllResult.js +26 -20
  37. package/lib/module/screens/SearchConversation/SearchAllResult.js.map +1 -1
  38. package/lib/module/screens/SearchConversation/SearchCommunityResult.js +79 -0
  39. package/lib/module/screens/SearchConversation/SearchCommunityResult.js.map +1 -0
  40. package/lib/module/screens/SearchConversation/SearchContactsResult.js +117 -0
  41. package/lib/module/screens/SearchConversation/SearchContactsResult.js.map +1 -0
  42. package/lib/module/screens/SearchConversation/SearchEmpty.js +2 -0
  43. package/lib/module/screens/SearchConversation/SearchEmpty.js.map +1 -1
  44. package/lib/module/screens/SearchConversation/SearchMessagesResult.js +1 -8
  45. package/lib/module/screens/SearchConversation/SearchMessagesResult.js.map +1 -1
  46. package/lib/module/screens/SearchConversation/SearchResultItem.js +2 -0
  47. package/lib/module/screens/SearchConversation/SearchResultItem.js.map +1 -1
  48. package/lib/module/screens/SearchConversation/TabResult.js +24 -7
  49. package/lib/module/screens/SearchConversation/TabResult.js.map +1 -1
  50. package/lib/module/screens/SearchConversation/index.js +6 -2
  51. package/lib/module/screens/SearchConversation/index.js.map +1 -1
  52. package/lib/module/screens/SearchConversation/types.js +1 -0
  53. package/lib/module/screens/SearchConversation/types.js.map +1 -1
  54. package/lib/module/screens/chat-detail/ChatListLegend.js +71 -21
  55. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  56. package/lib/module/screens/chat-detail/legend/LegendChatDay.js +8 -2
  57. package/lib/module/screens/chat-detail/legend/LegendChatDay.js.map +1 -1
  58. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +38 -13
  59. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  60. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +11 -1
  61. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -1
  62. package/lib/module/services/endpoints.js +2 -1
  63. package/lib/module/services/endpoints.js.map +1 -1
  64. package/lib/module/translation/resources/i18n.js +6 -0
  65. package/lib/module/translation/resources/i18n.js.map +1 -1
  66. package/lib/module/types/chat.js +8 -0
  67. package/lib/module/types/chat.js.map +1 -1
  68. package/lib/module/utils/dateTimeUtils.js +10 -0
  69. package/lib/module/utils/dateTimeUtils.js.map +1 -1
  70. package/lib/module/utils/highlightSearch.js +6 -2
  71. package/lib/module/utils/highlightSearch.js.map +1 -1
  72. package/lib/module/utils/legendListMessage.js +86 -12
  73. package/lib/module/utils/legendListMessage.js.map +1 -1
  74. package/lib/module/utils/message.js +19 -1
  75. package/lib/module/utils/message.js.map +1 -1
  76. package/lib/module/utils/messageUtils.js +16 -2
  77. package/lib/module/utils/messageUtils.js.map +1 -1
  78. package/lib/typescript/src/components/messages/BubbleTail.d.ts +13 -0
  79. package/lib/typescript/src/components/messages/BubbleTail.d.ts.map +1 -0
  80. package/lib/typescript/src/components/messages/BubbleTailRow.d.ts +25 -0
  81. package/lib/typescript/src/components/messages/BubbleTailRow.d.ts.map +1 -0
  82. package/lib/typescript/src/components/messages/bubbleTail.utils.d.ts +11 -0
  83. package/lib/typescript/src/components/messages/bubbleTail.utils.d.ts.map +1 -0
  84. package/lib/typescript/src/components/messages/fileMessage/index.d.ts +1 -1
  85. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
  86. package/lib/typescript/src/components/messages/imageMessage/index.d.ts +1 -1
  87. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -1
  88. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +1 -1
  89. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  90. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts +1 -1
  91. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -1
  92. package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -1
  93. package/lib/typescript/src/components/messages/revokedMessage/index.d.ts +1 -1
  94. package/lib/typescript/src/components/messages/revokedMessage/index.d.ts.map +1 -1
  95. package/lib/typescript/src/components/messages/stickerMessage/index.d.ts +1 -1
  96. package/lib/typescript/src/components/messages/stickerMessage/index.d.ts.map +1 -1
  97. package/lib/typescript/src/components/messages/textMessage/index.d.ts +1 -1
  98. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
  99. package/lib/typescript/src/components/messages/types.d.ts +3 -0
  100. package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
  101. package/lib/typescript/src/components/messages/videoMessage/index.d.ts +1 -1
  102. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -1
  103. package/lib/typescript/src/config/api-endpoints.d.ts +1 -0
  104. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
  105. package/lib/typescript/src/hooks/contacts/useSearchContacts.d.ts +221 -0
  106. package/lib/typescript/src/hooks/contacts/useSearchContacts.d.ts.map +1 -0
  107. package/lib/typescript/src/hooks/groups/useDismissGroup.d.ts.map +1 -1
  108. package/lib/typescript/src/hooks/query-keys.d.ts +4 -0
  109. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  110. package/lib/typescript/src/screens/SearchConversation/ContactResultItem.d.ts +9 -0
  111. package/lib/typescript/src/screens/SearchConversation/ContactResultItem.d.ts.map +1 -0
  112. package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts +3 -2
  113. package/lib/typescript/src/screens/SearchConversation/SearchAllResult.d.ts.map +1 -1
  114. package/lib/typescript/src/screens/SearchConversation/SearchCommunityResult.d.ts +7 -0
  115. package/lib/typescript/src/screens/SearchConversation/SearchCommunityResult.d.ts.map +1 -0
  116. package/lib/typescript/src/screens/SearchConversation/SearchContactsResult.d.ts +8 -0
  117. package/lib/typescript/src/screens/SearchConversation/SearchContactsResult.d.ts.map +1 -0
  118. package/lib/typescript/src/screens/SearchConversation/SearchEmpty.d.ts.map +1 -1
  119. package/lib/typescript/src/screens/SearchConversation/SearchMessagesResult.d.ts.map +1 -1
  120. package/lib/typescript/src/screens/SearchConversation/SearchResultItem.d.ts.map +1 -1
  121. package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts +4 -2
  122. package/lib/typescript/src/screens/SearchConversation/TabResult.d.ts.map +1 -1
  123. package/lib/typescript/src/screens/SearchConversation/index.d.ts +4 -2
  124. package/lib/typescript/src/screens/SearchConversation/index.d.ts.map +1 -1
  125. package/lib/typescript/src/screens/SearchConversation/types.d.ts +2 -1
  126. package/lib/typescript/src/screens/SearchConversation/types.d.ts.map +1 -1
  127. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  128. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts +6 -1
  129. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts.map +1 -1
  130. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +14 -1
  131. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  132. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -1
  133. package/lib/typescript/src/services/endpoints.d.ts +1 -0
  134. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  135. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  136. package/lib/typescript/src/types/chat.d.ts +27 -0
  137. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  138. package/lib/typescript/src/utils/dateTimeUtils.d.ts +2 -0
  139. package/lib/typescript/src/utils/dateTimeUtils.d.ts.map +1 -1
  140. package/lib/typescript/src/utils/highlightSearch.d.ts.map +1 -1
  141. package/lib/typescript/src/utils/legendListMessage.d.ts +19 -4
  142. package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
  143. package/lib/typescript/src/utils/message.d.ts +17 -0
  144. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  145. package/lib/typescript/src/utils/messageUtils.d.ts +1 -0
  146. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  147. package/package.json +3 -1
  148. package/src/build-ignore.d.ts +2 -0
  149. package/src/components/messages/BubbleTail.tsx +35 -0
  150. package/src/components/messages/BubbleTailRow.tsx +64 -0
  151. package/src/components/messages/bubbleTail.utils.ts +21 -0
  152. package/src/components/messages/fileMessage/index.tsx +90 -83
  153. package/src/components/messages/imageMessage/index.tsx +26 -23
  154. package/src/components/messages/linkMessage/index.tsx +6 -18
  155. package/src/components/messages/mergedMessage/index.tsx +27 -15
  156. package/src/components/messages/quotedMessage/index.tsx +7 -19
  157. package/src/components/messages/revokedMessage/index.tsx +23 -14
  158. package/src/components/messages/stickerMessage/index.tsx +16 -11
  159. package/src/components/messages/textMessage/index.tsx +6 -22
  160. package/src/components/messages/types.ts +3 -0
  161. package/src/components/messages/videoMessage/index.tsx +13 -10
  162. package/src/config/api-endpoints.ts +2 -0
  163. package/src/hooks/contacts/useSearchContacts.ts +66 -0
  164. package/src/hooks/groups/useDismissGroup.tsx +1 -0
  165. package/src/hooks/query-keys.ts +6 -0
  166. package/src/screens/SearchConversation/ContactResultItem.tsx +128 -0
  167. package/src/screens/SearchConversation/SearchAllResult.tsx +38 -24
  168. package/src/screens/SearchConversation/SearchCommunityResult.tsx +96 -0
  169. package/src/screens/SearchConversation/SearchContactsResult.tsx +155 -0
  170. package/src/screens/SearchConversation/SearchEmpty.tsx +2 -0
  171. package/src/screens/SearchConversation/SearchMessagesResult.tsx +1 -6
  172. package/src/screens/SearchConversation/SearchResultItem.tsx +6 -1
  173. package/src/screens/SearchConversation/TabResult.tsx +40 -5
  174. package/src/screens/SearchConversation/index.tsx +10 -2
  175. package/src/screens/SearchConversation/types.tsx +1 -0
  176. package/src/screens/chat-detail/ChatListLegend.tsx +97 -28
  177. package/src/screens/chat-detail/legend/LegendChatDay.tsx +25 -15
  178. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +62 -12
  179. package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +22 -4
  180. package/src/services/endpoints.ts +1 -0
  181. package/src/translation/resources/i18n.ts +6 -0
  182. package/src/types/chat.ts +30 -0
  183. package/src/utils/dateTimeUtils.ts +15 -0
  184. package/src/utils/highlightSearch.ts +12 -2
  185. package/src/utils/legendListMessage.ts +123 -14
  186. package/src/utils/message.ts +19 -1
  187. package/src/utils/messageUtils.ts +23 -9
@@ -3,6 +3,8 @@ import { StyleSheet } from 'react-native';
3
3
  import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
4
4
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
5
5
  import { messageStyles } from '../styles';
6
+ import { getTailCornerStyle } from '../bubbleTail.utils';
7
+ import { BubbleTailRow } from '../BubbleTailRow';
6
8
  import type { BaseLegendMessageProps } from '../types';
7
9
  import { useFileDownload } from '../../../hooks/useFileDownload';
8
10
  import {
@@ -12,7 +14,7 @@ import {
12
14
  } from '../../../utils/imageUtils';
13
15
  import { trans } from '../../../translation';
14
16
  export const FileMessage = memo(
15
- ({ message, isOutgoing }: BaseLegendMessageProps) => {
17
+ ({ message, isOutgoing, groupPosition }: BaseLegendMessageProps) => {
16
18
  const fileName = message.fileElem?.fileName || '[File]';
17
19
  const fileSize = message.fileElem?.fileSize ?? 0;
18
20
  const url =
@@ -49,97 +51,102 @@ export const FileMessage = memo(
49
51
  const isDownloading = state.status === 'downloading';
50
52
 
51
53
  return (
52
- <KContainer.View
53
- padding={'0.5rem'}
54
- style={[
55
- styles.wrapper,
56
- isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
57
- ]}
58
- >
54
+ <BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
59
55
  <KContainer.View
60
- row
61
- alignItems
62
- br={'3x'}
63
- paddingR={'0.75rem'}
64
- paddingV={'0.25rem'}
65
- background={KColors.white}
56
+ padding={'0.5rem'}
57
+ style={[
58
+ styles.wrapper,
59
+ isOutgoing
60
+ ? messageStyles.bubbleSent
61
+ : messageStyles.bubbleReceived,
62
+ getTailCornerStyle(isOutgoing, groupPosition),
63
+ ]}
66
64
  >
67
- <KContainer.VisibleView visible={isDownloading}>
68
- <KContainer.Touchable
69
- center
70
- br="round"
71
- style={styles.cancelBtn}
72
- width={32}
73
- height={32}
74
- onPress={handleDownload}
75
- >
76
- <KContainer.View
65
+ <KContainer.View
66
+ row
67
+ alignItems
68
+ br={'3x'}
69
+ paddingR={'0.75rem'}
70
+ paddingV={'0.25rem'}
71
+ background={KColors.white}
72
+ >
73
+ <KContainer.VisibleView visible={isDownloading}>
74
+ <KContainer.Touchable
77
75
  center
78
76
  br="round"
79
- style={styles.cancelBtnIconContainer}
80
- width={26}
81
- height={26}
77
+ style={styles.cancelBtn}
78
+ width={32}
79
+ height={32}
80
+ onPress={handleDownload}
82
81
  >
83
- <KImage.VectorIcons
84
- name="close"
85
- provider="MaterialCommunityIcons"
86
- size={24}
87
- color={KColors.white}
88
- />
89
- </KContainer.View>
90
- </KContainer.Touchable>
91
- </KContainer.VisibleView>
92
- <KContainer.VisibleView visible={!isDownloading}>
93
- <KImage.Base
94
- uri={iconSource}
95
- style={styles.fileIcon}
96
- resizeMode="contain"
97
- />
98
- </KContainer.VisibleView>
82
+ <KContainer.View
83
+ center
84
+ br="round"
85
+ style={styles.cancelBtnIconContainer}
86
+ width={26}
87
+ height={26}
88
+ >
89
+ <KImage.VectorIcons
90
+ name="close"
91
+ provider="MaterialCommunityIcons"
92
+ size={24}
93
+ color={KColors.white}
94
+ />
95
+ </KContainer.View>
96
+ </KContainer.Touchable>
97
+ </KContainer.VisibleView>
98
+ <KContainer.VisibleView visible={!isDownloading}>
99
+ <KImage.Base
100
+ uri={iconSource}
101
+ style={styles.fileIcon}
102
+ resizeMode="contain"
103
+ />
104
+ </KContainer.VisibleView>
99
105
 
100
- {/* File name + size/progress */}
101
- <KContainer.View flexS={1}>
102
- <KLabel.Text
103
- typo="TextSmMedium"
104
- color={CHAT_BUBBLE_COLORS.text}
105
- numberOfLines={1}
106
- ellipsizeMode="middle"
107
- >
108
- {fileName}
109
- </KLabel.Text>
110
- <KLabel.Text
111
- typo="TextXsNormal"
112
- color={subtitleColor}
113
- numberOfLines={1}
114
- >
115
- {subtitle}
116
- </KLabel.Text>
117
- </KContainer.View>
106
+ {/* File name + size/progress */}
107
+ <KContainer.View flexS={1}>
108
+ <KLabel.Text
109
+ typo="TextSmMedium"
110
+ color={CHAT_BUBBLE_COLORS.text}
111
+ numberOfLines={1}
112
+ ellipsizeMode="middle"
113
+ >
114
+ {fileName}
115
+ </KLabel.Text>
116
+ <KLabel.Text
117
+ typo="TextXsNormal"
118
+ color={subtitleColor}
119
+ numberOfLines={1}
120
+ >
121
+ {subtitle}
122
+ </KLabel.Text>
123
+ </KContainer.View>
118
124
 
119
- {/* Eye (view) button */}
120
- <KImage.VectorIcons
121
- name="eye-outline"
122
- provider="MaterialCommunityIcons"
123
- onPress={handleView}
124
- size={20}
125
- color={KColors.palette.gray.w600}
126
- containerStyle={styles.viewIcon}
127
- />
125
+ {/* Eye (view) button */}
126
+ <KImage.VectorIcons
127
+ name="eye-outline"
128
+ provider="MaterialCommunityIcons"
129
+ onPress={handleView}
130
+ size={20}
131
+ color={KColors.palette.gray.w600}
132
+ containerStyle={styles.viewIcon}
133
+ />
128
134
 
129
- <KImage.VectorIcons
130
- name={'download-o'}
131
- provider="DroppiiNew"
132
- onPress={isDownloading ? undefined : handleDownload}
133
- size={20}
134
- color={
135
- state.status === 'error'
136
- ? (KColors.palette.danger?.w400 ?? '#FF3B30')
137
- : KColors.palette.gray.w600
138
- }
139
- containerStyle={isDownloading ? styles.disabledIcon : undefined}
140
- />
135
+ <KImage.VectorIcons
136
+ name={'download-o'}
137
+ provider="DroppiiNew"
138
+ onPress={isDownloading ? undefined : handleDownload}
139
+ size={20}
140
+ color={
141
+ state.status === 'error'
142
+ ? (KColors.palette.danger?.w400 ?? '#FF3B30')
143
+ : KColors.palette.gray.w600
144
+ }
145
+ containerStyle={isDownloading ? styles.disabledIcon : undefined}
146
+ />
147
+ </KContainer.View>
141
148
  </KContainer.View>
142
- </KContainer.View>
149
+ </BubbleTailRow>
143
150
  );
144
151
  }
145
152
  );
@@ -3,6 +3,7 @@ import { StyleSheet, useWindowDimensions } from 'react-native';
3
3
  import { KContainer, KImage } from '@droppii/libs';
4
4
  import ChatImageDimens from '../../../utils/chatImageDimens';
5
5
  import { messageStyles } from '../styles';
6
+ import { BubbleTailRow } from '../BubbleTailRow';
6
7
  import type { BaseLegendMessageProps } from '../types';
7
8
  const DEFAULT_DIMS = { width: 300, height: 300 };
8
9
 
@@ -72,7 +73,7 @@ export const resolveImageDisplayDims = (
72
73
  };
73
74
 
74
75
  export const ImageMessage = memo(
75
- ({ message, onMediaPress }: BaseLegendMessageProps) => {
76
+ ({ message, isOutgoing, onMediaPress }: BaseLegendMessageProps) => {
76
77
  const windowWidth = useWindowDimensions().width;
77
78
  const [isLoading, setIsLoading] = useState(true);
78
79
  const pictureElem = (message as any).pictureElem;
@@ -130,28 +131,30 @@ export const ImageMessage = memo(
130
131
  );
131
132
 
132
133
  return (
133
- <KContainer.View style={styles.wrapper}>
134
- <KContainer.Touchable
135
- onPress={handlePress}
136
- style={[
137
- messageStyles.imageBubble,
138
- styles.imageContainer,
139
- {
140
- width: imageDimensions.width,
141
- height: imageDimensions.height,
142
- },
143
- ]}
144
- >
145
- <KImage.Base
146
- uri={optimizedUrl}
147
- width={imageDimensions.width}
148
- height={imageDimensions.height}
149
- style={[messageStyles.imageBubble]}
150
- onLoad={handleLoad}
151
- />
152
- {isLoading && <KContainer.View style={styles.skeleton} />}
153
- </KContainer.Touchable>
154
- </KContainer.View>
134
+ <BubbleTailRow isOutgoing={isOutgoing} withTail={false}>
135
+ <KContainer.View style={styles.wrapper}>
136
+ <KContainer.Touchable
137
+ onPress={handlePress}
138
+ style={[
139
+ messageStyles.imageBubble,
140
+ styles.imageContainer,
141
+ {
142
+ width: imageDimensions.width,
143
+ height: imageDimensions.height,
144
+ },
145
+ ]}
146
+ >
147
+ <KImage.Base
148
+ uri={optimizedUrl}
149
+ width={imageDimensions.width}
150
+ height={imageDimensions.height}
151
+ style={[messageStyles.imageBubble]}
152
+ onLoad={handleLoad}
153
+ />
154
+ {isLoading && <KContainer.View style={styles.skeleton} />}
155
+ </KContainer.Touchable>
156
+ </KContainer.View>
157
+ </BubbleTailRow>
155
158
  );
156
159
  }
157
160
  );
@@ -7,11 +7,12 @@ import {
7
7
  KColors,
8
8
  KSpacingValue,
9
9
  } from '@droppii/libs';
10
- import { formatMessageTime } from '../types';
11
10
  import type { BaseLegendMessageProps } from '../types';
12
11
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
13
12
  import { parseUrlMetadata } from '../../../utils/messageUtils';
14
13
  import { messageStyles } from '../styles';
14
+ import { getTailCornerStyle } from '../bubbleTail.utils';
15
+ import { BubbleTailRow } from '../BubbleTailRow';
15
16
 
16
17
  const renderTextWithLinks = (
17
18
  content: string,
@@ -61,22 +62,22 @@ export const LinkMessage = memo(
61
62
  ({
62
63
  message,
63
64
  isOutgoing,
64
- createdAtTime,
65
65
  onPressUrl,
66
66
  isHighlighted,
67
+ groupPosition,
67
68
  }: BaseLegendMessageProps & { onPressUrl?: (url: string) => void }) => {
68
69
  const content = message?.urlTextElem?.content;
69
70
  const urls = message?.urlTextElem?.urls ?? [];
70
71
  const metadata = parseUrlMetadata(message.ex);
71
- const timeLabel = formatMessageTime(createdAtTime);
72
72
 
73
73
  return (
74
- <>
74
+ <BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
75
75
  <KContainer.View
76
76
  style={[
77
77
  styles.bubble,
78
78
  isOutgoing ? styles.bubbleSent : styles.bubbleReceived,
79
79
  isHighlighted && messageStyles.bubbleHighlighted,
80
+ getTailCornerStyle(isOutgoing, groupPosition),
80
81
  ]}
81
82
  >
82
83
  {!!content?.trim() && (
@@ -131,19 +132,7 @@ export const LinkMessage = memo(
131
132
  </KContainer.Touchable>
132
133
  )}
133
134
  </KContainer.View>
134
-
135
- <KContainer.View row alignItems style={styles.timeLabel}>
136
- {!isOutgoing && timeLabel ? (
137
- <KLabel.Text
138
- typo="TextXsNormal"
139
- color={CHAT_BUBBLE_COLORS.timestamp}
140
- marginL="0.25rem"
141
- >
142
- {timeLabel}
143
- </KLabel.Text>
144
- ) : null}
145
- </KContainer.View>
146
- </>
135
+ </BubbleTailRow>
147
136
  );
148
137
  }
149
138
  );
@@ -184,5 +173,4 @@ const styles = StyleSheet.create({
184
173
  paddingVertical: KSpacingValue['0.5rem'],
185
174
  gap: 2,
186
175
  },
187
- timeLabel: { gap: 4, marginTop: 2 },
188
176
  });
@@ -2,9 +2,16 @@ import { memo } from 'react';
2
2
  import { KContainer } from '@droppii/libs';
3
3
  import { MergedImageGrid } from '../../MergedImageGrid';
4
4
  import { messageStyles } from '../styles';
5
+ import { getTailCornerStyle } from '../bubbleTail.utils';
6
+ import { BubbleTailRow } from '../BubbleTailRow';
5
7
  import type { BaseLegendMessageProps } from '../types';
6
8
  export const MergedMessage = memo(
7
- ({ message, isOutgoing, onMediaPress }: BaseLegendMessageProps) => {
9
+ ({
10
+ message,
11
+ isOutgoing,
12
+ onMediaPress,
13
+ groupPosition,
14
+ }: BaseLegendMessageProps) => {
8
15
  const mergeElem = (message as any).mergeElem;
9
16
 
10
17
  if (!mergeElem?.multiMessage) {
@@ -17,20 +24,25 @@ export const MergedMessage = memo(
17
24
  );
18
25
 
19
26
  return (
20
- <KContainer.View
21
- style={[
22
- messageStyles.bubble,
23
- isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
24
- ]}
25
- >
26
- {imageMessages.length > 0 && (
27
- <MergedImageGrid
28
- images={imageMessages}
29
- maxDisplay={3}
30
- onMediaPress={onMediaPress}
31
- />
32
- )}
33
- </KContainer.View>
27
+ <BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
28
+ <KContainer.View
29
+ style={[
30
+ messageStyles.bubble,
31
+ isOutgoing
32
+ ? messageStyles.bubbleSent
33
+ : messageStyles.bubbleReceived,
34
+ getTailCornerStyle(isOutgoing, groupPosition),
35
+ ]}
36
+ >
37
+ {imageMessages.length > 0 && (
38
+ <MergedImageGrid
39
+ images={imageMessages}
40
+ maxDisplay={3}
41
+ onMediaPress={onMediaPress}
42
+ />
43
+ )}
44
+ </KContainer.View>
45
+ </BubbleTailRow>
34
46
  );
35
47
  }
36
48
  );
@@ -1,6 +1,8 @@
1
1
  import { memo, useCallback, useMemo } from 'react';
2
2
  import { messageStyles } from '../styles';
3
- import { formatMessageTime, type BaseLegendMessageProps } from '../types';
3
+ import { getTailCornerStyle } from '../bubbleTail.utils';
4
+ import { BubbleTailRow } from '../BubbleTailRow';
5
+ import { type BaseLegendMessageProps } from '../types';
4
6
  import {
5
7
  KColors,
6
8
  KContainer,
@@ -25,7 +27,7 @@ import { ChatEvent } from '../../../types/events';
25
27
  interface QuotedMessageProps extends BaseLegendMessageProps {}
26
28
 
27
29
  const QuotedMessage = memo((props: QuotedMessageProps) => {
28
- const { message, isOutgoing, createdAtTime, isHighlighted } = props || {};
30
+ const { message, isOutgoing, isHighlighted, groupPosition } = props || {};
29
31
 
30
32
  const { conversation } = useChatDetailContext();
31
33
 
@@ -35,8 +37,6 @@ const QuotedMessage = memo((props: QuotedMessageProps) => {
35
37
 
36
38
  const messagePreview = getMessagePreviewParts(quotedMessage);
37
39
 
38
- const timeLabel = formatMessageTime(createdAtTime);
39
-
40
40
  const imageUrl = useMemo(() => {
41
41
  switch (quotedMessage?.contentType) {
42
42
  case MessageType.PictureMessage:
@@ -64,13 +64,14 @@ const QuotedMessage = memo((props: QuotedMessageProps) => {
64
64
  }, [quotedMessage]);
65
65
 
66
66
  return (
67
- <>
67
+ <BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
68
68
  <KContainer.View
69
69
  style={[
70
70
  messageStyles.bubble,
71
71
  styles.container,
72
72
  isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
73
73
  isHighlighted && messageStyles.bubbleHighlighted,
74
+ getTailCornerStyle(isOutgoing, groupPosition),
74
75
  ]}
75
76
  >
76
77
  <KContainer.Touchable
@@ -128,19 +129,7 @@ const QuotedMessage = memo((props: QuotedMessageProps) => {
128
129
  {message?.quoteElem?.text || ''}
129
130
  </KLabel.Text>
130
131
  </KContainer.View>
131
-
132
- <KContainer.View row alignItems style={styles.timeLabel}>
133
- {!isOutgoing && timeLabel ? (
134
- <KLabel.Text
135
- typo="TextXsNormal"
136
- color={CHAT_BUBBLE_COLORS.timestamp}
137
- marginL="0.25rem"
138
- >
139
- {timeLabel}
140
- </KLabel.Text>
141
- ) : null}
142
- </KContainer.View>
143
- </>
132
+ </BubbleTailRow>
144
133
  );
145
134
  });
146
135
 
@@ -198,5 +187,4 @@ const styles = StyleSheet.create({
198
187
  justifyContent: 'center',
199
188
  alignItems: 'center',
200
189
  },
201
- timeLabel: { gap: 4, marginTop: 2 },
202
190
  });
@@ -3,21 +3,30 @@ import { KContainer, KLabel } from '@droppii/libs';
3
3
  import { trans } from '../../../translation';
4
4
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
5
5
  import { messageStyles } from '../styles';
6
+ import { getTailCornerStyle } from '../bubbleTail.utils';
7
+ import { BubbleTailRow } from '../BubbleTailRow';
6
8
  import type { BaseLegendMessageProps } from '../types';
7
9
 
8
- export const RevokedMessage = memo(({ isOutgoing }: BaseLegendMessageProps) => {
9
- return (
10
- <KContainer.View
11
- style={[
12
- messageStyles.bubble,
13
- isOutgoing ? messageStyles.bubbleSent : messageStyles.bubbleReceived,
14
- ]}
15
- >
16
- <KLabel.Text typo="TextMdNormal" color={CHAT_BUBBLE_COLORS.timestamp}>
17
- {trans('msg_type_revoke')}
18
- </KLabel.Text>
19
- </KContainer.View>
20
- );
21
- });
10
+ export const RevokedMessage = memo(
11
+ ({ isOutgoing, groupPosition }: BaseLegendMessageProps) => {
12
+ return (
13
+ <BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
14
+ <KContainer.View
15
+ style={[
16
+ messageStyles.bubble,
17
+ isOutgoing
18
+ ? messageStyles.bubbleSent
19
+ : messageStyles.bubbleReceived,
20
+ getTailCornerStyle(isOutgoing, groupPosition),
21
+ ]}
22
+ >
23
+ <KLabel.Text typo="TextMdNormal" color={CHAT_BUBBLE_COLORS.timestamp}>
24
+ {trans('msg_type_revoke')}
25
+ </KLabel.Text>
26
+ </KContainer.View>
27
+ </BubbleTailRow>
28
+ );
29
+ }
30
+ );
22
31
 
23
32
  RevokedMessage.displayName = 'RevokedMessage';
@@ -2,19 +2,24 @@ import { memo } from 'react';
2
2
  import type { BaseLegendMessageProps } from '../types';
3
3
  import { KContainer, KImage } from '@droppii/libs';
4
4
  import { StyleSheet } from 'react-native';
5
+ import { BubbleTailRow } from '../BubbleTailRow';
5
6
 
6
7
  export const STICKER_SIZE = 120;
7
- export const StickerMessage = memo(({ message }: BaseLegendMessageProps) => {
8
- return (
9
- <KContainer.View style={styles.message}>
10
- <KImage.Base
11
- uri={message?.stickerElem?.content || ''}
12
- width={STICKER_SIZE}
13
- height={STICKER_SIZE}
14
- />
15
- </KContainer.View>
16
- );
17
- });
8
+ export const StickerMessage = memo(
9
+ ({ message, isOutgoing }: BaseLegendMessageProps) => {
10
+ return (
11
+ <BubbleTailRow isOutgoing={isOutgoing} withTail={false}>
12
+ <KContainer.View style={styles.message}>
13
+ <KImage.Base
14
+ uri={message?.stickerElem?.content || ''}
15
+ width={STICKER_SIZE}
16
+ height={STICKER_SIZE}
17
+ />
18
+ </KContainer.View>
19
+ </BubbleTailRow>
20
+ );
21
+ }
22
+ );
18
23
 
19
24
  const styles = StyleSheet.create({
20
25
  message: {
@@ -3,25 +3,24 @@ import { KContainer, KLabel } from '@droppii/libs';
3
3
  import { getMessageText } from '../../../utils/legendListMessage';
4
4
  import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
5
5
  import { messageStyles } from '../styles';
6
- import { formatMessageTime } from '../types';
6
+ import { getTailCornerStyle } from '../bubbleTail.utils';
7
+ import { BubbleTailRow } from '../BubbleTailRow';
7
8
  import type { BaseLegendMessageProps } from '../types';
8
- import { StyleSheet } from 'react-native';
9
9
  export const TextMessage = memo(
10
10
  ({
11
11
  message,
12
12
  isOutgoing,
13
- createdAtTime,
14
13
  isHighlighted,
14
+ groupPosition,
15
15
  }: BaseLegendMessageProps) => {
16
16
  const messageText = getMessageText(message);
17
- const timeLabel = formatMessageTime(createdAtTime);
18
17
 
19
18
  if (!messageText?.trim()) {
20
19
  return null;
21
20
  }
22
21
 
23
22
  return (
24
- <>
23
+ <BubbleTailRow isOutgoing={isOutgoing} groupPosition={groupPosition}>
25
24
  <KContainer.View
26
25
  style={[
27
26
  messageStyles.bubble,
@@ -29,31 +28,16 @@ export const TextMessage = memo(
29
28
  ? messageStyles.bubbleSent
30
29
  : messageStyles.bubbleReceived,
31
30
  isHighlighted && messageStyles.bubbleHighlighted,
31
+ getTailCornerStyle(isOutgoing, groupPosition),
32
32
  ]}
33
33
  >
34
34
  <KLabel.Text typo="TextMdNormal" color={CHAT_BUBBLE_COLORS.text}>
35
35
  {messageText}
36
36
  </KLabel.Text>
37
37
  </KContainer.View>
38
-
39
- <KContainer.View row alignItems style={styles.timeLabel}>
40
- {!isOutgoing && timeLabel ? (
41
- <KLabel.Text
42
- typo="TextXsNormal"
43
- color={CHAT_BUBBLE_COLORS.timestamp}
44
- marginL={'0.25rem'}
45
- >
46
- {timeLabel}
47
- </KLabel.Text>
48
- ) : null}
49
- </KContainer.View>
50
- </>
38
+ </BubbleTailRow>
51
39
  );
52
40
  }
53
41
  );
54
42
 
55
43
  TextMessage.displayName = 'TextMessage';
56
-
57
- const styles = StyleSheet.create({
58
- timeLabel: { gap: 4, marginTop: 2 },
59
- });
@@ -1,11 +1,14 @@
1
1
  import type { MediaItem } from '../../hooks/useMediaViewer';
2
2
  import type { MessageItem } from '@droppii/openim-rn-client-sdk';
3
+ import type { GroupPosition } from '../../utils/legendListMessage';
3
4
 
4
5
  export interface BaseLegendMessageProps {
5
6
  message: MessageItem;
6
7
  isOutgoing: boolean;
7
8
  createdAtTime: number;
8
9
  isHighlighted?: boolean;
10
+ /** Position within its same-sender cluster — drives the bubble tail. */
11
+ groupPosition?: GroupPosition;
9
12
  onMediaPress?: (items: MediaItem[], index?: number) => void;
10
13
  }
11
14