@droppii-org/chat-mobile 0.2.65 → 0.2.67
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.
- package/lib/module/components/Avatar/Avatar.js +39 -2
- package/lib/module/components/Avatar/Avatar.js.map +1 -1
- package/lib/module/components/Avatar/DoubleAvatar.js +18 -18
- package/lib/module/components/Avatar/DoubleAvatar.js.map +1 -1
- package/lib/module/components/Avatar/QuadAvatar.js +131 -0
- package/lib/module/components/Avatar/QuadAvatar.js.map +1 -0
- package/lib/module/components/Avatar/TripleAvatar.js +97 -0
- package/lib/module/components/Avatar/TripleAvatar.js.map +1 -0
- package/lib/module/components/Avatar/index.js +2 -0
- package/lib/module/components/Avatar/index.js.map +1 -1
- package/lib/module/components/ThreadCard/AvatarSection.js +41 -0
- package/lib/module/components/ThreadCard/AvatarSection.js.map +1 -1
- package/lib/module/components/ThreadCard/NamePrefixIcon.js +5 -4
- package/lib/module/components/ThreadCard/NamePrefixIcon.js.map +1 -1
- package/lib/module/components/ThreadCard/ThreadCard.js +3 -1
- package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
- package/lib/module/components/messages/groupNotificationMessage/index.js +25 -0
- package/lib/module/components/messages/groupNotificationMessage/index.js.map +1 -0
- package/lib/module/context/ChatContext.js +1 -1
- package/lib/module/context/ChatContext.js.map +1 -1
- package/lib/module/context/ChatDetailContext.js +9 -0
- package/lib/module/context/ChatDetailContext.js.map +1 -1
- package/lib/module/core/useChatListener.js +120 -21
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/hooks/conversation/useGetGroupInfo.js +21 -0
- package/lib/module/hooks/conversation/useGetGroupInfo.js.map +1 -0
- package/lib/module/hooks/message/useSendMessage.js +6 -3
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/query-keys.js +2 -1
- package/lib/module/hooks/query-keys.js.map +1 -1
- package/lib/module/hooks/useConversationList.js +9 -4
- package/lib/module/hooks/useConversationList.js.map +1 -1
- package/lib/module/screens/ChatInfo/ChatInfo.js +18 -2
- package/lib/module/screens/ChatInfo/ChatInfo.js.map +1 -1
- package/lib/module/screens/ChatInfo/ChatInfoActions.js +83 -55
- package/lib/module/screens/ChatInfo/ChatInfoActions.js.map +1 -1
- package/lib/module/screens/ChatInfo/ChatInfoDescriptionRow.js +77 -0
- package/lib/module/screens/ChatInfo/ChatInfoDescriptionRow.js.map +1 -0
- package/lib/module/screens/ChatInfo/ChatInfoProfile.js +4 -2
- package/lib/module/screens/ChatInfo/ChatInfoProfile.js.map +1 -1
- package/lib/module/screens/ChatInfo/GroupDescriptionModal.js +58 -0
- package/lib/module/screens/ChatInfo/GroupDescriptionModal.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatComposer.js +11 -6
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetail.js +7 -0
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetailHeader.js +10 -3
- package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
- package/lib/module/screens/chat-detail/conversationHeader.utils.js +13 -3
- package/lib/module/screens/chat-detail/conversationHeader.utils.js.map +1 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +29 -2
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/screens/inbox/MessagesTab.js.map +1 -1
- package/lib/module/store/conversation.js +56 -12
- package/lib/module/store/conversation.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +34 -0
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/events.js +1 -0
- package/lib/module/types/events.js.map +1 -1
- package/lib/module/utils/conversation.js +3 -0
- package/lib/module/utils/conversation.js.map +1 -1
- package/lib/module/utils/messageUtils.js +95 -2
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.d.ts +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/Avatar.types.d.ts +5 -1
- package/lib/typescript/src/components/Avatar/Avatar.types.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/DoubleAvatar.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/QuadAvatar.d.ts +16 -0
- package/lib/typescript/src/components/Avatar/QuadAvatar.d.ts.map +1 -0
- package/lib/typescript/src/components/Avatar/TripleAvatar.d.ts +15 -0
- package/lib/typescript/src/components/Avatar/TripleAvatar.d.ts.map +1 -0
- package/lib/typescript/src/components/Avatar/index.d.ts +2 -0
- package/lib/typescript/src/components/Avatar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +2 -2
- package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts +3 -1
- package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/groupNotificationMessage/index.d.ts +3 -0
- package/lib/typescript/src/components/messages/groupNotificationMessage/index.d.ts.map +1 -0
- package/lib/typescript/src/context/ChatDetailContext.d.ts +2 -0
- package/lib/typescript/src/context/ChatDetailContext.d.ts.map +1 -1
- package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
- package/lib/typescript/src/hooks/conversation/useGetGroupInfo.d.ts +2 -0
- package/lib/typescript/src/hooks/conversation/useGetGroupInfo.d.ts.map +1 -0
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/query-keys.d.ts +1 -0
- package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -1
- package/lib/typescript/src/screens/ChatInfo/ChatInfo.d.ts +1 -1
- package/lib/typescript/src/screens/ChatInfo/ChatInfo.d.ts.map +1 -1
- package/lib/typescript/src/screens/ChatInfo/ChatInfoActions.d.ts +3 -1
- package/lib/typescript/src/screens/ChatInfo/ChatInfoActions.d.ts.map +1 -1
- package/lib/typescript/src/screens/ChatInfo/ChatInfoDescriptionRow.d.ts +7 -0
- package/lib/typescript/src/screens/ChatInfo/ChatInfoDescriptionRow.d.ts.map +1 -0
- package/lib/typescript/src/screens/ChatInfo/ChatInfoProfile.d.ts.map +1 -1
- package/lib/typescript/src/screens/ChatInfo/GroupDescriptionModal.d.ts +9 -0
- package/lib/typescript/src/screens/ChatInfo/GroupDescriptionModal.d.ts.map +1 -0
- package/lib/typescript/src/screens/ChatInfo/types.d.ts +1 -0
- package/lib/typescript/src/screens/ChatInfo/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/MessagesTab.d.ts.map +1 -1
- package/lib/typescript/src/store/conversation.d.ts +11 -0
- package/lib/typescript/src/store/conversation.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +6 -1
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/lib/typescript/src/utils/conversation.d.ts +1 -0
- package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
- package/lib/typescript/src/utils/messageUtils.d.ts +6 -1
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/build-ignore.d.ts +1 -0
- package/src/components/Avatar/Avatar.tsx +47 -0
- package/src/components/Avatar/Avatar.types.ts +5 -1
- package/src/components/Avatar/DoubleAvatar.tsx +16 -13
- package/src/components/Avatar/QuadAvatar.tsx +161 -0
- package/src/components/Avatar/TripleAvatar.tsx +119 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/ThreadCard/AvatarSection.tsx +58 -2
- package/src/components/ThreadCard/NamePrefixIcon.tsx +32 -25
- package/src/components/ThreadCard/ThreadCard.tsx +2 -0
- package/src/components/messages/groupNotificationMessage/index.tsx +28 -0
- package/src/context/ChatContext.tsx +1 -1
- package/src/context/ChatDetailContext.tsx +15 -0
- package/src/core/useChatListener.ts +175 -23
- package/src/hooks/conversation/useGetGroupInfo.ts +20 -0
- package/src/hooks/message/useSendMessage.ts +11 -3
- package/src/hooks/query-keys.ts +1 -0
- package/src/hooks/useConversationList.ts +17 -7
- package/src/screens/ChatInfo/ChatInfo.tsx +16 -0
- package/src/screens/ChatInfo/ChatInfoActions.tsx +68 -35
- package/src/screens/ChatInfo/ChatInfoDescriptionRow.tsx +102 -0
- package/src/screens/ChatInfo/ChatInfoProfile.tsx +6 -3
- package/src/screens/ChatInfo/GroupDescriptionModal.tsx +67 -0
- package/src/screens/ChatInfo/types.ts +1 -0
- package/src/screens/chat-detail/ChatComposer.tsx +19 -10
- package/src/screens/chat-detail/ChatDetail.tsx +12 -1
- package/src/screens/chat-detail/ChatDetailHeader.tsx +19 -3
- package/src/screens/chat-detail/conversationHeader.utils.ts +14 -6
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +36 -1
- package/src/screens/inbox/MessagesTab.tsx +0 -1
- package/src/store/conversation.ts +62 -17
- package/src/translation/resources/i18n.ts +34 -0
- package/src/types/events.ts +6 -0
- package/src/utils/conversation.ts +6 -0
- package/src/utils/messageUtils.ts +103 -1
|
@@ -13,18 +13,22 @@ import { trans } from '../../translation';
|
|
|
13
13
|
interface ChatInfoActionsProps {
|
|
14
14
|
conversation?: ConversationItem;
|
|
15
15
|
isTargetActive: boolean;
|
|
16
|
+
isGroup?: boolean;
|
|
16
17
|
onPressOrders?: () => void;
|
|
17
18
|
onPressCreateGroup?: () => void;
|
|
18
19
|
onPressAddToGroup?: () => void;
|
|
20
|
+
onPressLeaveGroup?: () => void;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const ChatInfoActions = memo(
|
|
22
24
|
({
|
|
23
25
|
conversation,
|
|
24
26
|
isTargetActive,
|
|
27
|
+
isGroup,
|
|
25
28
|
onPressOrders,
|
|
26
29
|
onPressCreateGroup,
|
|
27
30
|
onPressAddToGroup,
|
|
31
|
+
onPressLeaveGroup,
|
|
28
32
|
}: ChatInfoActionsProps) => {
|
|
29
33
|
const { pinConversation, unpinConversation } = usePinConversation();
|
|
30
34
|
const isPinned = Boolean(conversation?.isPinned);
|
|
@@ -41,8 +45,10 @@ const ChatInfoActions = memo(
|
|
|
41
45
|
const handleDeleteHistory = useCallback(() => {}, []);
|
|
42
46
|
|
|
43
47
|
const primaryData = useMemo(() => {
|
|
44
|
-
const items: KListItemBaseItemProps[] = [
|
|
45
|
-
|
|
48
|
+
const items: KListItemBaseItemProps[] = [];
|
|
49
|
+
|
|
50
|
+
if (!isGroup) {
|
|
51
|
+
items.push({
|
|
46
52
|
leftNode: { icon: { vectorName: 'receipt-o' } },
|
|
47
53
|
onPress: onPressOrders,
|
|
48
54
|
paddingH: '0.75rem',
|
|
@@ -52,34 +58,34 @@ const ChatInfoActions = memo(
|
|
|
52
58
|
{trans('chat:info_orders', { name: showName })}
|
|
53
59
|
</KLabel.Text>
|
|
54
60
|
),
|
|
55
|
-
}
|
|
56
|
-
];
|
|
61
|
+
});
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
if (isTargetActive) {
|
|
64
|
+
items.push(
|
|
65
|
+
{
|
|
66
|
+
leftNode: { icon: { vectorName: 'user-three-o' } },
|
|
67
|
+
onPress: onPressCreateGroup,
|
|
68
|
+
paddingH: '0.75rem',
|
|
69
|
+
paddingV: '0.75rem',
|
|
70
|
+
customLabel: (
|
|
71
|
+
<KLabel.Text typo="TextMdMedium" numberOfLines={1}>
|
|
72
|
+
{trans('chat:info_create_group', { name: showName })}
|
|
73
|
+
</KLabel.Text>
|
|
74
|
+
),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
leftNode: { icon: { vectorName: 'user-add-o' } },
|
|
78
|
+
onPress: onPressAddToGroup,
|
|
79
|
+
paddingH: '0.75rem',
|
|
80
|
+
paddingV: '0.75rem',
|
|
81
|
+
customLabel: (
|
|
82
|
+
<KLabel.Text typo="TextMdMedium" numberOfLines={1}>
|
|
83
|
+
{trans('chat:info_add_to_group')}
|
|
84
|
+
</KLabel.Text>
|
|
85
|
+
),
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
items.push({
|
|
@@ -102,6 +108,7 @@ const ChatInfoActions = memo(
|
|
|
102
108
|
return items;
|
|
103
109
|
}, [
|
|
104
110
|
showName,
|
|
111
|
+
isGroup,
|
|
105
112
|
isTargetActive,
|
|
106
113
|
isPinned,
|
|
107
114
|
onPressOrders,
|
|
@@ -110,8 +117,35 @@ const ChatInfoActions = memo(
|
|
|
110
117
|
handlePinPress,
|
|
111
118
|
]);
|
|
112
119
|
|
|
113
|
-
const
|
|
114
|
-
()
|
|
120
|
+
const dangerData: KListItemBaseItemProps[] = useMemo(() => {
|
|
121
|
+
if (isGroup) {
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
leftNode: {
|
|
125
|
+
icon: {
|
|
126
|
+
vectorName: 'logout-o',
|
|
127
|
+
tintColor: KColors.danger.normal,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
tintColor: KColors.danger.normal,
|
|
131
|
+
onPress: onPressLeaveGroup,
|
|
132
|
+
showChevron: false,
|
|
133
|
+
paddingH: '0.75rem',
|
|
134
|
+
paddingV: '0.75rem',
|
|
135
|
+
customLabel: (
|
|
136
|
+
<KLabel.Text
|
|
137
|
+
typo="TextMdMedium"
|
|
138
|
+
color={KColors.danger.normal}
|
|
139
|
+
numberOfLines={1}
|
|
140
|
+
>
|
|
141
|
+
{trans('chat:info_leave_group')}
|
|
142
|
+
</KLabel.Text>
|
|
143
|
+
),
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return [
|
|
115
149
|
{
|
|
116
150
|
label: trans('chat:info_delete_history'),
|
|
117
151
|
leftNode: {
|
|
@@ -135,9 +169,8 @@ const ChatInfoActions = memo(
|
|
|
135
169
|
</KLabel.Text>
|
|
136
170
|
),
|
|
137
171
|
},
|
|
138
|
-
]
|
|
139
|
-
|
|
140
|
-
);
|
|
172
|
+
];
|
|
173
|
+
}, [isGroup, onPressLeaveGroup, handleDeleteHistory]);
|
|
141
174
|
|
|
142
175
|
return (
|
|
143
176
|
<>
|
|
@@ -158,7 +191,7 @@ const ChatInfoActions = memo(
|
|
|
158
191
|
marginT="0.75rem"
|
|
159
192
|
hiddenOverflow
|
|
160
193
|
>
|
|
161
|
-
<KListItem.Base data={
|
|
194
|
+
<KListItem.Base data={dangerData} />
|
|
162
195
|
</KContainer.View>
|
|
163
196
|
</>
|
|
164
197
|
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { memo, useCallback, useState } from 'react';
|
|
2
|
+
import { StyleSheet, type TextLayoutEvent } from 'react-native';
|
|
3
|
+
import { KContainer, KLabel, KColors } from '@droppii/libs';
|
|
4
|
+
import { trans } from '../../translation';
|
|
5
|
+
import GroupDescriptionModal from './GroupDescriptionModal';
|
|
6
|
+
|
|
7
|
+
interface ChatInfoDescriptionRowProps {
|
|
8
|
+
introduction?: string;
|
|
9
|
+
groupName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const EXTRA_TEXT_LENGTH = 14; // Length of "...View More" text
|
|
13
|
+
const MAX_LINES = 2;
|
|
14
|
+
|
|
15
|
+
const ChatInfoDescriptionRow = memo(
|
|
16
|
+
({ introduction, groupName }: ChatInfoDescriptionRowProps) => {
|
|
17
|
+
const [modalVisible, setModalVisible] = useState(false);
|
|
18
|
+
const handlePressDescription = useCallback(() => setModalVisible(true), []);
|
|
19
|
+
const handleCloseDescription = useCallback(
|
|
20
|
+
() => setModalVisible(false),
|
|
21
|
+
[]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const [isTruncated, setIsTruncated] = useState(false);
|
|
25
|
+
const [displayedText, setDisplayedText] = useState(introduction);
|
|
26
|
+
|
|
27
|
+
const handleTextLayout = useCallback((event: TextLayoutEvent) => {
|
|
28
|
+
const isTextTruncated = event.nativeEvent.lines.length > MAX_LINES;
|
|
29
|
+
|
|
30
|
+
setIsTruncated(isTextTruncated);
|
|
31
|
+
|
|
32
|
+
if (isTextTruncated) {
|
|
33
|
+
setDisplayedText(
|
|
34
|
+
event.nativeEvent.lines
|
|
35
|
+
.slice(0, MAX_LINES)
|
|
36
|
+
.map((line) => line.text)
|
|
37
|
+
.join(' ')
|
|
38
|
+
.slice(0, -EXTRA_TEXT_LENGTH) + '...'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
const textProps = {
|
|
44
|
+
color: KColors.gray.dark,
|
|
45
|
+
typo: 'TextMdMedium' as const,
|
|
46
|
+
paddingH: '0.75rem' as const,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (!introduction) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
<KContainer.View
|
|
56
|
+
background={KColors.white}
|
|
57
|
+
br="3x"
|
|
58
|
+
marginH="0.75rem"
|
|
59
|
+
marginT="0.75rem"
|
|
60
|
+
paddingV="0.75rem"
|
|
61
|
+
>
|
|
62
|
+
<KLabel.Text
|
|
63
|
+
{...textProps}
|
|
64
|
+
style={styles.hiddenText}
|
|
65
|
+
numberOfLines={MAX_LINES + 1}
|
|
66
|
+
onTextLayout={handleTextLayout}
|
|
67
|
+
>
|
|
68
|
+
{introduction}
|
|
69
|
+
</KLabel.Text>
|
|
70
|
+
<KLabel.Text {...textProps} numberOfLines={2}>
|
|
71
|
+
{displayedText}
|
|
72
|
+
<KContainer.VisibleView visible={isTruncated}>
|
|
73
|
+
<KLabel.Text
|
|
74
|
+
{...textProps}
|
|
75
|
+
color={KColors.primary.normal}
|
|
76
|
+
onPress={handlePressDescription}
|
|
77
|
+
>
|
|
78
|
+
{trans('chat:info_view_more')}
|
|
79
|
+
</KLabel.Text>
|
|
80
|
+
</KContainer.VisibleView>
|
|
81
|
+
</KLabel.Text>
|
|
82
|
+
</KContainer.View>
|
|
83
|
+
<GroupDescriptionModal
|
|
84
|
+
visible={modalVisible}
|
|
85
|
+
groupName={groupName}
|
|
86
|
+
introduction={introduction}
|
|
87
|
+
onClose={handleCloseDescription}
|
|
88
|
+
/>
|
|
89
|
+
</>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
export default ChatInfoDescriptionRow;
|
|
95
|
+
|
|
96
|
+
const styles = StyleSheet.create({
|
|
97
|
+
hiddenText: {
|
|
98
|
+
position: 'absolute',
|
|
99
|
+
opacity: 0,
|
|
100
|
+
zIndex: -1,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
@@ -93,12 +93,15 @@ const ChatInfoProfile = memo(
|
|
|
93
93
|
fullName={showName}
|
|
94
94
|
badge={badge}
|
|
95
95
|
/>
|
|
96
|
-
<KContainer.View row alignItems marginT="0.5rem">
|
|
97
|
-
<NamePrefixIcon
|
|
96
|
+
<KContainer.View row alignItems marginT="0.5rem" paddingH={'0.75rem'}>
|
|
97
|
+
<NamePrefixIcon
|
|
98
|
+
peerType={conversation?.peerType}
|
|
99
|
+
marginR="0.5rem"
|
|
100
|
+
/>
|
|
98
101
|
<KLabel.Text
|
|
99
102
|
typo="TextLgBold"
|
|
100
103
|
color={KColors.black}
|
|
101
|
-
numberOfLines={
|
|
104
|
+
numberOfLines={2}
|
|
102
105
|
>
|
|
103
106
|
{showName}
|
|
104
107
|
</KLabel.Text>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { Modal, StyleSheet } from 'react-native';
|
|
3
|
+
import { KContainer, KNavigation, KLabel, KColors } from '@droppii/libs';
|
|
4
|
+
import { trans } from '../../translation';
|
|
5
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
6
|
+
|
|
7
|
+
interface GroupDescriptionModalProps {
|
|
8
|
+
visible: boolean;
|
|
9
|
+
groupName?: string;
|
|
10
|
+
introduction?: string;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const GroupDescriptionModal = memo(
|
|
15
|
+
({
|
|
16
|
+
visible,
|
|
17
|
+
groupName,
|
|
18
|
+
introduction,
|
|
19
|
+
onClose,
|
|
20
|
+
}: GroupDescriptionModalProps) => (
|
|
21
|
+
<Modal
|
|
22
|
+
visible={visible}
|
|
23
|
+
transparent={false}
|
|
24
|
+
statusBarTranslucent
|
|
25
|
+
style={styles.modal}
|
|
26
|
+
presentationStyle="fullScreen"
|
|
27
|
+
animationType="slide"
|
|
28
|
+
onRequestClose={onClose}
|
|
29
|
+
>
|
|
30
|
+
<SafeAreaProvider>
|
|
31
|
+
<KContainer.Page flex edges={['bottom']} background={KColors.white}>
|
|
32
|
+
<KNavigation.Header
|
|
33
|
+
theme="light"
|
|
34
|
+
alignment="left"
|
|
35
|
+
size="small"
|
|
36
|
+
leftButton={{
|
|
37
|
+
id: 'GroupDescriptionModalCloseButton',
|
|
38
|
+
icon: { vectorName: 'close-o' },
|
|
39
|
+
onPress: onClose,
|
|
40
|
+
}}
|
|
41
|
+
title={trans('chat:group_description_title')}
|
|
42
|
+
subTitle={groupName}
|
|
43
|
+
shadow
|
|
44
|
+
/>
|
|
45
|
+
<KLabel.Text
|
|
46
|
+
paddingH="1rem"
|
|
47
|
+
paddingT="0.75rem"
|
|
48
|
+
typo="TextMdNormal"
|
|
49
|
+
color={KColors.gray.dark}
|
|
50
|
+
>
|
|
51
|
+
{introduction}
|
|
52
|
+
</KLabel.Text>
|
|
53
|
+
</KContainer.Page>
|
|
54
|
+
</SafeAreaProvider>
|
|
55
|
+
</Modal>
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
GroupDescriptionModal.displayName = 'GroupDescriptionModal';
|
|
60
|
+
|
|
61
|
+
export default GroupDescriptionModal;
|
|
62
|
+
|
|
63
|
+
const styles = StyleSheet.create({
|
|
64
|
+
modal: {
|
|
65
|
+
flex: 1,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -63,7 +63,11 @@ import { ChatCurrentQuotedMessage } from './ChatCurrentQuotedMessage';
|
|
|
63
63
|
import { useSendMessage } from '../../hooks/message/useSendMessage';
|
|
64
64
|
import { openBotMenuSheet } from './BotMenuSheet';
|
|
65
65
|
import { UIUtils } from '../../utils/UIUtils';
|
|
66
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
GroupPermission,
|
|
68
|
+
PeerType,
|
|
69
|
+
SessionType,
|
|
70
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
67
71
|
import { useConversationStore } from '../../store';
|
|
68
72
|
|
|
69
73
|
interface ChatComposerInternalProps {
|
|
@@ -88,10 +92,12 @@ export const ChatComposer = memo(
|
|
|
88
92
|
|
|
89
93
|
const {
|
|
90
94
|
isBotConversation,
|
|
95
|
+
conversation,
|
|
91
96
|
conversationFlow,
|
|
92
97
|
conversationFlowMainMenu,
|
|
93
98
|
flowState,
|
|
94
99
|
} = useChatDetailContext();
|
|
100
|
+
const isGroupConversation = conversation?.peerType === PeerType.Group;
|
|
95
101
|
const { mutate: runFlow, isPending } = useRunFlow();
|
|
96
102
|
const { mutate: resetFlow } = useResetFlow();
|
|
97
103
|
const { mutateAsync: closeSupportSession } = useCloseSupportSession();
|
|
@@ -409,7 +415,7 @@ export const ChatComposer = memo(
|
|
|
409
415
|
conversationFlow?.flowCurrentStep?.type === ReactFlowsNodeTypes.Start &&
|
|
410
416
|
isEmptyMessage;
|
|
411
417
|
|
|
412
|
-
if (isDisableCompose) {
|
|
418
|
+
if (isDisableCompose && !isGroupConversation) {
|
|
413
419
|
content = (
|
|
414
420
|
<KContainer.View row alignItems justifyContent>
|
|
415
421
|
<KContainer.View marginR="0.25rem">
|
|
@@ -441,14 +447,17 @@ export const ChatComposer = memo(
|
|
|
441
447
|
content = (
|
|
442
448
|
<KContainer.View background={'#F9F9FA'}>
|
|
443
449
|
<ChatCurrentQuotedMessage />
|
|
444
|
-
<
|
|
445
|
-
visible
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
450
|
+
<KContainer.VisibleView
|
|
451
|
+
visible={conversation?.peerType === PeerType.Bot}
|
|
452
|
+
>
|
|
453
|
+
<ChatQuickActions
|
|
454
|
+
visible
|
|
455
|
+
actions={DEFAULT_QUICK_ACTIONS}
|
|
456
|
+
onActionPress={(action) =>
|
|
457
|
+
console.log('[ChatComposer] quick action:', action.id)
|
|
458
|
+
}
|
|
459
|
+
/>
|
|
460
|
+
</KContainer.VisibleView>
|
|
452
461
|
{compose.showLinkPreview && (
|
|
453
462
|
<ChatLinkPreview
|
|
454
463
|
metadata={compose.urlMetadata}
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
} from '../../hooks/conversation/useGetConversationDetail';
|
|
33
33
|
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
34
34
|
import { useMessages } from '../../hooks/message/useMessages';
|
|
35
|
-
import { ChatEvent } from '../../types/events';
|
|
35
|
+
import { ChatEvent, type GroupRemovedPayload } from '../../types/events';
|
|
36
36
|
import { useSetIsShowFLashMessage } from '../../context/global';
|
|
37
37
|
|
|
38
38
|
const ChatDetail = memo(
|
|
@@ -112,6 +112,17 @@ const ChatDetail = memo(
|
|
|
112
112
|
};
|
|
113
113
|
}, [conversationId, targetUserId, targetGroupId, setShowFLashMessage]);
|
|
114
114
|
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
const sub = DeviceEventEmitter.addListener(
|
|
117
|
+
ChatEvent.GroupRemoved,
|
|
118
|
+
(payload: GroupRemovedPayload) => {
|
|
119
|
+
if (payload.groupID !== targetGroupId) return;
|
|
120
|
+
(onBack ?? handleBackDefault)();
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
return () => sub.remove();
|
|
124
|
+
}, [targetGroupId, onBack, handleBackDefault]);
|
|
125
|
+
|
|
115
126
|
const resolvedShowAddMember =
|
|
116
127
|
showAddMember ?? shouldShowAddMember(conversation);
|
|
117
128
|
|
|
@@ -12,6 +12,8 @@ import type { ChatDetailHeaderProps } from './types';
|
|
|
12
12
|
import { useConversation } from '../../store';
|
|
13
13
|
import { getConversationSubtitle } from './conversationHeader.utils';
|
|
14
14
|
import { useGetUsersInfo } from '../../hooks/users/useGetUsersInfo';
|
|
15
|
+
import { useGetGroupInfo } from '../../hooks/conversation/useGetGroupInfo';
|
|
16
|
+
import { isGroupConversation } from '../../utils/conversation';
|
|
15
17
|
|
|
16
18
|
const ChatDetailHeader = memo(
|
|
17
19
|
({
|
|
@@ -28,7 +30,10 @@ const ChatDetailHeader = memo(
|
|
|
28
30
|
const { data: usersInfo } = useGetUsersInfo(
|
|
29
31
|
targetUserId ? [targetUserId] : []
|
|
30
32
|
);
|
|
31
|
-
|
|
33
|
+
const isGroup = isGroupConversation(conversation);
|
|
34
|
+
const { data: groupInfo } = useGetGroupInfo(
|
|
35
|
+
isGroup ? conversation?.groupID : undefined
|
|
36
|
+
);
|
|
32
37
|
const { faceURL, showName } = useMemo(() => {
|
|
33
38
|
const userInfo = usersInfo?.[0];
|
|
34
39
|
const resolvedFaceURL = conversation?.faceURL || userInfo?.faceURL;
|
|
@@ -103,6 +108,7 @@ const ChatDetailHeader = memo(
|
|
|
103
108
|
avatar={faceURL || null}
|
|
104
109
|
fullName={showName || ''}
|
|
105
110
|
peerType={conversation?.peerType}
|
|
111
|
+
members={conversation?.members}
|
|
106
112
|
/>
|
|
107
113
|
|
|
108
114
|
<KContainer.View flex marginL="0.5rem">
|
|
@@ -123,14 +129,24 @@ const ChatDetailHeader = memo(
|
|
|
123
129
|
color={KColors.palette.gray.w500}
|
|
124
130
|
numberOfLines={1}
|
|
125
131
|
>
|
|
126
|
-
{getConversationSubtitle(
|
|
132
|
+
{getConversationSubtitle(
|
|
133
|
+
conversation,
|
|
134
|
+
groupInfo?.memberCount
|
|
135
|
+
)}
|
|
127
136
|
</KLabel.Text>
|
|
128
137
|
)}
|
|
129
138
|
</KContainer.View>
|
|
130
139
|
</KContainer.Touchable>
|
|
131
140
|
</KContainer.View>
|
|
132
141
|
),
|
|
133
|
-
[
|
|
142
|
+
[
|
|
143
|
+
onBack,
|
|
144
|
+
onPressAvatar,
|
|
145
|
+
conversation,
|
|
146
|
+
faceURL,
|
|
147
|
+
showName,
|
|
148
|
+
groupInfo?.memberCount,
|
|
149
|
+
]
|
|
134
150
|
);
|
|
135
151
|
|
|
136
152
|
return (
|
|
@@ -18,18 +18,26 @@ export const getConversationAvatarUri = (
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export const getConversationSubtitle = (
|
|
21
|
-
conversation?: ConversationItem
|
|
21
|
+
conversation?: ConversationItem,
|
|
22
|
+
memberCount?: number
|
|
22
23
|
): string | undefined => {
|
|
23
24
|
if (!conversation) {
|
|
24
25
|
return undefined;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
if (conversation.peerType === PeerType.Group) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
if (typeof memberCount !== 'number') {
|
|
30
|
+
return 'Nhấn để xem thông tin';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// const unreadLabel =
|
|
34
|
+
// (conversation.unreadCount ?? 0) > 0
|
|
35
|
+
// ? `${conversation.unreadCount} tin nhắn mới`
|
|
36
|
+
// : 'Không có tin mới';
|
|
37
|
+
// if (memberCount == null) {
|
|
38
|
+
// return unreadLabel;
|
|
39
|
+
// }
|
|
40
|
+
return `${memberCount} thành viên`;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
if (conversation.peerType === PeerType.Customer) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KContainer, KImage, KColors } from '@droppii/libs';
|
|
1
|
+
import { KContainer, KImage, KColors, KSpacingValue } from '@droppii/libs';
|
|
2
2
|
import { StyleSheet } from 'react-native';
|
|
3
3
|
import { memo, useCallback, useMemo } from 'react';
|
|
4
4
|
import type { ComponentType, PropsWithChildren, ReactNode } from 'react';
|
|
@@ -14,11 +14,13 @@ import { FileMessage } from '../../../components/messages/fileMessage';
|
|
|
14
14
|
import { LinkMessage } from '../../../components/messages/linkMessage';
|
|
15
15
|
import { MergedMessage } from '../../../components/messages/mergedMessage';
|
|
16
16
|
import { RevokedMessage } from '../../../components/messages/revokedMessage';
|
|
17
|
+
import { GroupNotificationMessage } from '../../../components/messages/groupNotificationMessage';
|
|
17
18
|
import { MessageStatusIndicator } from '../../../components/messages/MessageStatusIndicator';
|
|
18
19
|
import type { BaseLegendMessageProps } from '../../../components/messages/types';
|
|
19
20
|
import { messageStyles } from './messageTypes';
|
|
20
21
|
import QuotedMessage from '../../../components/messages/quotedMessage';
|
|
21
22
|
import { StickerMessage } from '../../../components/messages/stickerMessage';
|
|
23
|
+
import { GROUP_NOTIFICATION_TEXT } from '../../../utils/messageUtils';
|
|
22
24
|
|
|
23
25
|
type MessageProps = BaseLegendMessageProps & {
|
|
24
26
|
onPressUrl?: (url: string) => void;
|
|
@@ -38,6 +40,16 @@ interface LegendChatMessageProps {
|
|
|
38
40
|
isHighlighted?: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
// Notification-style messages (group created/renamed/member changes/etc.)
|
|
44
|
+
// are system banners, not user-authored text — they render centered,
|
|
45
|
+
// full-width, without a bubble, avatar/status chrome, or left/right
|
|
46
|
+
// alignment (see SYSTEM_MESSAGE_TYPES below). Driven by the same lookup
|
|
47
|
+
// messageUtils uses to build their preview/thread text, so adding a new
|
|
48
|
+
// notification content type there is enough to cover both places.
|
|
49
|
+
const GROUP_NOTIFICATION_TYPES = Object.keys(GROUP_NOTIFICATION_TEXT).map(
|
|
50
|
+
Number
|
|
51
|
+
) as MessageType[];
|
|
52
|
+
|
|
41
53
|
const messageComponentMap: Partial<
|
|
42
54
|
Record<MessageType, ComponentType<MessageProps>>
|
|
43
55
|
> = {
|
|
@@ -51,6 +63,9 @@ const messageComponentMap: Partial<
|
|
|
51
63
|
[MessageType.MergeMessage]: MergedMessage,
|
|
52
64
|
[MessageType.RevokeMessage]: RevokedMessage,
|
|
53
65
|
[MessageType.StickerMessage]: StickerMessage,
|
|
66
|
+
...Object.fromEntries(
|
|
67
|
+
GROUP_NOTIFICATION_TYPES.map((type) => [type, GroupNotificationMessage])
|
|
68
|
+
),
|
|
54
69
|
};
|
|
55
70
|
|
|
56
71
|
const MEDIA_TYPES: number[] = [
|
|
@@ -59,6 +74,8 @@ const MEDIA_TYPES: number[] = [
|
|
|
59
74
|
MessageType.MergeMessage,
|
|
60
75
|
];
|
|
61
76
|
|
|
77
|
+
const SYSTEM_MESSAGE_TYPES: number[] = GROUP_NOTIFICATION_TYPES;
|
|
78
|
+
|
|
62
79
|
export const LegendChatMessage = memo(
|
|
63
80
|
({
|
|
64
81
|
message,
|
|
@@ -86,6 +103,20 @@ export const LegendChatMessage = memo(
|
|
|
86
103
|
return customNode;
|
|
87
104
|
}
|
|
88
105
|
|
|
106
|
+
if (SYSTEM_MESSAGE_TYPES.includes(message?.contentType)) {
|
|
107
|
+
const SystemMessageComponent =
|
|
108
|
+
messageComponentMap[message.contentType] ?? TextMessage;
|
|
109
|
+
return (
|
|
110
|
+
<KContainer.View center style={styles.systemMessageWrapper}>
|
|
111
|
+
<SystemMessageComponent
|
|
112
|
+
message={message}
|
|
113
|
+
isOutgoing={isOutgoing}
|
|
114
|
+
createdAtTime={createdAtTime}
|
|
115
|
+
/>
|
|
116
|
+
</KContainer.View>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
89
120
|
const MessageComponent: ComponentType<MessageProps> =
|
|
90
121
|
messageComponentMap[message?.contentType] ?? TextMessage;
|
|
91
122
|
|
|
@@ -134,6 +165,10 @@ const styles = StyleSheet.create({
|
|
|
134
165
|
content: {
|
|
135
166
|
position: 'relative',
|
|
136
167
|
},
|
|
168
|
+
systemMessageWrapper: {
|
|
169
|
+
width: '100%',
|
|
170
|
+
marginVertical: KSpacingValue['0.25rem'],
|
|
171
|
+
},
|
|
137
172
|
pinBadge: {
|
|
138
173
|
position: 'absolute',
|
|
139
174
|
// Capped at the inter-row gap (messageRowLeft/Right's marginBottom) so the
|