@droppii-org/chat-mobile 0.2.1 → 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.
Files changed (78) hide show
  1. package/lib/module/assets/images/icon_droppii.png +0 -0
  2. package/lib/module/assets/images/index.js +2 -1
  3. package/lib/module/assets/images/index.js.map +1 -1
  4. package/lib/module/components/Avatar/SingleAvatar.js +26 -6
  5. package/lib/module/components/Avatar/SingleAvatar.js.map +1 -1
  6. package/lib/module/components/ThreadCard/NamePrefixIcon.js +18 -1
  7. package/lib/module/components/ThreadCard/NamePrefixIcon.js.map +1 -1
  8. package/lib/module/components/ThreadCard/ThreadCard.js +3 -1
  9. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  10. package/lib/module/context/ChatContext.js +31 -0
  11. package/lib/module/context/ChatContext.js.map +1 -0
  12. package/lib/module/context/index.js +4 -0
  13. package/lib/module/context/index.js.map +1 -0
  14. package/lib/module/core/useChatListener.js +0 -5
  15. package/lib/module/core/useChatListener.js.map +1 -1
  16. package/lib/module/hooks/useChatMessages.js +4 -8
  17. package/lib/module/hooks/useChatMessages.js.map +1 -1
  18. package/lib/module/index.js +1 -0
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/screens/inbox/MessagesTab.js +13 -0
  21. package/lib/module/screens/inbox/MessagesTab.js.map +1 -1
  22. package/lib/module/services/apis.js +2 -0
  23. package/lib/module/services/apis.js.map +1 -1
  24. package/lib/module/services/message.js +4 -2
  25. package/lib/module/services/message.js.map +1 -1
  26. package/lib/module/store/conversation.js +17 -20
  27. package/lib/module/store/conversation.js.map +1 -1
  28. package/lib/module/types/chat.js +5 -0
  29. package/lib/module/types/chat.js.map +1 -1
  30. package/lib/module/utils/conversation.js +6 -17
  31. package/lib/module/utils/conversation.js.map +1 -1
  32. package/lib/module/utils/message.js +34 -12
  33. package/lib/module/utils/message.js.map +1 -1
  34. package/lib/typescript/src/assets/images/index.d.ts +1 -0
  35. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  36. package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts +7 -6
  37. package/lib/typescript/src/components/Avatar/SingleAvatar.d.ts.map +1 -1
  38. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -1
  39. package/lib/typescript/src/context/ChatContext.d.ts +5 -0
  40. package/lib/typescript/src/context/ChatContext.d.ts.map +1 -0
  41. package/lib/typescript/src/context/index.d.ts +2 -0
  42. package/lib/typescript/src/context/index.d.ts.map +1 -0
  43. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  44. package/lib/typescript/src/hooks/useChatMessages.d.ts +1 -3
  45. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  46. package/lib/typescript/src/index.d.ts +1 -0
  47. package/lib/typescript/src/index.d.ts.map +1 -1
  48. package/lib/typescript/src/screens/inbox/MessagesTab.d.ts.map +1 -1
  49. package/lib/typescript/src/services/apis.d.ts.map +1 -1
  50. package/lib/typescript/src/services/message.d.ts +0 -2
  51. package/lib/typescript/src/services/message.d.ts.map +1 -1
  52. package/lib/typescript/src/store/conversation.d.ts +1 -0
  53. package/lib/typescript/src/store/conversation.d.ts.map +1 -1
  54. package/lib/typescript/src/types/chat.d.ts +14 -2
  55. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  56. package/lib/typescript/src/utils/conversation.d.ts +5 -4
  57. package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
  58. package/lib/typescript/src/utils/message.d.ts +6 -2
  59. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  60. package/package.json +3 -3
  61. package/src/assets/images/icon_droppii.png +0 -0
  62. package/src/assets/images/index.ts +1 -0
  63. package/src/build-ignore.d.ts +3 -0
  64. package/src/components/Avatar/SingleAvatar.tsx +40 -17
  65. package/src/components/ThreadCard/NamePrefixIcon.tsx +17 -4
  66. package/src/components/ThreadCard/ThreadCard.tsx +1 -1
  67. package/src/context/ChatContext.tsx +23 -0
  68. package/src/context/index.ts +1 -0
  69. package/src/core/useChatListener.ts +0 -11
  70. package/src/hooks/useChatMessages.ts +26 -38
  71. package/src/index.tsx +1 -0
  72. package/src/screens/inbox/MessagesTab.tsx +11 -1
  73. package/src/services/apis.ts +2 -0
  74. package/src/services/message.ts +8 -7
  75. package/src/store/conversation.ts +25 -21
  76. package/src/types/chat.ts +20 -2
  77. package/src/utils/conversation.ts +6 -29
  78. 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
- async (text: string) => {
163
- const trimmed = text.trim();
164
- if (!trimmed) {
165
- return;
166
- }
157
+ const sendTextMessage = useCallback(async (text: string) => {
158
+ const trimmed = text.trim();
159
+ if (!trimmed) {
160
+ return;
161
+ }
167
162
 
168
- const sentMessage = await ChatMessageAPI.sendTextMessage({
169
- text: trimmed,
170
- recvID,
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
- if (!relevant.length) {
186
- return;
187
- }
167
+ setMessages((current) => mergeMessages(current, [sentMessage]));
168
+ }, []);
188
169
 
189
- setMessages((current) =>
190
- mergeMessages(current, relevant as DMessageItem[])
191
- );
170
+ const appendIncomingMessages = useCallback((incoming: MessageItem[]) => {
171
+ const relevant = incoming.filter((message) =>
172
+ belongsToConversation(message, conversationIdRef.current)
173
+ );
192
174
 
193
- ChatMessageAPI.markConversationAsRead(conversationIdRef.current).catch(
194
- () => undefined
195
- );
196
- },
197
- [groupID]
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,6 +1,7 @@
1
1
  export { ChatAPI } from './services';
2
2
  export * from './core';
3
3
  export * from './store';
4
+ export * from './context';
4
5
  export * from './hooks/useConversationList';
5
6
  export * from './hooks/useChatMessages';
6
7
  export * from './types/chat';
@@ -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}
@@ -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
 
@@ -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 Parameters<
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
- recvID: params.recvID ?? '',
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
- dConversationCompare,
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
- const newMap = conversations.reduce(
47
- (pre, curr) => {
48
- pre[curr.conversationId] = curr;
49
- return pre;
50
- },
51
- { ...get().map }
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
- const newList = Object.keys(nextMap).sort((a, b) =>
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
- set({
106
- map: {
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 interface ChatProviderProps {
9
- children: React.ReactNode;
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 isBotCrmChat = (chatCategory: DChatCategory) =>
83
- chatCategory === DChatCategory.BIZ_BOT_CRM ||
84
- chatCategory === DChatCategory.BIZ_BOT_PDP;
85
-
86
- export const resolveDisplayName = (
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
  };
@@ -1,5 +1,9 @@
1
1
  import { SessionType, type MessageItem } from '@droppii/openim-rn-client-sdk';
2
- import type { DMessageItem } from '../types/chat';
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
- const runtimeConversationId = (
103
- message as MessageItem & {
104
- conversationID?: string;
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
- if (runtimeConversationId === conversationId) {
109
- return true;
110
- }
120
+ case SessionType.Group: {
121
+ return conversationId === `sg_${message.groupID}`;
122
+ }
111
123
 
112
- if (groupID) {
113
- return (
114
- message.groupID === groupID ||
115
- conversationId === `sg_${groupID}` ||
116
- conversationId.endsWith(`_${groupID}`)
117
- );
124
+ default:
125
+ return false;
118
126
  }
127
+ };
119
128
 
120
- if (message.sessionType !== SessionType.Single) {
121
- return false;
122
- }
129
+ export const getReceiverId = (conversation?: DConversationItem) => {
130
+ if (conversation == null) return { groupID: '', recvID: '' };
123
131
 
124
- const forwardId = getConversationID(
125
- message.sessionType,
126
- message.sendID,
127
- message.recvID
128
- );
129
- const reverseId = getConversationID(
130
- message.sessionType,
131
- message.recvID,
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 conversationId === forwardId || conversationId === reverseId;
141
+ return { groupID: '', recvID: conversation.peer.id ?? '' };
136
142
  };