@droppii-org/chat-mobile 0.2.79 → 0.2.80

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 (97) 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/hooks/groups/useDismissGroup.js +2 -1
  27. package/lib/module/hooks/groups/useDismissGroup.js.map +1 -1
  28. package/lib/module/screens/chat-detail/ChatListLegend.js +71 -21
  29. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  30. package/lib/module/screens/chat-detail/legend/LegendChatDay.js +8 -2
  31. package/lib/module/screens/chat-detail/legend/LegendChatDay.js.map +1 -1
  32. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +38 -13
  33. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  34. package/lib/module/utils/legendListMessage.js +86 -12
  35. package/lib/module/utils/legendListMessage.js.map +1 -1
  36. package/lib/module/utils/message.js +19 -1
  37. package/lib/module/utils/message.js.map +1 -1
  38. package/lib/module/utils/messageUtils.js +6 -0
  39. package/lib/module/utils/messageUtils.js.map +1 -1
  40. package/lib/typescript/src/components/messages/BubbleTail.d.ts +13 -0
  41. package/lib/typescript/src/components/messages/BubbleTail.d.ts.map +1 -0
  42. package/lib/typescript/src/components/messages/BubbleTailRow.d.ts +25 -0
  43. package/lib/typescript/src/components/messages/BubbleTailRow.d.ts.map +1 -0
  44. package/lib/typescript/src/components/messages/bubbleTail.utils.d.ts +11 -0
  45. package/lib/typescript/src/components/messages/bubbleTail.utils.d.ts.map +1 -0
  46. package/lib/typescript/src/components/messages/fileMessage/index.d.ts +1 -1
  47. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
  48. package/lib/typescript/src/components/messages/imageMessage/index.d.ts +1 -1
  49. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -1
  50. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +1 -1
  51. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  52. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts +1 -1
  53. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -1
  54. package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -1
  55. package/lib/typescript/src/components/messages/revokedMessage/index.d.ts +1 -1
  56. package/lib/typescript/src/components/messages/revokedMessage/index.d.ts.map +1 -1
  57. package/lib/typescript/src/components/messages/stickerMessage/index.d.ts +1 -1
  58. package/lib/typescript/src/components/messages/stickerMessage/index.d.ts.map +1 -1
  59. package/lib/typescript/src/components/messages/textMessage/index.d.ts +1 -1
  60. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
  61. package/lib/typescript/src/components/messages/types.d.ts +3 -0
  62. package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
  63. package/lib/typescript/src/components/messages/videoMessage/index.d.ts +1 -1
  64. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -1
  65. package/lib/typescript/src/hooks/groups/useDismissGroup.d.ts.map +1 -1
  66. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  67. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts +6 -1
  68. package/lib/typescript/src/screens/chat-detail/legend/LegendChatDay.d.ts.map +1 -1
  69. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +14 -1
  70. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  71. package/lib/typescript/src/utils/legendListMessage.d.ts +19 -4
  72. package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
  73. package/lib/typescript/src/utils/message.d.ts +17 -0
  74. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  75. package/lib/typescript/src/utils/messageUtils.d.ts +1 -0
  76. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/components/messages/BubbleTail.tsx +35 -0
  79. package/src/components/messages/BubbleTailRow.tsx +64 -0
  80. package/src/components/messages/bubbleTail.utils.ts +21 -0
  81. package/src/components/messages/fileMessage/index.tsx +90 -83
  82. package/src/components/messages/imageMessage/index.tsx +26 -23
  83. package/src/components/messages/linkMessage/index.tsx +6 -18
  84. package/src/components/messages/mergedMessage/index.tsx +27 -15
  85. package/src/components/messages/quotedMessage/index.tsx +7 -19
  86. package/src/components/messages/revokedMessage/index.tsx +23 -14
  87. package/src/components/messages/stickerMessage/index.tsx +16 -11
  88. package/src/components/messages/textMessage/index.tsx +6 -22
  89. package/src/components/messages/types.ts +3 -0
  90. package/src/components/messages/videoMessage/index.tsx +13 -10
  91. package/src/hooks/groups/useDismissGroup.tsx +1 -0
  92. package/src/screens/chat-detail/ChatListLegend.tsx +97 -28
  93. package/src/screens/chat-detail/legend/LegendChatDay.tsx +25 -15
  94. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +62 -12
  95. package/src/utils/legendListMessage.ts +123 -14
  96. package/src/utils/message.ts +19 -1
  97. package/src/utils/messageUtils.ts +8 -0
@@ -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