@droppii-org/chat-mobile 0.2.68 → 0.2.70
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/GroupItem/index.js +152 -0
- package/lib/module/components/GroupItem/index.js.map +1 -0
- package/lib/module/core/useChatListener.js +33 -2
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/hooks/conversation/useGetConversationDetail.js +2 -2
- package/lib/module/hooks/conversation/useGetConversationDetail.js.map +1 -1
- package/lib/module/hooks/groups/useFetchPublicGroup.js +39 -0
- package/lib/module/hooks/groups/useFetchPublicGroup.js.map +1 -0
- package/lib/module/hooks/groups/useJoinGroup.js +42 -0
- package/lib/module/hooks/groups/useJoinGroup.js.map +1 -0
- package/lib/module/hooks/query-keys.js +6 -0
- package/lib/module/hooks/query-keys.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/SearchConversation/TabResult.js +1 -1
- package/lib/module/screens/SearchConversation/TabResult.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +33 -4
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetail.js +20 -2
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js +16 -17
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/types.js.map +1 -1
- package/lib/module/screens/inbox/CommunityTab.js +55 -0
- package/lib/module/screens/inbox/CommunityTab.js.map +1 -0
- package/lib/module/screens/inbox/Inbox.js +51 -40
- package/lib/module/screens/inbox/Inbox.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +10 -1
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/typescript/src/components/GroupItem/index.d.ts +8 -0
- package/lib/typescript/src/components/GroupItem/index.d.ts.map +1 -0
- package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
- package/lib/typescript/src/hooks/groups/useFetchPublicGroup.d.ts +213 -0
- package/lib/typescript/src/hooks/groups/useFetchPublicGroup.d.ts.map +1 -0
- package/lib/typescript/src/hooks/groups/useJoinGroup.d.ts +5 -0
- package/lib/typescript/src/hooks/groups/useJoinGroup.d.ts.map +1 -0
- package/lib/typescript/src/hooks/query-keys.d.ts +6 -0
- package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +3 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts +2 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/CommunityTab.d.ts +6 -0
- package/lib/typescript/src/screens/inbox/CommunityTab.d.ts.map +1 -0
- package/lib/typescript/src/screens/inbox/Inbox.d.ts +2 -1
- package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/GroupItem/index.tsx +174 -0
- package/src/core/useChatListener.ts +53 -1
- package/src/hooks/conversation/useGetConversationDetail.ts +2 -2
- package/src/hooks/groups/useFetchPublicGroup.ts +43 -0
- package/src/hooks/groups/useJoinGroup.ts +46 -0
- package/src/hooks/query-keys.ts +8 -0
- package/src/index.tsx +5 -1
- package/src/screens/SearchConversation/TabResult.tsx +1 -1
- package/src/screens/chat-detail/ChatComposer.tsx +46 -3
- package/src/screens/chat-detail/ChatDetail.tsx +25 -1
- package/src/screens/chat-detail/ChatListLegend.tsx +11 -13
- package/src/screens/chat-detail/types.ts +5 -1
- package/src/screens/inbox/CommunityTab.tsx +65 -0
- package/src/screens/inbox/Inbox.tsx +51 -39
- package/src/translation/resources/i18n.ts +9 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KButton,
|
|
3
|
+
KColors,
|
|
4
|
+
KContainer,
|
|
5
|
+
KLabel,
|
|
6
|
+
KSpacingValue,
|
|
7
|
+
} from '@droppii/libs';
|
|
8
|
+
import {
|
|
9
|
+
GroupJoinSource,
|
|
10
|
+
type SearchPublicGroupInfo,
|
|
11
|
+
type SearchPublicGroupMemberInfo,
|
|
12
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
13
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
14
|
+
import { StyleSheet } from 'react-native';
|
|
15
|
+
import { Avatar } from '../Avatar';
|
|
16
|
+
import type { AvatarProps } from '../Avatar/Avatar.types';
|
|
17
|
+
import { trans } from '../../translation';
|
|
18
|
+
import { useJoinGroup } from '../../hooks/groups/useJoinGroup';
|
|
19
|
+
|
|
20
|
+
interface IGroupItemProps {
|
|
21
|
+
item: SearchPublicGroupInfo;
|
|
22
|
+
onPress?: (groupId: string) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const buildAvatarProps = (
|
|
26
|
+
faceURL: string,
|
|
27
|
+
groupName: string,
|
|
28
|
+
members: SearchPublicGroupMemberInfo[],
|
|
29
|
+
memberCount: number
|
|
30
|
+
): Pick<
|
|
31
|
+
AvatarProps,
|
|
32
|
+
| 'type'
|
|
33
|
+
| 'source'
|
|
34
|
+
| 'secondarySource'
|
|
35
|
+
| 'tertiarySource'
|
|
36
|
+
| 'remainingCount'
|
|
37
|
+
| 'fullName'
|
|
38
|
+
| 'secondaryFullName'
|
|
39
|
+
| 'tertiaryFullName'
|
|
40
|
+
> => {
|
|
41
|
+
if (faceURL) {
|
|
42
|
+
return { type: 'single', source: faceURL, fullName: groupName };
|
|
43
|
+
}
|
|
44
|
+
const [m0, m1, m2] = members;
|
|
45
|
+
if (members.length >= 4) {
|
|
46
|
+
return {
|
|
47
|
+
type: 'quad',
|
|
48
|
+
source: m0?.faceURL,
|
|
49
|
+
secondarySource: m1?.faceURL,
|
|
50
|
+
tertiarySource: m2?.faceURL,
|
|
51
|
+
remainingCount: memberCount - 3,
|
|
52
|
+
fullName: m0?.nickname,
|
|
53
|
+
secondaryFullName: m1?.nickname,
|
|
54
|
+
tertiaryFullName: m2?.nickname,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (members.length === 3) {
|
|
58
|
+
return {
|
|
59
|
+
type: 'triple',
|
|
60
|
+
source: m0?.faceURL,
|
|
61
|
+
secondarySource: m1?.faceURL,
|
|
62
|
+
tertiarySource: m2?.faceURL,
|
|
63
|
+
fullName: m0?.nickname,
|
|
64
|
+
secondaryFullName: m1?.nickname,
|
|
65
|
+
tertiaryFullName: m2?.nickname,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (members.length === 2) {
|
|
69
|
+
return {
|
|
70
|
+
type: 'double',
|
|
71
|
+
source: m0?.faceURL,
|
|
72
|
+
secondarySource: m1?.faceURL,
|
|
73
|
+
fullName: m0?.nickname,
|
|
74
|
+
secondaryFullName: m1?.nickname,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
type: 'single',
|
|
79
|
+
source: faceURL,
|
|
80
|
+
fullName: groupName,
|
|
81
|
+
remainingCount: memberCount,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const GroupItem = memo((props: IGroupItemProps) => {
|
|
86
|
+
const { item, onPress } = props || {};
|
|
87
|
+
const {
|
|
88
|
+
groupID,
|
|
89
|
+
groupName = '',
|
|
90
|
+
memberCount = 0,
|
|
91
|
+
introduction = '',
|
|
92
|
+
faceURL = '',
|
|
93
|
+
isJoined = false,
|
|
94
|
+
members = [],
|
|
95
|
+
} = item || {};
|
|
96
|
+
|
|
97
|
+
const { mutate: joinGroup, isPending } = useJoinGroup();
|
|
98
|
+
|
|
99
|
+
const avatarProps = useMemo(
|
|
100
|
+
() => buildAvatarProps(faceURL, groupName, members, memberCount),
|
|
101
|
+
[faceURL, groupName, members, memberCount]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const handleActionPress = useCallback(() => {
|
|
105
|
+
if (isJoined) {
|
|
106
|
+
onPress?.(groupID);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
joinGroup(
|
|
110
|
+
{ groupID, reqMsg: '', joinSource: GroupJoinSource.Search, groupName },
|
|
111
|
+
{ onSuccess: () => onPress?.(groupID) }
|
|
112
|
+
);
|
|
113
|
+
}, [isJoined, groupID, joinGroup, onPress, groupName]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<KContainer.Touchable
|
|
117
|
+
style={styles.item}
|
|
118
|
+
onPress={() => onPress?.(groupID)}
|
|
119
|
+
>
|
|
120
|
+
<Avatar size="md" {...avatarProps} />
|
|
121
|
+
<KContainer.View style={styles.itemContent}>
|
|
122
|
+
<KLabel.Text typo="TextMdMedium" numberOfLines={1}>
|
|
123
|
+
{groupName}
|
|
124
|
+
</KLabel.Text>
|
|
125
|
+
<KContainer.View style={styles.itemDescription}>
|
|
126
|
+
<KContainer.View flex>
|
|
127
|
+
<KLabel.Text
|
|
128
|
+
typo="TextNmNormal"
|
|
129
|
+
numberOfLines={1}
|
|
130
|
+
color={KColors.gray.normal}
|
|
131
|
+
>
|
|
132
|
+
{trans('group:member_count', { count: memberCount })}
|
|
133
|
+
</KLabel.Text>
|
|
134
|
+
<KLabel.Text
|
|
135
|
+
typo="TextNmNormal"
|
|
136
|
+
numberOfLines={1}
|
|
137
|
+
color={KColors.gray.normal}
|
|
138
|
+
>
|
|
139
|
+
{introduction || trans('group:public_group')}
|
|
140
|
+
</KLabel.Text>
|
|
141
|
+
</KContainer.View>
|
|
142
|
+
<KButton.Solid
|
|
143
|
+
label={trans(isJoined ? 'group:access' : 'group:join')}
|
|
144
|
+
onPress={handleActionPress}
|
|
145
|
+
size="xs"
|
|
146
|
+
isLightSolid
|
|
147
|
+
kind={isJoined ? 'light' : 'primary'}
|
|
148
|
+
loading={isPending}
|
|
149
|
+
/>
|
|
150
|
+
</KContainer.View>
|
|
151
|
+
</KContainer.View>
|
|
152
|
+
</KContainer.Touchable>
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const styles = StyleSheet.create({
|
|
157
|
+
item: {
|
|
158
|
+
paddingVertical: KSpacingValue['0.5rem'],
|
|
159
|
+
flexDirection: 'row',
|
|
160
|
+
alignItems: 'center',
|
|
161
|
+
gap: KSpacingValue['0.5rem'],
|
|
162
|
+
borderBottomWidth: 1,
|
|
163
|
+
borderBlockColor: KColors.border.light,
|
|
164
|
+
},
|
|
165
|
+
itemContent: {
|
|
166
|
+
flex: 1,
|
|
167
|
+
},
|
|
168
|
+
itemDescription: {
|
|
169
|
+
flexDirection: 'row',
|
|
170
|
+
alignItems: 'center',
|
|
171
|
+
gap: KSpacingValue['0.5rem'],
|
|
172
|
+
justifyContent: 'space-between',
|
|
173
|
+
},
|
|
174
|
+
});
|
|
@@ -4,6 +4,7 @@ import OpenIMSDK, {
|
|
|
4
4
|
OpenIMEvent,
|
|
5
5
|
SessionType,
|
|
6
6
|
type ConversationItem,
|
|
7
|
+
type GroupApplicationItem,
|
|
7
8
|
type GroupItem,
|
|
8
9
|
type GroupMemberItem,
|
|
9
10
|
type MessageItem,
|
|
@@ -24,7 +25,7 @@ import { belongsToConversation } from '../utils/message';
|
|
|
24
25
|
import { getConversationID } from '../utils/conversation';
|
|
25
26
|
import { DeviceEventEmitter } from 'react-native';
|
|
26
27
|
import { ChatEvent } from '../types/events';
|
|
27
|
-
import { conversationQueryKeys } from '../hooks/query-keys';
|
|
28
|
+
import { conversationQueryKeys, groupQueryKeys } from '../hooks/query-keys';
|
|
28
29
|
|
|
29
30
|
export const useChatListener = () => {
|
|
30
31
|
const updateOneMessage = useMessageStore((state) => state.updateOneMessage);
|
|
@@ -266,6 +267,48 @@ export const useChatListener = () => {
|
|
|
266
267
|
[currentUserId]
|
|
267
268
|
);
|
|
268
269
|
|
|
270
|
+
const markGroupJoined = useCallback(
|
|
271
|
+
(groupID: string) => {
|
|
272
|
+
queryClient.invalidateQueries({
|
|
273
|
+
queryKey: conversationQueryKeys.joinedGroup(groupID),
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
queryClient.setQueriesData<{
|
|
277
|
+
pages: {
|
|
278
|
+
total: number;
|
|
279
|
+
groups: { groupID: string; isJoined: boolean }[];
|
|
280
|
+
}[];
|
|
281
|
+
pageParams: unknown[];
|
|
282
|
+
}>({ queryKey: groupQueryKeys.public(), exact: false }, (old) => {
|
|
283
|
+
if (!old) return old;
|
|
284
|
+
return {
|
|
285
|
+
...old,
|
|
286
|
+
pages: old.pages.map((page) => ({
|
|
287
|
+
...page,
|
|
288
|
+
groups: page.groups.map((g) =>
|
|
289
|
+
g.groupID === groupID ? { ...g, isJoined: true } : g
|
|
290
|
+
),
|
|
291
|
+
})),
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
[queryClient]
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const onJoinedGroupAdded = useCallback(
|
|
299
|
+
(group: GroupItem) => {
|
|
300
|
+
markGroupJoined(group.groupID);
|
|
301
|
+
},
|
|
302
|
+
[markGroupJoined]
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const onGroupApplicationAdded = useCallback(
|
|
306
|
+
(application: GroupApplicationItem) => {
|
|
307
|
+
markGroupJoined(application.groupID);
|
|
308
|
+
},
|
|
309
|
+
[markGroupJoined]
|
|
310
|
+
);
|
|
311
|
+
|
|
269
312
|
const onMessageRevoked = useCallback((revokedInfo: RevokedInfo) => {
|
|
270
313
|
markMessageRevoked(revokedInfo.clientMsgID);
|
|
271
314
|
}, []);
|
|
@@ -327,6 +370,8 @@ export const useChatListener = () => {
|
|
|
327
370
|
);
|
|
328
371
|
OpenIMSDK.on(OpenIMEvent.OnJoinedGroupDeleted, onJoinedGroupDeleted);
|
|
329
372
|
OpenIMSDK.on(OpenIMEvent.OnGroupDismissed, onGroupDismissed);
|
|
373
|
+
OpenIMSDK.on(OpenIMEvent.OnJoinedGroupAdded, onJoinedGroupAdded);
|
|
374
|
+
OpenIMSDK.on(OpenIMEvent.OnGroupApplicationAdded, onGroupApplicationAdded);
|
|
330
375
|
|
|
331
376
|
// Message status
|
|
332
377
|
OpenIMSDK.on(OpenIMEvent.SendMessageProgress, onSendMessageProgress);
|
|
@@ -353,6 +398,11 @@ export const useChatListener = () => {
|
|
|
353
398
|
);
|
|
354
399
|
OpenIMSDK.off(OpenIMEvent.OnJoinedGroupDeleted, onJoinedGroupDeleted);
|
|
355
400
|
OpenIMSDK.off(OpenIMEvent.OnGroupDismissed, onGroupDismissed);
|
|
401
|
+
OpenIMSDK.off(OpenIMEvent.OnJoinedGroupAdded, onJoinedGroupAdded);
|
|
402
|
+
OpenIMSDK.off(
|
|
403
|
+
OpenIMEvent.OnGroupApplicationAdded,
|
|
404
|
+
onGroupApplicationAdded
|
|
405
|
+
);
|
|
356
406
|
|
|
357
407
|
// Message status
|
|
358
408
|
OpenIMSDK.off(OpenIMEvent.SendMessageProgress, onSendMessageProgress);
|
|
@@ -378,5 +428,7 @@ export const useChatListener = () => {
|
|
|
378
428
|
OnGroupMemberInfoChanged,
|
|
379
429
|
onJoinedGroupDeleted,
|
|
380
430
|
onGroupDismissed,
|
|
431
|
+
onJoinedGroupAdded,
|
|
432
|
+
onGroupApplicationAdded,
|
|
381
433
|
]);
|
|
382
434
|
};
|
|
@@ -96,10 +96,10 @@ export const useGetConversationDetail = ({
|
|
|
96
96
|
targetGroupId,
|
|
97
97
|
targetBotId,
|
|
98
98
|
}: UseGetConversationDetailParams): UseGetConversationDetailResult => {
|
|
99
|
-
const { data: botData,
|
|
99
|
+
const { data: botData, isFetching: botIdLoading } =
|
|
100
100
|
useBotConversationData(targetBotId);
|
|
101
101
|
|
|
102
|
-
const { data: conversationData,
|
|
102
|
+
const { data: conversationData, isFetching: conversationLoading } =
|
|
103
103
|
useGetOneConversation({
|
|
104
104
|
targetUserId,
|
|
105
105
|
targetGroupId: targetGroupId || botData?.groupId,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import OpenIMSDK, {
|
|
4
|
+
PublicGroupJoinStatus,
|
|
5
|
+
type SearchPublicGroupInfo,
|
|
6
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
7
|
+
import { PAGE_SIZE } from '../../constants';
|
|
8
|
+
import { groupQueryKeys } from '../query-keys';
|
|
9
|
+
|
|
10
|
+
export const useFetchPublicGroup = (keyword: string) => {
|
|
11
|
+
const { data, ...rest } = useInfiniteQuery({
|
|
12
|
+
queryKey: groupQueryKeys.publicSearch(keyword),
|
|
13
|
+
queryFn: ({ pageParam = 0 }) => {
|
|
14
|
+
return OpenIMSDK.searchPublicGroups(
|
|
15
|
+
keyword,
|
|
16
|
+
PublicGroupJoinStatus.All,
|
|
17
|
+
pageParam,
|
|
18
|
+
PAGE_SIZE
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
getNextPageParam: (lastPage, allPages) => {
|
|
22
|
+
const fetched = allPages.reduce((sum, p) => sum + p.groups.length, 0);
|
|
23
|
+
return fetched < lastPage.total ? fetched : undefined;
|
|
24
|
+
},
|
|
25
|
+
initialPageParam: 0,
|
|
26
|
+
maxPages: 1,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const dataFlatten: SearchPublicGroupInfo[] = useMemo(() => {
|
|
30
|
+
const seen = new Set<string>();
|
|
31
|
+
return (
|
|
32
|
+
data?.pages.flatMap((page) =>
|
|
33
|
+
page.groups.filter((g) => {
|
|
34
|
+
if (seen.has(g.groupID)) return false;
|
|
35
|
+
seen.add(g.groupID);
|
|
36
|
+
return true;
|
|
37
|
+
})
|
|
38
|
+
) ?? []
|
|
39
|
+
);
|
|
40
|
+
}, [data]);
|
|
41
|
+
|
|
42
|
+
return { dataFlatten, ...rest };
|
|
43
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import OpenIMSDK, { type JoinGroupParams } from '@droppii/openim-rn-client-sdk';
|
|
2
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { useMutation } from '@tanstack/react-query';
|
|
4
|
+
import { conversationQueryKeys, groupQueryKeys } from '../query-keys';
|
|
5
|
+
import { UIUtils } from '../../utils/UIUtils';
|
|
6
|
+
import { trans } from '../../translation';
|
|
7
|
+
|
|
8
|
+
export const useJoinGroup = () => {
|
|
9
|
+
const queryClient = useQueryClient();
|
|
10
|
+
|
|
11
|
+
return useMutation({
|
|
12
|
+
mutationKey: groupQueryKeys.join(),
|
|
13
|
+
mutationFn: (params: JoinGroupParams & { groupName: string }) =>
|
|
14
|
+
OpenIMSDK.joinGroup(params),
|
|
15
|
+
onSuccess: (_data, params) => {
|
|
16
|
+
queryClient.invalidateQueries({
|
|
17
|
+
queryKey: conversationQueryKeys.joinedGroup(params.groupID),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
queryClient.setQueriesData<{
|
|
21
|
+
pages: {
|
|
22
|
+
total: number;
|
|
23
|
+
groups: { groupID: string; isJoined: boolean }[];
|
|
24
|
+
}[];
|
|
25
|
+
pageParams: unknown[];
|
|
26
|
+
}>({ queryKey: groupQueryKeys.public(), exact: false }, (old) => {
|
|
27
|
+
if (!old) return old;
|
|
28
|
+
return {
|
|
29
|
+
...old,
|
|
30
|
+
pages: old.pages.map((page) => ({
|
|
31
|
+
...page,
|
|
32
|
+
groups: page.groups.map((g) =>
|
|
33
|
+
g.groupID === params.groupID ? { ...g, isJoined: true } : g
|
|
34
|
+
),
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
UIUtils.toast.open({
|
|
40
|
+
title: trans('group:joined_group', {
|
|
41
|
+
groupName: params?.groupName || '',
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
};
|
package/src/hooks/query-keys.ts
CHANGED
|
@@ -61,3 +61,11 @@ export const messageQueryKeys = {
|
|
|
61
61
|
search: (query: string, conversationId?: string) =>
|
|
62
62
|
[...messageQueryKeys.all, 'search', query, conversationId] as const,
|
|
63
63
|
};
|
|
64
|
+
|
|
65
|
+
export const groupQueryKeys = {
|
|
66
|
+
all: ['groups'] as const,
|
|
67
|
+
public: () => [...groupQueryKeys.all, 'public'] as const,
|
|
68
|
+
publicSearch: (keyword: string) =>
|
|
69
|
+
[...groupQueryKeys.public(), keyword] as const,
|
|
70
|
+
join: () => [...groupQueryKeys.all, 'join'] as const,
|
|
71
|
+
};
|
package/src/index.tsx
CHANGED
|
@@ -40,7 +40,11 @@ export type {
|
|
|
40
40
|
MessageItem,
|
|
41
41
|
ConversationItem,
|
|
42
42
|
} from '@droppii/openim-rn-client-sdk';
|
|
43
|
-
export {
|
|
43
|
+
export {
|
|
44
|
+
LoginStatus,
|
|
45
|
+
MessageType,
|
|
46
|
+
GroupJoinSource,
|
|
47
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
44
48
|
export * from './screens/chat-detail';
|
|
45
49
|
export type {
|
|
46
50
|
DChatActionItem,
|
|
@@ -122,7 +122,7 @@ const styles = StyleSheet.create({
|
|
|
122
122
|
tabRow: {
|
|
123
123
|
flexDirection: 'row',
|
|
124
124
|
borderBottomWidth: 1,
|
|
125
|
-
borderBottomColor: KColors.border.
|
|
125
|
+
borderBottomColor: KColors.border.light,
|
|
126
126
|
},
|
|
127
127
|
tabItem: {
|
|
128
128
|
paddingVertical: KSpacingValue['0.5rem'],
|
|
@@ -64,7 +64,11 @@ import { useSendMessage } from '../../hooks/message/useSendMessage';
|
|
|
64
64
|
import { openBotMenuSheet } from './BotMenuSheet';
|
|
65
65
|
import { UIUtils } from '../../utils/UIUtils';
|
|
66
66
|
import { GroupPermission, PeerType } from '@droppii/openim-rn-client-sdk';
|
|
67
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
useGuardedAction,
|
|
69
|
+
useHasGroupPermission,
|
|
70
|
+
} from '../../hooks/useGroupPermission';
|
|
71
|
+
import { useIsJoinedGroup } from '../../hooks/useIsJoinedGroup';
|
|
68
72
|
|
|
69
73
|
interface ChatComposerInternalProps {
|
|
70
74
|
isEmptyMessage?: boolean;
|
|
@@ -72,6 +76,8 @@ interface ChatComposerInternalProps {
|
|
|
72
76
|
isComposeDisabled?: boolean;
|
|
73
77
|
composerRef?: RefObject<View | null>;
|
|
74
78
|
onComposerLayout?: (event: LayoutChangeEvent) => void;
|
|
79
|
+
onPressJoinGroup?: () => void;
|
|
80
|
+
isJoiningGroup?: boolean;
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
export const ChatComposer = memo(
|
|
@@ -81,6 +87,8 @@ export const ChatComposer = memo(
|
|
|
81
87
|
isComposeDisabled = false,
|
|
82
88
|
composerRef,
|
|
83
89
|
onComposerLayout,
|
|
90
|
+
onPressJoinGroup,
|
|
91
|
+
isJoiningGroup = false,
|
|
84
92
|
}: ChatComposerInternalProps) => {
|
|
85
93
|
const insets = useSafeAreaInsets();
|
|
86
94
|
const isDisableCompose = isComposeDisabled;
|
|
@@ -99,6 +107,15 @@ export const ChatComposer = memo(
|
|
|
99
107
|
const { mutateAsync: closeSupportSession } = useCloseSupportSession();
|
|
100
108
|
const { sendMessage } = useSendMessage();
|
|
101
109
|
const compose = useChatCompose();
|
|
110
|
+
const isSendMessageAllowed = useHasGroupPermission(
|
|
111
|
+
GroupPermission.SendMessage
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const { data: isJoinedGroup } = useIsJoinedGroup(conversation?.groupID);
|
|
115
|
+
|
|
116
|
+
const isNotInGroup = useMemo(() => {
|
|
117
|
+
return conversation?.groupID ? !isJoinedGroup : false;
|
|
118
|
+
}, [conversation?.groupID, isJoinedGroup]);
|
|
102
119
|
|
|
103
120
|
const {
|
|
104
121
|
activePanelType,
|
|
@@ -403,7 +420,7 @@ export const ChatComposer = memo(
|
|
|
403
420
|
conversationFlow?.flowCurrentStep?.type === ReactFlowsNodeTypes.Start &&
|
|
404
421
|
isEmptyMessage;
|
|
405
422
|
|
|
406
|
-
if (isDisableCompose && !isGroupConversation) {
|
|
423
|
+
if ((isDisableCompose && !isGroupConversation) || !isSendMessageAllowed) {
|
|
407
424
|
content = (
|
|
408
425
|
<KContainer.View row alignItems justifyContent>
|
|
409
426
|
<KContainer.View marginR="0.25rem">
|
|
@@ -414,7 +431,9 @@ export const ChatComposer = memo(
|
|
|
414
431
|
/>
|
|
415
432
|
</KContainer.View>
|
|
416
433
|
<KLabel.Text typo="TextXsNormal">
|
|
417
|
-
{
|
|
434
|
+
{!isSendMessageAllowed
|
|
435
|
+
? trans('group:cannot_send_message')
|
|
436
|
+
: trans('chat:compose_disabled_seller_inactive')}
|
|
418
437
|
</KLabel.Text>
|
|
419
438
|
</KContainer.View>
|
|
420
439
|
);
|
|
@@ -431,6 +450,30 @@ export const ChatComposer = memo(
|
|
|
431
450
|
/>
|
|
432
451
|
</KContainer.View>
|
|
433
452
|
);
|
|
453
|
+
} else if (isNotInGroup) {
|
|
454
|
+
content = (
|
|
455
|
+
<KContainer.View
|
|
456
|
+
paddingH="0.75rem"
|
|
457
|
+
justifyContent="center"
|
|
458
|
+
brTW={1}
|
|
459
|
+
brTC={KColors.border.light}
|
|
460
|
+
>
|
|
461
|
+
<KLabel.Text
|
|
462
|
+
typo="TextSmNormal"
|
|
463
|
+
textAlign="center"
|
|
464
|
+
paddingV={'0.75rem'}
|
|
465
|
+
>
|
|
466
|
+
{trans('group:not_in_group')}
|
|
467
|
+
</KLabel.Text>
|
|
468
|
+
<KButton.Solid
|
|
469
|
+
size="md"
|
|
470
|
+
label={trans('group:join')}
|
|
471
|
+
stretch
|
|
472
|
+
loading={isJoiningGroup}
|
|
473
|
+
onPress={onPressJoinGroup}
|
|
474
|
+
/>
|
|
475
|
+
</KContainer.View>
|
|
476
|
+
);
|
|
434
477
|
} else {
|
|
435
478
|
content = (
|
|
436
479
|
<KContainer.View background={'#F9F9FA'}>
|
|
@@ -30,10 +30,14 @@ import {
|
|
|
30
30
|
useGetConversationDetail,
|
|
31
31
|
useGetConversationTarget,
|
|
32
32
|
} from '../../hooks/conversation/useGetConversationDetail';
|
|
33
|
-
import
|
|
33
|
+
import {
|
|
34
|
+
GroupJoinSource,
|
|
35
|
+
type MessageItem,
|
|
36
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
34
37
|
import { useMessages } from '../../hooks/message/useMessages';
|
|
35
38
|
import { ChatEvent, type GroupRemovedPayload } from '../../types/events';
|
|
36
39
|
import { useSetIsShowFLashMessage } from '../../context/global';
|
|
40
|
+
import { useJoinGroup } from '../../hooks/groups/useJoinGroup';
|
|
37
41
|
|
|
38
42
|
const ChatDetail = memo(
|
|
39
43
|
({
|
|
@@ -50,6 +54,7 @@ const ChatDetail = memo(
|
|
|
50
54
|
onPressUrl,
|
|
51
55
|
customMessageItemFactory,
|
|
52
56
|
pannelActions,
|
|
57
|
+
groupJoinSource,
|
|
53
58
|
}: ChatDetailProps) => {
|
|
54
59
|
const navigation = useNavigation();
|
|
55
60
|
const mediaViewerRef = useRef<MediaViewerModalHandle>(null);
|
|
@@ -94,6 +99,8 @@ const ChatDetail = memo(
|
|
|
94
99
|
hasMoreNew,
|
|
95
100
|
} = useMessages({ conversationId, startClientMsgId: targetMessageId });
|
|
96
101
|
|
|
102
|
+
const { mutate: joinGroup, isPending: isJoiningGroup } = useJoinGroup();
|
|
103
|
+
|
|
97
104
|
const setShowFLashMessage = useSetIsShowFLashMessage();
|
|
98
105
|
|
|
99
106
|
const { pinnedMessages, pinMessage, unpinMessage } =
|
|
@@ -164,6 +171,21 @@ const ChatDetail = memo(
|
|
|
164
171
|
onPressMenu?.(conversationId);
|
|
165
172
|
}, [conversationId, onPressMenu]);
|
|
166
173
|
|
|
174
|
+
const handlePressJoinGroup = useCallback(() => {
|
|
175
|
+
if (!conversation?.groupID) return;
|
|
176
|
+
joinGroup({
|
|
177
|
+
groupID: conversation?.groupID,
|
|
178
|
+
reqMsg: '',
|
|
179
|
+
joinSource: groupJoinSource || GroupJoinSource.Search,
|
|
180
|
+
groupName: conversation?.showName || '',
|
|
181
|
+
});
|
|
182
|
+
}, [
|
|
183
|
+
groupJoinSource,
|
|
184
|
+
conversation?.groupID,
|
|
185
|
+
joinGroup,
|
|
186
|
+
conversation?.showName,
|
|
187
|
+
]);
|
|
188
|
+
|
|
167
189
|
return (
|
|
168
190
|
<KContainer.Page flex edges={['bottom']}>
|
|
169
191
|
<ChatDetailHeader
|
|
@@ -207,6 +229,8 @@ const ChatDetail = memo(
|
|
|
207
229
|
isEmptyMessage={messages?.length === 0}
|
|
208
230
|
pannelActions={pannelActions}
|
|
209
231
|
isComposeDisabled={isComposeDisabled}
|
|
232
|
+
onPressJoinGroup={handlePressJoinGroup}
|
|
233
|
+
isJoiningGroup={isJoiningGroup}
|
|
210
234
|
/>
|
|
211
235
|
|
|
212
236
|
<MediaViewerModal ref={mediaViewerRef} />
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type RefObject,
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
|
+
ActivityIndicator,
|
|
11
12
|
DeviceEventEmitter,
|
|
12
13
|
Platform,
|
|
13
14
|
StyleSheet,
|
|
@@ -89,6 +90,7 @@ export const ChatListLegend = memo(
|
|
|
89
90
|
freeze,
|
|
90
91
|
scrollMessageToEnd,
|
|
91
92
|
keyboardOffset,
|
|
93
|
+
isLoading,
|
|
92
94
|
}: ChatListLegendInternalProps) => {
|
|
93
95
|
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
94
96
|
const windowWidth = useWindowDimensions().width;
|
|
@@ -492,16 +494,13 @@ export const ChatListLegend = memo(
|
|
|
492
494
|
listRef,
|
|
493
495
|
]);
|
|
494
496
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
// </KContainer.View>
|
|
503
|
-
// );
|
|
504
|
-
// }
|
|
497
|
+
if (isLoading) {
|
|
498
|
+
return (
|
|
499
|
+
<KContainer.View flex center background={KColors.white}>
|
|
500
|
+
<ActivityIndicator color={KColors.primary.normal} size="small" />
|
|
501
|
+
</KContainer.View>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
505
504
|
|
|
506
505
|
return (
|
|
507
506
|
<KContainer.View flex background={KColors.white} style={styles.container}>
|
|
@@ -509,7 +508,7 @@ export const ChatListLegend = memo(
|
|
|
509
508
|
ref={listRef}
|
|
510
509
|
data={messages}
|
|
511
510
|
renderItem={renderItem}
|
|
512
|
-
estimatedItemSize={
|
|
511
|
+
estimatedItemSize={200}
|
|
513
512
|
getItemType={getItemType}
|
|
514
513
|
getFixedItemSize={getFixedItemSize}
|
|
515
514
|
keyExtractor={(item) => String(item.clientMsgID || item.serverMsgID)}
|
|
@@ -517,7 +516,6 @@ export const ChatListLegend = memo(
|
|
|
517
516
|
extraData={`${conversationFlow?.flowCurrentStep?.id ?? ''}-${lastMessageId ?? ''}-${highlightedId ?? ''}`}
|
|
518
517
|
alignItemsAtEnd
|
|
519
518
|
maintainVisibleContentPosition={maintainVisiblePositionConfig}
|
|
520
|
-
maintainScrollAtEnd={true}
|
|
521
519
|
freeze={freeze}
|
|
522
520
|
keyboardOffset={keyboardOffset}
|
|
523
521
|
scrollEventThrottle={16}
|
|
@@ -532,7 +530,7 @@ export const ChatListLegend = memo(
|
|
|
532
530
|
onEndReached={handleEndReached}
|
|
533
531
|
onEndReachedThreshold={0.2}
|
|
534
532
|
showsVerticalScrollIndicator={false}
|
|
535
|
-
|
|
533
|
+
initialScrollIndex={messages.length - 1}
|
|
536
534
|
/>
|
|
537
535
|
<Animated.View
|
|
538
536
|
style={[styles.scrollToBottomButton, animatedButtonStyle]}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { MediaItem } from '../../hooks/useMediaViewer';
|
|
3
3
|
import type { MeasuredRect } from './messageToolbox/types';
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
GroupJoinSource,
|
|
6
|
+
MessageItem,
|
|
7
|
+
} from '@droppii/openim-rn-client-sdk';
|
|
5
8
|
|
|
6
9
|
export type DChatActionIconProvider = 'MaterialCommunityIcons' | 'DroppiiNew';
|
|
7
10
|
|
|
@@ -100,4 +103,5 @@ export interface ChatDetailProps extends Omit<
|
|
|
100
103
|
onPressUrl?: (url: string) => void;
|
|
101
104
|
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
102
105
|
pannelActions?: DChatPannelAction[];
|
|
106
|
+
groupJoinSource?: GroupJoinSource;
|
|
103
107
|
}
|