@droppii-org/chat-mobile 0.2.2 → 0.2.3
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/assets/images/icon_droppii.png +0 -0
- package/lib/module/assets/images/index.js +2 -1
- package/lib/module/assets/images/index.js.map +1 -1
- package/lib/module/components/Avatar/SingleAvatar.js +26 -6
- package/lib/module/components/Avatar/SingleAvatar.js.map +1 -1
- package/lib/module/components/ThreadCard/NamePrefixIcon.js +18 -1
- 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/context/ChatContext.js +31 -0
- package/lib/module/context/ChatContext.js.map +1 -0
- package/lib/module/context/index.js +4 -0
- package/lib/module/context/index.js.map +1 -0
- package/lib/module/core/useChatListener.js +0 -5
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/hooks/useChatMessages.js +4 -8
- package/lib/module/hooks/useChatMessages.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/inbox/MessagesTab.js +13 -0
- package/lib/module/screens/inbox/MessagesTab.js.map +1 -1
- package/lib/module/services/apis.js +2 -0
- package/lib/module/services/apis.js.map +1 -1
- package/lib/module/services/message.js +4 -2
- package/lib/module/services/message.js.map +1 -1
- package/lib/module/store/conversation.js +17 -20
- package/lib/module/store/conversation.js.map +1 -1
- package/lib/module/types/chat.js +5 -0
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/utils/conversation.js +6 -17
- package/lib/module/utils/conversation.js.map +1 -1
- package/lib/module/utils/message.js +34 -12
- package/lib/module/utils/message.js.map +1 -1
- package/lib/typescript/src/assets/images/index.d.ts +1 -0
- package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts +7 -6
- package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts.map +1 -1
- package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -1
- package/lib/typescript/src/context/ChatContext.d.ts +5 -0
- package/lib/typescript/src/context/ChatContext.d.ts.map +1 -0
- package/lib/typescript/src/context/index.d.ts +2 -0
- package/lib/typescript/src/context/index.d.ts.map +1 -0
- package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useChatMessages.d.ts +1 -3
- package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/MessagesTab.d.ts.map +1 -1
- package/lib/typescript/src/services/apis.d.ts.map +1 -1
- package/lib/typescript/src/services/message.d.ts +0 -2
- package/lib/typescript/src/services/message.d.ts.map +1 -1
- package/lib/typescript/src/store/conversation.d.ts +1 -0
- package/lib/typescript/src/store/conversation.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +14 -2
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/utils/conversation.d.ts +5 -4
- package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
- package/lib/typescript/src/utils/message.d.ts +6 -2
- package/lib/typescript/src/utils/message.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/assets/images/icon_droppii.png +0 -0
- package/src/assets/images/index.ts +1 -0
- package/src/build-ignore.d.ts +3 -0
- package/src/components/Avatar/SingleAvatar.tsx +40 -17
- package/src/components/ThreadCard/NamePrefixIcon.tsx +17 -4
- package/src/components/ThreadCard/ThreadCard.tsx +1 -1
- package/src/context/ChatContext.tsx +23 -0
- package/src/context/index.ts +1 -0
- package/src/core/useChatListener.ts +0 -11
- package/src/hooks/useChatMessages.ts +26 -38
- package/src/index.tsx +1 -0
- package/src/screens/inbox/MessagesTab.tsx +11 -1
- package/src/services/apis.ts +2 -0
- package/src/services/message.ts +8 -7
- package/src/store/conversation.ts +25 -21
- package/src/types/chat.ts +20 -2
- package/src/utils/conversation.ts +6 -29
- package/src/utils/message.ts +36 -30
|
@@ -15,16 +15,12 @@ import {
|
|
|
15
15
|
|
|
16
16
|
type UseChatMessagesOptions = {
|
|
17
17
|
conversationId: string;
|
|
18
|
-
recvID?: string;
|
|
19
|
-
groupID?: string;
|
|
20
18
|
enabled?: boolean;
|
|
21
19
|
pageSize?: number;
|
|
22
20
|
};
|
|
23
21
|
|
|
24
22
|
export function useChatMessages({
|
|
25
23
|
conversationId,
|
|
26
|
-
recvID = '',
|
|
27
|
-
groupID = '',
|
|
28
24
|
enabled = true,
|
|
29
25
|
pageSize = 20,
|
|
30
26
|
}: UseChatMessagesOptions) {
|
|
@@ -158,44 +154,36 @@ export function useChatMessages({
|
|
|
158
154
|
}
|
|
159
155
|
}, [conversationId, pageSize]);
|
|
160
156
|
|
|
161
|
-
const sendTextMessage = useCallback(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
157
|
+
const sendTextMessage = useCallback(async (text: string) => {
|
|
158
|
+
const trimmed = text.trim();
|
|
159
|
+
if (!trimmed) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
167
162
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
groupID,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
setMessages((current) => mergeMessages(current, [sentMessage]));
|
|
175
|
-
},
|
|
176
|
-
[groupID, recvID]
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const appendIncomingMessages = useCallback(
|
|
180
|
-
(incoming: MessageItem[]) => {
|
|
181
|
-
const relevant = incoming.filter((message) =>
|
|
182
|
-
belongsToConversation(message, conversationIdRef.current, groupID)
|
|
183
|
-
);
|
|
163
|
+
const sentMessage = await ChatMessageAPI.sendTextMessage({
|
|
164
|
+
text: trimmed,
|
|
165
|
+
});
|
|
184
166
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
167
|
+
setMessages((current) => mergeMessages(current, [sentMessage]));
|
|
168
|
+
}, []);
|
|
188
169
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
170
|
+
const appendIncomingMessages = useCallback((incoming: MessageItem[]) => {
|
|
171
|
+
const relevant = incoming.filter((message) =>
|
|
172
|
+
belongsToConversation(message, conversationIdRef.current)
|
|
173
|
+
);
|
|
192
174
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
175
|
+
if (!relevant.length) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
setMessages((current) =>
|
|
180
|
+
mergeMessages(current, relevant as DMessageItem[])
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
ChatMessageAPI.markConversationAsRead(conversationIdRef.current).catch(
|
|
184
|
+
() => undefined
|
|
185
|
+
);
|
|
186
|
+
}, []);
|
|
199
187
|
|
|
200
188
|
useEffect(() => {
|
|
201
189
|
resetState();
|
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memo, useCallback } from 'react';
|
|
2
|
-
import { GestureResponderEvent } from 'react-native';
|
|
2
|
+
import { GestureResponderEvent, ActivityIndicator } from 'react-native';
|
|
3
3
|
import { KContainer, KDivider, KLabel, KColors } from '@droppii/libs';
|
|
4
4
|
import { useConversationList } from '../../hooks/useConversationList';
|
|
5
5
|
import { ThreadCard } from '../../components/ThreadCard';
|
|
@@ -36,6 +36,12 @@ export const MessagesTab = memo(
|
|
|
36
36
|
[handleThreadPress]
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
+
const LoadingComponent = (
|
|
40
|
+
<KContainer.View center flex paddingV="3rem">
|
|
41
|
+
<ActivityIndicator size="large" color={KColors.palette.primary.w400} />
|
|
42
|
+
</KContainer.View>
|
|
43
|
+
);
|
|
44
|
+
|
|
39
45
|
const EmptyComponent = isLoading ? null : (
|
|
40
46
|
<KContainer.View center flex paddingV="3rem">
|
|
41
47
|
<KLabel.Text typo="TextSmNormal" color={KColors.palette.gray.w400}>
|
|
@@ -44,6 +50,10 @@ export const MessagesTab = memo(
|
|
|
44
50
|
</KContainer.View>
|
|
45
51
|
);
|
|
46
52
|
|
|
53
|
+
if (isLoading && data.length === 0) {
|
|
54
|
+
return LoadingComponent;
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
return (
|
|
48
58
|
<KContainer.FlatList
|
|
49
59
|
data={data}
|
package/src/services/apis.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
import OpenIMSDK, { LoginStatus } from '@droppii/openim-rn-client-sdk';
|
|
7
7
|
import type { BaseResponse, GetOpenIMTokenResponse } from '../types/auth';
|
|
8
8
|
import { Platform } from 'react-native';
|
|
9
|
+
import { useConversationStore } from '../store';
|
|
9
10
|
|
|
10
11
|
let apiInstance: AxiosInstance | null = null;
|
|
11
12
|
|
|
@@ -38,6 +39,7 @@ export namespace ChatAPI {
|
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
export const logout = async (operationID?: string) => {
|
|
42
|
+
useConversationStore.getState().reset();
|
|
41
43
|
return OpenIMSDK.logout(operationID);
|
|
42
44
|
};
|
|
43
45
|
|
package/src/services/message.ts
CHANGED
|
@@ -4,6 +4,8 @@ import OpenIMSDK, {
|
|
|
4
4
|
type MessageItem,
|
|
5
5
|
} from '@droppii/openim-rn-client-sdk';
|
|
6
6
|
import type { DMessageItem } from '../types/chat';
|
|
7
|
+
import { useConversationStore } from '../store';
|
|
8
|
+
import { getReceiverId } from '../utils/message';
|
|
7
9
|
|
|
8
10
|
const DEFAULT_PAGE_SIZE = 20;
|
|
9
11
|
|
|
@@ -28,21 +30,20 @@ export namespace ChatMessageAPI {
|
|
|
28
30
|
// ...(options?.startClientMsgID
|
|
29
31
|
// ? { lastMinSeq: options.lastMinSeq ?? 0 }
|
|
30
32
|
// : {}),
|
|
31
|
-
} as
|
|
32
|
-
typeof OpenIMSDK.getAdvancedHistoryMessageList
|
|
33
|
-
>[0]) as Promise<HistoryMessageResult>;
|
|
33
|
+
}) as Promise<HistoryMessageResult>;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export async function sendTextMessage(params: {
|
|
37
37
|
text: string;
|
|
38
|
-
recvID?: string;
|
|
39
|
-
groupID?: string;
|
|
40
38
|
}): Promise<DMessageItem> {
|
|
39
|
+
const currentConversation = useConversationStore
|
|
40
|
+
.getState()
|
|
41
|
+
.getCurrentConversation();
|
|
42
|
+
|
|
41
43
|
const message = await OpenIMSDK.createTextMessage(params.text);
|
|
42
44
|
|
|
43
45
|
const sentMessage = await OpenIMSDK.sendMessage({
|
|
44
|
-
|
|
45
|
-
groupID: params.groupID ?? '',
|
|
46
|
+
...getReceiverId(currentConversation),
|
|
46
47
|
message,
|
|
47
48
|
});
|
|
48
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
sortConversation,
|
|
4
4
|
mergeOpenIMIntoConversation,
|
|
5
5
|
} from '../utils/conversation';
|
|
6
6
|
import type { DConversationItem } from '../types/chat';
|
|
@@ -27,6 +27,7 @@ export interface ConversationStore {
|
|
|
27
27
|
mergeOpenIMConversations(conversations: ConversationItem[]): boolean;
|
|
28
28
|
removeConversation(id: ConversationID): void;
|
|
29
29
|
updateLastMessage(id: ConversationID, message: MessageItem): void;
|
|
30
|
+
reset(): void;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export const useConversationStore = create<ConversationStore>()((set, get) => ({
|
|
@@ -43,20 +44,17 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
|
|
|
43
44
|
return get().map[id];
|
|
44
45
|
},
|
|
45
46
|
updateConversations(conversations) {
|
|
46
|
-
|
|
47
|
-
(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
set(
|
|
48
|
+
sortConversation(
|
|
49
|
+
conversations.reduce(
|
|
50
|
+
(pre, curr) => {
|
|
51
|
+
pre[curr.conversationId] = curr;
|
|
52
|
+
return pre;
|
|
53
|
+
},
|
|
54
|
+
{ ...get().map }
|
|
55
|
+
)
|
|
56
|
+
)
|
|
52
57
|
);
|
|
53
|
-
const newList = Object.keys(newMap).sort((a, b) =>
|
|
54
|
-
dConversationCompare(newMap[a]!, newMap[b]!)
|
|
55
|
-
);
|
|
56
|
-
set({
|
|
57
|
-
map: newMap,
|
|
58
|
-
list: newList,
|
|
59
|
-
});
|
|
60
58
|
},
|
|
61
59
|
mergeOpenIMConversations(conversations) {
|
|
62
60
|
const currentMap = get().map;
|
|
@@ -78,10 +76,7 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
|
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
if (changed) {
|
|
81
|
-
|
|
82
|
-
dConversationCompare(nextMap[a]!, nextMap[b]!)
|
|
83
|
-
);
|
|
84
|
-
set({ map: nextMap, list: newList });
|
|
79
|
+
set(sortConversation(nextMap));
|
|
85
80
|
}
|
|
86
81
|
|
|
87
82
|
if (hasUnknown) {
|
|
@@ -102,11 +97,20 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
|
|
|
102
97
|
updateLastMessage(id, message) {
|
|
103
98
|
const conversation = get().map[id];
|
|
104
99
|
if (conversation == null) return;
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
|
|
101
|
+
set(
|
|
102
|
+
sortConversation({
|
|
107
103
|
[id]: { ...conversation, lastMessage: message },
|
|
108
104
|
...get().map,
|
|
109
|
-
}
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
reset() {
|
|
109
|
+
set({
|
|
110
|
+
currentConversationId: undefined,
|
|
111
|
+
list: [],
|
|
112
|
+
map: {},
|
|
113
|
+
newConversationSignal: 0,
|
|
110
114
|
});
|
|
111
115
|
},
|
|
112
116
|
}));
|
package/src/types/chat.ts
CHANGED
|
@@ -4,11 +4,29 @@ import {
|
|
|
4
4
|
type MessageReceiveOptType,
|
|
5
5
|
type GroupAtType,
|
|
6
6
|
} from '@droppii/openim-rn-client-sdk';
|
|
7
|
+
import type { PropsWithChildren } from 'react';
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
-
|
|
9
|
+
export const EventProvider = {
|
|
10
|
+
ga: 'ga',
|
|
11
|
+
appsflyer: 'appsflyer',
|
|
12
|
+
netcore: 'netcore',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
type LogGA = (
|
|
16
|
+
event: string,
|
|
17
|
+
params?: any,
|
|
18
|
+
provider?: `${keyof typeof EventProvider}`
|
|
19
|
+
) => void;
|
|
20
|
+
|
|
21
|
+
export interface ChatContextType {
|
|
22
|
+
logGA?: LogGA;
|
|
10
23
|
}
|
|
11
24
|
|
|
25
|
+
export type ChatProviderProps = PropsWithChildren<{
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
logGA?: LogGA;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
12
30
|
export enum DChatApplicationType {
|
|
13
31
|
BIZ = 'BIZ',
|
|
14
32
|
MALL = 'MALL',
|
|
@@ -2,11 +2,7 @@ import {
|
|
|
2
2
|
SessionType,
|
|
3
3
|
type ConversationItem,
|
|
4
4
|
} from '@droppii/openim-rn-client-sdk';
|
|
5
|
-
import {
|
|
6
|
-
type DConversationItem,
|
|
7
|
-
type DMessageItem,
|
|
8
|
-
DChatCategory,
|
|
9
|
-
} from '../types/chat';
|
|
5
|
+
import { type DConversationItem, type DMessageItem } from '../types/chat';
|
|
10
6
|
|
|
11
7
|
export const conversationCompare = (
|
|
12
8
|
a: ConversationItem,
|
|
@@ -79,28 +75,9 @@ export const getConversationID = (
|
|
|
79
75
|
return [prefix, senderID, receiverID].filter(Boolean).join('_');
|
|
80
76
|
};
|
|
81
77
|
|
|
82
|
-
export const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
item: DConversationItem,
|
|
88
|
-
t: (key: string) => string
|
|
89
|
-
) => {
|
|
90
|
-
if (isBotCrmChat(item.chatCategory)) {
|
|
91
|
-
const botName = item.peer.botName?.trim();
|
|
92
|
-
if (botName) return botName;
|
|
93
|
-
return t('thread_card_bot_crm_name');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const fullName = item.peer.fullName?.trim();
|
|
97
|
-
if (fullName) return fullName;
|
|
98
|
-
const username = item.peer.username?.trim();
|
|
99
|
-
if (username) return username;
|
|
100
|
-
return t('thread_card_fallback_name');
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export const resolveAvatarUrl = (item: DConversationItem) => {
|
|
104
|
-
if (isBotCrmChat(item.chatCategory)) return null;
|
|
105
|
-
return item.peer.avatar;
|
|
78
|
+
export const sortConversation = (map: Record<string, DConversationItem>) => {
|
|
79
|
+
const list = Object.keys(map).sort((a, b) =>
|
|
80
|
+
dConversationCompare(map[a]!, map[b]!)
|
|
81
|
+
);
|
|
82
|
+
return { map, list };
|
|
106
83
|
};
|
package/src/utils/message.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { SessionType, type MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
DChatType,
|
|
4
|
+
type DConversationItem,
|
|
5
|
+
type DMessageItem,
|
|
6
|
+
} from '../types/chat';
|
|
3
7
|
import { getConversationID } from './conversation';
|
|
4
8
|
|
|
5
9
|
const getMessageTime = (message: DMessageItem) =>
|
|
@@ -96,41 +100,43 @@ export const hasNewHistoryMessages = (
|
|
|
96
100
|
|
|
97
101
|
export const belongsToConversation = (
|
|
98
102
|
message: MessageItem,
|
|
99
|
-
conversationId: string
|
|
100
|
-
groupID?: string
|
|
103
|
+
conversationId: string
|
|
101
104
|
): boolean => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
switch (message.sessionType) {
|
|
106
|
+
case SessionType.Single: {
|
|
107
|
+
const forwardId = getConversationID(
|
|
108
|
+
message.sessionType,
|
|
109
|
+
message.sendID,
|
|
110
|
+
message.recvID
|
|
111
|
+
);
|
|
112
|
+
const reverseId = getConversationID(
|
|
113
|
+
message.sessionType,
|
|
114
|
+
message.recvID,
|
|
115
|
+
message.sendID
|
|
116
|
+
);
|
|
117
|
+
return conversationId === forwardId || conversationId === reverseId;
|
|
105
118
|
}
|
|
106
|
-
).conversationID;
|
|
107
119
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
case SessionType.Group: {
|
|
121
|
+
return conversationId === `sg_${message.groupID}`;
|
|
122
|
+
}
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
message.groupID === groupID ||
|
|
115
|
-
conversationId === `sg_${groupID}` ||
|
|
116
|
-
conversationId.endsWith(`_${groupID}`)
|
|
117
|
-
);
|
|
124
|
+
default:
|
|
125
|
+
return false;
|
|
118
126
|
}
|
|
127
|
+
};
|
|
119
128
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
129
|
+
export const getReceiverId = (conversation?: DConversationItem) => {
|
|
130
|
+
if (conversation == null) return { groupID: '', recvID: '' };
|
|
123
131
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
message.sendID
|
|
133
|
-
);
|
|
132
|
+
const isGroupChat = conversation.chatType === DChatType.GROUP;
|
|
133
|
+
if (isGroupChat) {
|
|
134
|
+
const isBot = conversation.chatCategory?.includes('BOT');
|
|
135
|
+
const groupID = isBot
|
|
136
|
+
? conversation.conversationId?.slice(3) // Remove sg_
|
|
137
|
+
: conversation.groupID;
|
|
138
|
+
return { groupID: groupID ?? '', recvID: '' };
|
|
139
|
+
}
|
|
134
140
|
|
|
135
|
-
return
|
|
141
|
+
return { groupID: '', recvID: conversation.peer.id ?? '' };
|
|
136
142
|
};
|