@droppii-org/chat-mobile 0.2.66 → 0.2.68
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 +121 -21
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/hooks/conversation/useGetGroupInfo.js +3 -1
- package/lib/module/hooks/conversation/useGetGroupInfo.js.map +1 -1
- package/lib/module/hooks/message/usePinMessage.js +4 -2
- package/lib/module/hooks/message/usePinMessage.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +13 -15
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/useConversationList.js +9 -4
- package/lib/module/hooks/useConversationList.js.map +1 -1
- package/lib/module/hooks/useGroupPermission.js +34 -0
- package/lib/module/hooks/useGroupPermission.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/chat-detail/ChatComposer.js +16 -18
- 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/chat-detail/messageToolbox/MessageToolboxOverlay.js +5 -0
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.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 +32 -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/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.map +1 -1
- package/lib/typescript/src/hooks/message/usePinMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useGroupPermission.d.ts +6 -0
- package/lib/typescript/src/hooks/useGroupPermission.d.ts.map +1 -0
- package/lib/typescript/src/screens/ChatInfo/ChatInfoProfile.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/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.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/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 +176 -23
- package/src/hooks/conversation/useGetGroupInfo.ts +2 -0
- package/src/hooks/message/usePinMessage.ts +8 -2
- package/src/hooks/message/useSendMessage.ts +22 -18
- package/src/hooks/useConversationList.ts +17 -7
- package/src/hooks/useGroupPermission.ts +43 -0
- package/src/screens/ChatInfo/ChatInfoProfile.tsx +6 -3
- package/src/screens/chat-detail/ChatComposer.tsx +29 -32
- 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/chat-detail/messageToolbox/MessageToolboxOverlay.tsx +8 -1
- package/src/screens/inbox/MessagesTab.tsx +0 -1
- package/src/store/conversation.ts +62 -17
- package/src/translation/resources/i18n.ts +32 -0
- package/src/types/events.ts +6 -0
- package/src/utils/messageUtils.ts +103 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { KContainer, KColors } from '@droppii/libs';
|
|
4
|
+
import { SingleAvatar } from './SingleAvatar';
|
|
5
|
+
|
|
6
|
+
interface TripleAvatarProps {
|
|
7
|
+
primarySource: string | null | undefined;
|
|
8
|
+
secondarySource: string | null | undefined;
|
|
9
|
+
tertiarySource: string | null | undefined;
|
|
10
|
+
size: number;
|
|
11
|
+
borderColor: string;
|
|
12
|
+
borderWidth: number;
|
|
13
|
+
placeholderColor: string;
|
|
14
|
+
primaryFullName?: string;
|
|
15
|
+
secondaryFullName?: string;
|
|
16
|
+
tertiaryFullName?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const TripleAvatar = memo(
|
|
20
|
+
({
|
|
21
|
+
primarySource,
|
|
22
|
+
secondarySource,
|
|
23
|
+
tertiarySource,
|
|
24
|
+
size,
|
|
25
|
+
borderColor,
|
|
26
|
+
borderWidth,
|
|
27
|
+
placeholderColor,
|
|
28
|
+
primaryFullName,
|
|
29
|
+
secondaryFullName,
|
|
30
|
+
tertiaryFullName,
|
|
31
|
+
}: TripleAvatarProps) => {
|
|
32
|
+
const circleSize = Math.round(size * 0.6);
|
|
33
|
+
const topOffset = Math.round((size - circleSize) / 2);
|
|
34
|
+
const clusterBorderWidth = borderWidth + 1;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<KContainer.View
|
|
38
|
+
style={[styles.tripleAvatarContainer, { width: size, height: size }]}
|
|
39
|
+
>
|
|
40
|
+
<KContainer.View
|
|
41
|
+
style={[
|
|
42
|
+
styles.tripleAvatarChild,
|
|
43
|
+
styles.tripleAvatarBottomLeft,
|
|
44
|
+
{ width: circleSize, height: circleSize },
|
|
45
|
+
]}
|
|
46
|
+
>
|
|
47
|
+
<SingleAvatar
|
|
48
|
+
source={secondarySource}
|
|
49
|
+
size={circleSize}
|
|
50
|
+
borderColor={borderColor}
|
|
51
|
+
borderWidth={clusterBorderWidth}
|
|
52
|
+
placeholderColor={placeholderColor}
|
|
53
|
+
backgroundColor={KColors.palette.gray.w300}
|
|
54
|
+
fullName={secondaryFullName}
|
|
55
|
+
/>
|
|
56
|
+
</KContainer.View>
|
|
57
|
+
<KContainer.View
|
|
58
|
+
style={[
|
|
59
|
+
styles.tripleAvatarChild,
|
|
60
|
+
styles.tripleAvatarBottomRight,
|
|
61
|
+
{ width: circleSize, height: circleSize },
|
|
62
|
+
]}
|
|
63
|
+
>
|
|
64
|
+
<SingleAvatar
|
|
65
|
+
source={tertiarySource}
|
|
66
|
+
size={circleSize}
|
|
67
|
+
borderColor={borderColor}
|
|
68
|
+
borderWidth={clusterBorderWidth}
|
|
69
|
+
placeholderColor={placeholderColor}
|
|
70
|
+
backgroundColor={KColors.palette.gray.w300}
|
|
71
|
+
fullName={tertiaryFullName}
|
|
72
|
+
/>
|
|
73
|
+
</KContainer.View>
|
|
74
|
+
<KContainer.View
|
|
75
|
+
style={[
|
|
76
|
+
styles.tripleAvatarChild,
|
|
77
|
+
styles.tripleAvatarTop,
|
|
78
|
+
{ width: circleSize, height: circleSize, left: topOffset },
|
|
79
|
+
]}
|
|
80
|
+
>
|
|
81
|
+
<SingleAvatar
|
|
82
|
+
source={primarySource}
|
|
83
|
+
size={circleSize}
|
|
84
|
+
borderColor={borderColor}
|
|
85
|
+
borderWidth={clusterBorderWidth}
|
|
86
|
+
placeholderColor={placeholderColor}
|
|
87
|
+
backgroundColor={KColors.palette.gray.w300}
|
|
88
|
+
fullName={primaryFullName}
|
|
89
|
+
/>
|
|
90
|
+
</KContainer.View>
|
|
91
|
+
</KContainer.View>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
TripleAvatar.displayName = 'TripleAvatar';
|
|
97
|
+
|
|
98
|
+
const styles = StyleSheet.create({
|
|
99
|
+
tripleAvatarContainer: {
|
|
100
|
+
position: 'relative',
|
|
101
|
+
},
|
|
102
|
+
tripleAvatarChild: {
|
|
103
|
+
position: 'absolute',
|
|
104
|
+
},
|
|
105
|
+
tripleAvatarTop: {
|
|
106
|
+
top: 0,
|
|
107
|
+
zIndex: 3,
|
|
108
|
+
},
|
|
109
|
+
tripleAvatarBottomLeft: {
|
|
110
|
+
bottom: 0,
|
|
111
|
+
left: 0,
|
|
112
|
+
zIndex: 1,
|
|
113
|
+
},
|
|
114
|
+
tripleAvatarBottomRight: {
|
|
115
|
+
bottom: 0,
|
|
116
|
+
right: 0,
|
|
117
|
+
zIndex: 2,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { default as Avatar } from './Avatar';
|
|
2
2
|
export { SingleAvatar } from './SingleAvatar';
|
|
3
3
|
export { DoubleAvatar } from './DoubleAvatar';
|
|
4
|
+
export { TripleAvatar } from './TripleAvatar';
|
|
5
|
+
export { QuadAvatar } from './QuadAvatar';
|
|
4
6
|
export { AvatarBadge } from './AvatarBadge';
|
|
5
7
|
export type { AvatarProps } from './Avatar.types';
|
|
@@ -3,7 +3,7 @@ import { Avatar } from '../Avatar';
|
|
|
3
3
|
import { PeerType, type ConversationItem } from '@droppii/openim-rn-client-sdk';
|
|
4
4
|
|
|
5
5
|
interface AvatarSectionProps extends Partial<
|
|
6
|
-
Pick<ConversationItem, 'peerType'>
|
|
6
|
+
Pick<ConversationItem, 'peerType' | 'members'>
|
|
7
7
|
> {
|
|
8
8
|
avatar: string | null;
|
|
9
9
|
fullName: string;
|
|
@@ -11,7 +11,13 @@ interface AvatarSectionProps extends Partial<
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const AvatarSection = memo(
|
|
14
|
-
({
|
|
14
|
+
({
|
|
15
|
+
avatar,
|
|
16
|
+
fullName,
|
|
17
|
+
peerType,
|
|
18
|
+
members,
|
|
19
|
+
size = 'md',
|
|
20
|
+
}: AvatarSectionProps) => {
|
|
15
21
|
if (peerType === PeerType.Bot) {
|
|
16
22
|
return (
|
|
17
23
|
<Avatar
|
|
@@ -34,6 +40,56 @@ export const AvatarSection = memo(
|
|
|
34
40
|
);
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
const groupMembers = Array.isArray(members) ? members : [];
|
|
44
|
+
|
|
45
|
+
if (peerType === PeerType.Group && !avatar && groupMembers.length >= 4) {
|
|
46
|
+
const [first, second, third] = groupMembers;
|
|
47
|
+
const remainingCount = groupMembers.length - 3;
|
|
48
|
+
return (
|
|
49
|
+
<Avatar
|
|
50
|
+
type="quad"
|
|
51
|
+
source={first?.faceURL ?? null}
|
|
52
|
+
fullName={first?.nickname}
|
|
53
|
+
secondarySource={second?.faceURL ?? null}
|
|
54
|
+
secondaryFullName={second?.nickname}
|
|
55
|
+
tertiarySource={third?.faceURL ?? null}
|
|
56
|
+
tertiaryFullName={third?.nickname}
|
|
57
|
+
remainingCount={remainingCount}
|
|
58
|
+
size={size}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (peerType === PeerType.Group && !avatar && groupMembers.length === 3) {
|
|
64
|
+
const [first, second, third] = groupMembers;
|
|
65
|
+
return (
|
|
66
|
+
<Avatar
|
|
67
|
+
type="triple"
|
|
68
|
+
source={first?.faceURL ?? null}
|
|
69
|
+
fullName={first?.nickname}
|
|
70
|
+
secondarySource={second?.faceURL ?? null}
|
|
71
|
+
secondaryFullName={second?.nickname}
|
|
72
|
+
tertiarySource={third?.faceURL ?? null}
|
|
73
|
+
tertiaryFullName={third?.nickname}
|
|
74
|
+
size={size}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (peerType === PeerType.Group && !avatar && groupMembers.length === 2) {
|
|
80
|
+
const [first, second] = groupMembers;
|
|
81
|
+
return (
|
|
82
|
+
<Avatar
|
|
83
|
+
type="double"
|
|
84
|
+
source={first?.faceURL ?? null}
|
|
85
|
+
fullName={first?.nickname}
|
|
86
|
+
secondarySource={second?.faceURL ?? null}
|
|
87
|
+
secondaryFullName={second?.nickname}
|
|
88
|
+
size={size}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
37
93
|
return <Avatar source={avatar} fullName={fullName} size={size} />;
|
|
38
94
|
}
|
|
39
95
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { memo } from 'react';
|
|
2
2
|
import { Image, StyleSheet } from 'react-native';
|
|
3
|
-
import { KContainer, KImage, KColors } from '@droppii/libs';
|
|
3
|
+
import { KContainer, KImage, KColors, type KSpacing } from '@droppii/libs';
|
|
4
4
|
import Images from '../../assets/images';
|
|
5
5
|
import { PeerType } from '@droppii/openim-rn-client-sdk';
|
|
6
6
|
|
|
@@ -13,34 +13,41 @@ const styles = StyleSheet.create({
|
|
|
13
13
|
|
|
14
14
|
interface NamePrefixIconProps {
|
|
15
15
|
peerType?: PeerType | string;
|
|
16
|
+
marginR?: KSpacing;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export const NamePrefixIcon = memo(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
export const NamePrefixIcon = memo(
|
|
20
|
+
({ peerType, marginR = '0.25rem' }: NamePrefixIconProps) => {
|
|
21
|
+
if (peerType === PeerType.Bot) {
|
|
22
|
+
return (
|
|
23
|
+
<KContainer.View marginR={marginR}>
|
|
24
|
+
<Image source={Images.ICON_DROPPII} style={styles.icon} />
|
|
25
|
+
</KContainer.View>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
if (peerType === PeerType.Group) {
|
|
30
|
+
return (
|
|
31
|
+
<KContainer.View marginR={marginR}>
|
|
32
|
+
<KImage.VectorIcons
|
|
33
|
+
name="user-two-b"
|
|
34
|
+
size={14}
|
|
35
|
+
color={KColors.black}
|
|
36
|
+
/>
|
|
37
|
+
</KContainer.View>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// if (peerType === DChatPeerType.CUSTOMER) {
|
|
42
|
+
// return (
|
|
43
|
+
// <KContainer.View marginR={marginR}>
|
|
44
|
+
// <Image source={Images.ICON_DROPPII} style={styles.icon} />
|
|
45
|
+
// </KContainer.View>
|
|
46
|
+
// );
|
|
47
|
+
// }
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
);
|
|
45
52
|
|
|
46
53
|
NamePrefixIcon.displayName = 'NamePrefixIcon';
|
|
@@ -33,6 +33,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
|
|
|
33
33
|
peerType,
|
|
34
34
|
showName,
|
|
35
35
|
faceURL,
|
|
36
|
+
members,
|
|
36
37
|
unreadCount = 0,
|
|
37
38
|
recvMsgOpt = MessageReceiveOptType.Normal,
|
|
38
39
|
isPinned,
|
|
@@ -120,6 +121,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
|
|
|
120
121
|
avatar={faceURL ?? null}
|
|
121
122
|
fullName={fullName}
|
|
122
123
|
peerType={peerType}
|
|
124
|
+
members={members}
|
|
123
125
|
/>
|
|
124
126
|
|
|
125
127
|
{/* Chat-Content */}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { KLabel } from '@droppii/libs';
|
|
3
|
+
import { trans } from '../../../translation';
|
|
4
|
+
import { CHAT_BUBBLE_COLORS } from '../../../screens/chat-detail/constants';
|
|
5
|
+
import {
|
|
6
|
+
GROUP_NOTIFICATION_TEXT,
|
|
7
|
+
getNotificationOpUserNickname,
|
|
8
|
+
} from '../../../utils/messageUtils';
|
|
9
|
+
import type { BaseLegendMessageProps } from '../types';
|
|
10
|
+
|
|
11
|
+
export const GroupNotificationMessage = memo(
|
|
12
|
+
({ message, isOutgoing }: BaseLegendMessageProps) => {
|
|
13
|
+
const notification = GROUP_NOTIFICATION_TEXT[message.contentType];
|
|
14
|
+
if (!notification) return null;
|
|
15
|
+
|
|
16
|
+
const sender = isOutgoing
|
|
17
|
+
? 'Bạn'
|
|
18
|
+
: message.senderNickname || getNotificationOpUserNickname(message) || '';
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<KLabel.Text typo="TextXsNormal" color={CHAT_BUBBLE_COLORS.dayLabel}>
|
|
22
|
+
{trans(notification.bySenderKey, { sender })}
|
|
23
|
+
</KLabel.Text>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
GroupNotificationMessage.displayName = 'GroupNotificationMessage';
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
useEffect,
|
|
6
6
|
useMemo,
|
|
7
7
|
useState,
|
|
8
|
+
type ComponentType,
|
|
8
9
|
} from 'react';
|
|
9
10
|
import { DeviceEventEmitter } from 'react-native';
|
|
10
11
|
import {
|
|
@@ -189,3 +190,17 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
|
|
|
189
190
|
</ChatDetailContext.Provider>
|
|
190
191
|
);
|
|
191
192
|
};
|
|
193
|
+
|
|
194
|
+
export function withChatDetailProvider<P extends object>(
|
|
195
|
+
Component: ComponentType<P>
|
|
196
|
+
): ComponentType<P> {
|
|
197
|
+
const WithChatDetailProvider = (props: P) => (
|
|
198
|
+
<ChatDetailProvider>
|
|
199
|
+
<Component {...props} />
|
|
200
|
+
</ChatDetailProvider>
|
|
201
|
+
);
|
|
202
|
+
WithChatDetailProvider.displayName = `withChatDetailProvider(${
|
|
203
|
+
Component.displayName || Component.name || 'Component'
|
|
204
|
+
})`;
|
|
205
|
+
return WithChatDetailProvider;
|
|
206
|
+
}
|
|
@@ -2,12 +2,15 @@ import OpenIMSDK, {
|
|
|
2
2
|
MessageReceiveOptType,
|
|
3
3
|
MessageType,
|
|
4
4
|
OpenIMEvent,
|
|
5
|
+
SessionType,
|
|
5
6
|
type ConversationItem,
|
|
7
|
+
type GroupItem,
|
|
6
8
|
type GroupMemberItem,
|
|
7
9
|
type MessageItem,
|
|
8
10
|
type MessagePinned,
|
|
9
11
|
type RevokedInfo,
|
|
10
12
|
} from '@droppii/openim-rn-client-sdk';
|
|
13
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
11
14
|
import { useCallback, useEffect } from 'react';
|
|
12
15
|
import { useConversationStore } from '../store/conversation';
|
|
13
16
|
import { ToastFlashMessage } from '../utils/toastFlashMessage';
|
|
@@ -18,22 +21,31 @@ import isEmpty from 'lodash/isEmpty';
|
|
|
18
21
|
import { markMessageRevoked, useMessageStore } from '../store/message';
|
|
19
22
|
import { syncPinnedMessagesOnEvent } from '../hooks/message/usePinMessage';
|
|
20
23
|
import { belongsToConversation } from '../utils/message';
|
|
24
|
+
import { getConversationID } from '../utils/conversation';
|
|
21
25
|
import { DeviceEventEmitter } from 'react-native';
|
|
22
26
|
import { ChatEvent } from '../types/events';
|
|
27
|
+
import { conversationQueryKeys } from '../hooks/query-keys';
|
|
23
28
|
|
|
24
29
|
export const useChatListener = () => {
|
|
25
30
|
const updateOneMessage = useMessageStore((state) => state.updateOneMessage);
|
|
26
31
|
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
27
32
|
const onPressFlashMessage = useChatContext()?.onPressFlashMessage;
|
|
28
33
|
const isShowFLashMessage = useChatContext()?.isShowFLashMessage;
|
|
34
|
+
const queryClient = useQueryClient();
|
|
29
35
|
|
|
30
36
|
const onSendMessageProgress = useCallback(
|
|
31
|
-
({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
({
|
|
38
|
+
progress: _progress,
|
|
39
|
+
message,
|
|
40
|
+
}: {
|
|
41
|
+
progress: number;
|
|
42
|
+
message: MessageItem;
|
|
43
|
+
}) => {
|
|
44
|
+
// console.log('[Listener] SendMessageProgress', {
|
|
45
|
+
// clientMsgID: message.clientMsgID,
|
|
46
|
+
// status: message.status,
|
|
47
|
+
// progress,
|
|
48
|
+
// });
|
|
37
49
|
updateOneMessage(message);
|
|
38
50
|
},
|
|
39
51
|
[updateOneMessage]
|
|
@@ -41,6 +53,7 @@ export const useChatListener = () => {
|
|
|
41
53
|
|
|
42
54
|
const onConversationChanged = useCallback(
|
|
43
55
|
(conversations: ConversationItem[]) => {
|
|
56
|
+
// console.log('[Listener] OnConversationChanged', conversations);
|
|
44
57
|
const oldMap = useConversationStore.getState().map;
|
|
45
58
|
const visibleConversations: ConversationItem[] = [];
|
|
46
59
|
|
|
@@ -87,26 +100,157 @@ export const useChatListener = () => {
|
|
|
87
100
|
);
|
|
88
101
|
|
|
89
102
|
const onNewConversation = useCallback((conversations: ConversationItem[]) => {
|
|
90
|
-
console.log(
|
|
103
|
+
// console.log(
|
|
104
|
+
// '[Listener] OnNewConversation',
|
|
105
|
+
// conversations.map((c) => ({
|
|
106
|
+
// conversationID: c.conversationID,
|
|
107
|
+
// groupID: c.groupID,
|
|
108
|
+
// isNotInGroup: c.isNotInGroup,
|
|
109
|
+
// isGuardedRemoved: c.groupID
|
|
110
|
+
// ? useConversationStore.getState().removedGroupIds.has(c.groupID)
|
|
111
|
+
// : false,
|
|
112
|
+
// }))
|
|
113
|
+
// );
|
|
91
114
|
useConversationStore.getState().updateConversations(conversations);
|
|
92
115
|
}, []);
|
|
93
116
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
groupID:
|
|
98
|
-
|
|
99
|
-
|
|
117
|
+
const onGroupInfoChanged = useCallback(
|
|
118
|
+
(group: GroupItem) => {
|
|
119
|
+
// console.log('[Listener] OnGroupInfoChanged', {
|
|
120
|
+
// groupID: group.groupID,
|
|
121
|
+
// memberCount: group.memberCount,
|
|
122
|
+
// });
|
|
123
|
+
queryClient.setQueryData(
|
|
124
|
+
conversationQueryKeys.groupInfo(group.groupID),
|
|
125
|
+
group
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
[queryClient]
|
|
129
|
+
);
|
|
100
130
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
const onGroupMemberAdded = useCallback(
|
|
132
|
+
(member: GroupMemberItem) => {
|
|
133
|
+
// console.log('[Listener] OnGroupMemberAdded', {
|
|
134
|
+
// userID: member.userID,
|
|
135
|
+
// groupID: member.groupID,
|
|
136
|
+
// isCurrentUser: member.userID === currentUserId,
|
|
137
|
+
// });
|
|
138
|
+
queryClient.invalidateQueries({
|
|
139
|
+
queryKey: conversationQueryKeys.groupInfo(member.groupID),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// The current user (re)joined this group — lift the removal guard so
|
|
143
|
+
// a future OnConversationChanged/OnNewConversation for it isn't dropped.
|
|
144
|
+
if (member.userID === currentUserId) {
|
|
145
|
+
useConversationStore
|
|
146
|
+
.getState()
|
|
147
|
+
.restoreGroupConversation(member.groupID);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
[queryClient, currentUserId]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const removeGroupConversation = useCallback(
|
|
154
|
+
(
|
|
155
|
+
groupID: string,
|
|
156
|
+
groupName: string | undefined,
|
|
157
|
+
reason: 'left' | 'dismissed' | 'kicked'
|
|
158
|
+
) => {
|
|
159
|
+
// console.log('[Listener] removeGroupConversation', { groupID, reason });
|
|
160
|
+
|
|
161
|
+
// TEMPORARILY DISABLED: OnConversationChanged/OnNewConversation can
|
|
162
|
+
// still race this removal and resurrect the conversation before the
|
|
163
|
+
// removedGroupIds guard is proven reliable. Flip this back to `true`
|
|
164
|
+
// once that race is confirmed fixed.
|
|
165
|
+
const REMOVE_FLOW_ENABLED: boolean = false;
|
|
166
|
+
if (!REMOVE_FLOW_ENABLED) return;
|
|
167
|
+
|
|
168
|
+
const conversationID = getConversationID(SessionType.Group, groupID);
|
|
169
|
+
const conversation = useConversationStore.getState().map[conversationID];
|
|
170
|
+
const isCurrentConversation =
|
|
171
|
+
useConversationStore.getState().currentConversationId ===
|
|
172
|
+
conversationID;
|
|
173
|
+
|
|
174
|
+
// Marks groupID as removed so any late-arriving conversation-sync event
|
|
175
|
+
// for it (see onConversationChanged/onNewConversation) is dropped
|
|
176
|
+
// instead of silently resurrecting the conversation we just removed.
|
|
177
|
+
useConversationStore.getState().removeGroupConversation(groupID);
|
|
178
|
+
|
|
179
|
+
queryClient.removeQueries({
|
|
180
|
+
queryKey: conversationQueryKeys.groupInfo(groupID),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
ToastFlashMessage.show({
|
|
184
|
+
key: `group-removed-${groupID}`,
|
|
185
|
+
title: groupName || conversation?.showName || 'Nhóm',
|
|
186
|
+
body:
|
|
187
|
+
reason === 'dismissed'
|
|
188
|
+
? 'Nhóm đã bị giải tán'
|
|
189
|
+
: reason === 'kicked'
|
|
190
|
+
? 'Bạn đã bị mời khỏi nhóm'
|
|
191
|
+
: 'Bạn không còn trong nhóm này',
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (isCurrentConversation) {
|
|
195
|
+
DeviceEventEmitter.emit(ChatEvent.GroupRemoved, {
|
|
196
|
+
groupID,
|
|
197
|
+
reason,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
[queryClient]
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const onGroupMemberDeleted = useCallback(
|
|
205
|
+
(member: GroupMemberItem) => {
|
|
206
|
+
// console.log('[Listener] OnGroupMemberDeleted', {
|
|
207
|
+
// userID: member.userID,
|
|
208
|
+
// groupID: member.groupID,
|
|
209
|
+
// isCurrentUser: member.userID === currentUserId,
|
|
210
|
+
// });
|
|
211
|
+
queryClient.invalidateQueries({
|
|
212
|
+
queryKey: conversationQueryKeys.groupInfo(member.groupID),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// The removed member is the current user themselves — they were kicked,
|
|
216
|
+
// so their conversation for this group must be dropped like a leave/dismiss.
|
|
217
|
+
if (member.userID === currentUserId) {
|
|
218
|
+
removeGroupConversation(member.groupID, undefined, 'kicked');
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
[queryClient, currentUserId, removeGroupConversation]
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const onJoinedGroupDeleted = useCallback(
|
|
225
|
+
(group: GroupItem) => {
|
|
226
|
+
// console.log('[Listener] OnJoinedGroupDeleted', {
|
|
227
|
+
// groupID: group.groupID,
|
|
228
|
+
// groupName: group.groupName,
|
|
229
|
+
// });
|
|
230
|
+
removeGroupConversation(group.groupID, group.groupName, 'left');
|
|
231
|
+
},
|
|
232
|
+
[removeGroupConversation]
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const onGroupDismissed = useCallback(
|
|
236
|
+
(group: GroupItem) => {
|
|
237
|
+
// console.log('[Listener] OnGroupDismissed', {
|
|
238
|
+
// groupID: group.groupID,
|
|
239
|
+
// groupName: group.groupName,
|
|
240
|
+
// });
|
|
241
|
+
removeGroupConversation(group.groupID, group.groupName, 'dismissed');
|
|
242
|
+
},
|
|
243
|
+
[removeGroupConversation]
|
|
244
|
+
);
|
|
107
245
|
|
|
108
246
|
const OnGroupMemberInfoChanged = useCallback(
|
|
109
247
|
(member: GroupMemberItem) => {
|
|
248
|
+
// console.log('[Listener] OnGroupMemberInfoChanged', {
|
|
249
|
+
// userID: member.userID,
|
|
250
|
+
// groupID: member.groupID,
|
|
251
|
+
// permissions: member.permissions,
|
|
252
|
+
// roleLevel: member.roleLevel,
|
|
253
|
+
// });
|
|
110
254
|
const conversation = useConversationStore
|
|
111
255
|
.getState()
|
|
112
256
|
.getCurrentConversation();
|
|
@@ -148,7 +292,7 @@ export const useChatListener = () => {
|
|
|
148
292
|
);
|
|
149
293
|
|
|
150
294
|
const onRecvNewMessages = useCallback((messages: MessageItem[]) => {
|
|
151
|
-
console.log('[Listener] OnRecvNewMessages', messages);
|
|
295
|
+
// console.log('[Listener] OnRecvNewMessages', messages);
|
|
152
296
|
const conversationId =
|
|
153
297
|
useConversationStore.getState().currentConversationId;
|
|
154
298
|
const targetUserId = useConversationStore.getState().currentUserId;
|
|
@@ -164,8 +308,8 @@ export const useChatListener = () => {
|
|
|
164
308
|
relevant.forEach((msg) => useMessageStore.getState().pushNewMessage(msg));
|
|
165
309
|
}, []);
|
|
166
310
|
|
|
167
|
-
const onRecvOfflineNewMessages = useCallback((
|
|
168
|
-
console.log('[Listener] onRecvOfflineNewMessages',
|
|
311
|
+
const onRecvOfflineNewMessages = useCallback((_messages: MessageItem[]) => {
|
|
312
|
+
// console.log('[Listener] onRecvOfflineNewMessages', _messages);
|
|
169
313
|
}, []);
|
|
170
314
|
|
|
171
315
|
useEffect(() => {
|
|
@@ -174,12 +318,15 @@ export const useChatListener = () => {
|
|
|
174
318
|
OpenIMSDK.on(OpenIMEvent.OnNewConversation, onNewConversation);
|
|
175
319
|
|
|
176
320
|
// Group members
|
|
321
|
+
OpenIMSDK.on(OpenIMEvent.OnGroupInfoChanged, onGroupInfoChanged);
|
|
177
322
|
OpenIMSDK.on(OpenIMEvent.OnGroupMemberAdded, onGroupMemberAdded);
|
|
178
323
|
OpenIMSDK.on(OpenIMEvent.OnGroupMemberDeleted, onGroupMemberDeleted);
|
|
179
324
|
OpenIMSDK.on(
|
|
180
325
|
OpenIMEvent.OnGroupMemberInfoChanged,
|
|
181
326
|
OnGroupMemberInfoChanged
|
|
182
327
|
);
|
|
328
|
+
OpenIMSDK.on(OpenIMEvent.OnJoinedGroupDeleted, onJoinedGroupDeleted);
|
|
329
|
+
OpenIMSDK.on(OpenIMEvent.OnGroupDismissed, onGroupDismissed);
|
|
183
330
|
|
|
184
331
|
// Message status
|
|
185
332
|
OpenIMSDK.on(OpenIMEvent.SendMessageProgress, onSendMessageProgress);
|
|
@@ -197,12 +344,15 @@ export const useChatListener = () => {
|
|
|
197
344
|
OpenIMSDK.off(OpenIMEvent.OnNewConversation, onNewConversation);
|
|
198
345
|
|
|
199
346
|
// Group members
|
|
347
|
+
OpenIMSDK.off(OpenIMEvent.OnGroupInfoChanged, onGroupInfoChanged);
|
|
200
348
|
OpenIMSDK.off(OpenIMEvent.OnGroupMemberAdded, onGroupMemberAdded);
|
|
201
349
|
OpenIMSDK.off(OpenIMEvent.OnGroupMemberDeleted, onGroupMemberDeleted);
|
|
202
|
-
OpenIMSDK.
|
|
350
|
+
OpenIMSDK.off(
|
|
203
351
|
OpenIMEvent.OnGroupMemberInfoChanged,
|
|
204
352
|
OnGroupMemberInfoChanged
|
|
205
353
|
);
|
|
354
|
+
OpenIMSDK.off(OpenIMEvent.OnJoinedGroupDeleted, onJoinedGroupDeleted);
|
|
355
|
+
OpenIMSDK.off(OpenIMEvent.OnGroupDismissed, onGroupDismissed);
|
|
206
356
|
|
|
207
357
|
// Message status
|
|
208
358
|
OpenIMSDK.off(OpenIMEvent.SendMessageProgress, onSendMessageProgress);
|
|
@@ -216,6 +366,7 @@ export const useChatListener = () => {
|
|
|
216
366
|
};
|
|
217
367
|
}, [
|
|
218
368
|
onConversationChanged,
|
|
369
|
+
onGroupInfoChanged,
|
|
219
370
|
onGroupMemberAdded,
|
|
220
371
|
onGroupMemberDeleted,
|
|
221
372
|
onMessagePinned,
|
|
@@ -225,5 +376,7 @@ export const useChatListener = () => {
|
|
|
225
376
|
onRecvNewMessages,
|
|
226
377
|
onRecvOfflineNewMessages,
|
|
227
378
|
OnGroupMemberInfoChanged,
|
|
379
|
+
onJoinedGroupDeleted,
|
|
380
|
+
onGroupDismissed,
|
|
228
381
|
]);
|
|
229
382
|
};
|