@droppii-org/chat-mobile 0.2.47 → 0.2.49

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 (79) hide show
  1. package/lib/module/config/api-endpoints.js +3 -2
  2. package/lib/module/config/api-endpoints.js.map +1 -1
  3. package/lib/module/context/ChatContext.js +3 -0
  4. package/lib/module/context/ChatContext.js.map +1 -1
  5. package/lib/module/core/notification/pushToken.js +37 -18
  6. package/lib/module/core/notification/pushToken.js.map +1 -1
  7. package/lib/module/core/useChatListener.js +3 -2
  8. package/lib/module/core/useChatListener.js.map +1 -1
  9. package/lib/module/hooks/message/useSendMessage.js +6 -5
  10. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  11. package/lib/module/hooks/query-keys.js +6 -1
  12. package/lib/module/hooks/query-keys.js.map +1 -1
  13. package/lib/module/hooks/useChatCompose.js +18 -1
  14. package/lib/module/hooks/useChatCompose.js.map +1 -1
  15. package/lib/module/hooks/users/useGetUsersInfo.js +14 -0
  16. package/lib/module/hooks/users/useGetUsersInfo.js.map +1 -0
  17. package/lib/module/screens/chat-detail/ChatDetail.js +8 -5
  18. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  19. package/lib/module/screens/chat-detail/ChatDetailHeader.js +21 -4
  20. package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
  21. package/lib/module/screens/inbox/Inbox.js +3 -3
  22. package/lib/module/screens/inbox/Inbox.js.map +1 -1
  23. package/lib/module/services/endpoints.js +9 -5
  24. package/lib/module/services/endpoints.js.map +1 -1
  25. package/lib/module/store/conversation.js +24 -3
  26. package/lib/module/store/conversation.js.map +1 -1
  27. package/lib/module/store/user.js +5 -0
  28. package/lib/module/store/user.js.map +1 -1
  29. package/lib/module/types/chat.js +11 -0
  30. package/lib/module/types/chat.js.map +1 -1
  31. package/lib/module/utils/message.js +2 -2
  32. package/lib/module/utils/message.js.map +1 -1
  33. package/lib/typescript/src/config/api-endpoints.d.ts +2 -1
  34. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
  35. package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
  36. package/lib/typescript/src/core/notification/pushToken.d.ts +3 -13
  37. package/lib/typescript/src/core/notification/pushToken.d.ts.map +1 -1
  38. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  39. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  40. package/lib/typescript/src/hooks/query-keys.d.ts +5 -0
  41. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  42. package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -1
  43. package/lib/typescript/src/hooks/users/useGetUsersInfo.d.ts +2 -0
  44. package/lib/typescript/src/hooks/users/useGetUsersInfo.d.ts.map +1 -0
  45. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  46. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
  47. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
  48. package/lib/typescript/src/screens/chat-detail/types.d.ts +1 -0
  49. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  50. package/lib/typescript/src/screens/inbox/Inbox.d.ts +2 -2
  51. package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
  52. package/lib/typescript/src/services/endpoints.d.ts +2 -0
  53. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  54. package/lib/typescript/src/store/conversation.d.ts +6 -1
  55. package/lib/typescript/src/store/conversation.d.ts.map +1 -1
  56. package/lib/typescript/src/store/user.d.ts +2 -0
  57. package/lib/typescript/src/store/user.d.ts.map +1 -1
  58. package/lib/typescript/src/types/chat.d.ts +28 -0
  59. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  60. package/lib/typescript/src/utils/message.d.ts +1 -1
  61. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  62. package/package.json +2 -2
  63. package/src/config/api-endpoints.ts +4 -2
  64. package/src/context/ChatContext.tsx +3 -0
  65. package/src/core/notification/pushToken.ts +52 -16
  66. package/src/core/useChatListener.ts +3 -2
  67. package/src/hooks/message/useSendMessage.ts +9 -3
  68. package/src/hooks/query-keys.ts +6 -0
  69. package/src/hooks/useChatCompose.ts +19 -0
  70. package/src/hooks/users/useGetUsersInfo.ts +13 -0
  71. package/src/screens/chat-detail/ChatDetail.tsx +13 -5
  72. package/src/screens/chat-detail/ChatDetailHeader.tsx +20 -4
  73. package/src/screens/chat-detail/types.ts +1 -0
  74. package/src/screens/inbox/Inbox.tsx +9 -4
  75. package/src/services/endpoints.ts +8 -5
  76. package/src/store/conversation.ts +37 -3
  77. package/src/store/user.ts +5 -0
  78. package/src/types/chat.ts +32 -0
  79. package/src/utils/message.ts +7 -2
@@ -12,17 +12,19 @@
12
12
  export interface ApiEndpoints {
13
13
  // Chat service (relative paths - used with axios baseURL)
14
14
  urlMetadata: string;
15
- conversationFlow: string;
15
+ conversation: string;
16
16
  // Image upload service (relative path - full URL constructed in imageUpload service)
17
17
  imageUpload: string;
18
18
  bot: string;
19
+ uploadFcmToken: string;
19
20
  }
20
21
 
21
22
  const DEFAULT_ENDPOINTS: ApiEndpoints = {
22
23
  urlMetadata: '/chat-service/v1/utils/url-metadata',
23
- conversationFlow: '/chat-service/v1/app/conversations',
24
+ conversation: '/chat-service/v1/app/conversations',
24
25
  imageUpload: '/uploader-service/v1/uploader/permanently',
25
26
  bot: '/chat-service/v1/app/bots/',
27
+ uploadFcmToken: '/chat-service/third/fcm_update_token',
26
28
  };
27
29
 
28
30
  let endpoints: ApiEndpoints = { ...DEFAULT_ENDPOINTS };
@@ -42,6 +42,9 @@ const ChatProviderInner = ({
42
42
  if (getChatToken) {
43
43
  try {
44
44
  const res = await getChatToken();
45
+ if (res?.token) {
46
+ useUserStore.getState().setToken(res.token);
47
+ }
45
48
  const loginStatus = await OpenIMSDK.getLoginStatus();
46
49
  if (!!res?.id && !!res?.token && loginStatus === LoginStatus.Logout) {
47
50
  await OpenIMSDK.login(
@@ -1,24 +1,60 @@
1
- import OpenIMSDK from '@droppii/openim-rn-client-sdk';
1
+ import { Platform } from 'react-native';
2
+ import uuid from 'react-native-uuid';
3
+ import { apiInstance } from '../../services/apis';
4
+ import { ENDPOINTS } from '../../services/endpoints';
5
+ import { useUserStore } from '../../store/user';
6
+ import { DChatPlatform, type DChatProject } from '../../types/chat';
2
7
 
3
- /**
4
- * Default TTL (seconds — confirmed unit per docs.openim.io/sdks/api/third/updatefcmtoken).
5
- * Not an OpenIM-mandated value, just a reasonable default (90 days).
6
- */
7
- export const DEFAULT_PUSH_TOKEN_EXPIRE_SECONDS = 7776000;
8
+ const PUSH_TOKEN_EXPIRE_MS = 7 * 24 * 60 * 60 * 1000;
9
+
10
+ const platformID =
11
+ Platform.OS === 'ios' ? DChatPlatform.IOS : DChatPlatform.ANDROID;
12
+
13
+ const uploadFcmToken = async (
14
+ fcmToken: string,
15
+ userID: string,
16
+ project: DChatProject,
17
+ expireTime: number
18
+ ): Promise<void> => {
19
+ console.log('[chat-mobile] uploadFcmToken', {
20
+ platformID,
21
+ fcmToken,
22
+ account: userID,
23
+ project,
24
+ expireTime,
25
+ });
26
+ const token = useUserStore.getState().token;
27
+ if (!token) {
28
+ console.warn(
29
+ '[chat-mobile] uploadFcmToken: missing OpenIM token, request will likely be rejected'
30
+ );
31
+ }
32
+
33
+ await apiInstance?.post(
34
+ ENDPOINTS.chatService.uploadFcmToken,
35
+ {
36
+ platformID,
37
+ fcmToken,
38
+ account: userID,
39
+ expireTime,
40
+ project,
41
+ },
42
+ { headers: { operationID: uuid.v4(), token } }
43
+ );
44
+ };
8
45
 
9
46
  export const registerPushToken = (
10
47
  fcmToken: string,
11
- expireTime: number = DEFAULT_PUSH_TOKEN_EXPIRE_SECONDS
48
+ userID: string,
49
+ project: DChatProject,
50
+ expireTime: number = Date.now() + PUSH_TOKEN_EXPIRE_MS
12
51
  ): Promise<void> => {
13
- return OpenIMSDK.updateFcmToken(fcmToken, expireTime);
52
+ return uploadFcmToken(fcmToken, userID, project, expireTime);
14
53
  };
15
54
 
16
- /**
17
- * Clears the token on logout. OpenIM has no documented dedicated "clear" API
18
- * (docs.openim.io only documents setting a token) — this mirrors Firebase's
19
- * empty-token convention. UNVERIFIED with the OpenIM/backend team; confirm
20
- * before relying on this in production.
21
- */
22
- export const unregisterPushToken = (): Promise<void> => {
23
- return OpenIMSDK.updateFcmToken('', 0);
55
+ export const unregisterPushToken = (
56
+ userID: string,
57
+ project: DChatProject
58
+ ): Promise<void> => {
59
+ return uploadFcmToken('', userID, project, 0);
24
60
  };
@@ -124,9 +124,10 @@ export const useChatListener = () => {
124
124
  console.log('[Listener] OnRecvNewMessages', messages);
125
125
  const conversationId =
126
126
  useConversationStore.getState().currentConversationId;
127
- if (!conversationId) return;
127
+ const targetUserId = useConversationStore.getState().currentUserId;
128
+ if (!conversationId && !targetUserId) return;
128
129
  const relevant = messages.filter((message) =>
129
- belongsToConversation(message, conversationId)
130
+ belongsToConversation(message, conversationId, targetUserId)
130
131
  );
131
132
 
132
133
  if (!relevant.length) return;
@@ -66,7 +66,7 @@ export const useSendMessage = () => {
66
66
  extendMessageInfo,
67
67
  plainText
68
68
  );
69
- console.log('[useSendMessage] dispatchSend params', sendParams);
69
+ // console.log('[useSendMessage] dispatchSend params', sendParams);
70
70
  const sent = await OpenIMSDK.sendMessage(sendParams);
71
71
  deleteOneMessage(message.clientMsgID);
72
72
  pushNewMessage(sent);
@@ -91,8 +91,14 @@ export const useSendMessage = () => {
91
91
  flow?: IRunConversationFlowPayload;
92
92
  customMessage?: CustomMessagePayload;
93
93
  }) => {
94
- const { userID: recvID, groupID } =
95
- useConversationStore.getState().getCurrentConversation() || {};
94
+ const currentConversation = useConversationStore
95
+ .getState()
96
+ .getCurrentConversation();
97
+ const targetUserId = useConversationStore.getState().currentUserId;
98
+ const targetGroupId = useConversationStore.getState().currentGroupId;
99
+
100
+ const recvID = currentConversation?.userID || targetUserId;
101
+ const groupID = currentConversation?.groupID || targetGroupId;
96
102
  if (!recvID && !groupID) return;
97
103
 
98
104
  const messageList: MessageItem[] = [];
@@ -25,6 +25,12 @@ export const conversationQueryKeys = {
25
25
  detailByBotId: (botId: string) => ['conversationByBotId', botId],
26
26
  detailBySourceId: (sourceId: string) => ['detailBySourceId', sourceId],
27
27
  joinedGroup: (groupId?: string) => [...conversationQueryKeys.all, groupId],
28
+ create: (userId?: string) => ['create', userId],
29
+ };
30
+
31
+ export const usersQueryKeys = {
32
+ all: ['users'] as const,
33
+ list: (userIds: string[]) => [...usersQueryKeys.all, ...userIds],
28
34
  };
29
35
 
30
36
  export type ConversationQueryKeys = typeof conversationQueryKeys;
@@ -13,6 +13,9 @@ import {
13
13
  generateAndUploadVideoThumbnails,
14
14
  type VideoWithUploadStatus,
15
15
  } from '../utils/videoThumbnailUpload';
16
+ import { useConversationStore } from '../store';
17
+ import { DChatCategory } from '../types/chat';
18
+ import { useChatContext } from '../context';
16
19
 
17
20
  export function useChatCompose() {
18
21
  const [text, setText] = useState('');
@@ -22,6 +25,7 @@ export function useChatCompose() {
22
25
  const linkPreview = useLinkPreview(text);
23
26
  const { send: sendAttachments } = useSendAttachment();
24
27
  const { sendMessage } = useSendMessage();
28
+ const { applicationType } = useChatContext();
25
29
 
26
30
  const isUploading = useMemo(
27
31
  () =>
@@ -49,6 +53,20 @@ export function useChatCompose() {
49
53
  const { selectedImages, clearAllImages } = imageAttachment;
50
54
  const { selectedFiles, clearAllFiles } = fileAttachment;
51
55
  const urlMeta = linkPreview.isVisible ? linkPreview.metadata : undefined;
56
+ const currentConversationId =
57
+ useConversationStore.getState().currentConversationId;
58
+ const targetUserId = useConversationStore.getState().currentUserId;
59
+ const isCreatingConversation =
60
+ useConversationStore.getState().isCreatingConversation;
61
+
62
+ if (targetUserId && !currentConversationId && !isCreatingConversation) {
63
+ await useConversationStore.getState().createConversation({
64
+ chatType: 'SINGLE',
65
+ chatCategory: DChatCategory.BIZ_BIZ,
66
+ applicationType,
67
+ peerUserId: targetUserId,
68
+ });
69
+ }
52
70
 
53
71
  // --- Attachments ---
54
72
  if (selectedImages.length > 0 || selectedFiles.length > 0) {
@@ -161,6 +179,7 @@ export function useChatCompose() {
161
179
  sendAttachments,
162
180
  sendMessage,
163
181
  text,
182
+ applicationType,
164
183
  ]);
165
184
 
166
185
  return {
@@ -0,0 +1,13 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { usersQueryKeys } from '../query-keys';
3
+ import OpenIMSDK from '@droppii/openim-rn-client-sdk';
4
+
5
+ export const useGetUsersInfo = (userIds: string[]) =>
6
+ useQuery({
7
+ queryKey: [usersQueryKeys.list(userIds)],
8
+ queryFn: async () => {
9
+ const users = await OpenIMSDK.getUsersInfo(userIds);
10
+ return users || [];
11
+ },
12
+ enabled: userIds.length > 0,
13
+ });
@@ -36,6 +36,7 @@ import type { MessageItem } from '@droppii/openim-rn-client-sdk';
36
36
  import { useMessages } from '../../hooks/message/useMessages';
37
37
  import { getBotId } from '../../utils/conversation';
38
38
  import { ChatEvent } from '../../types/events';
39
+ import { useSetIsShowFLashMessage } from '../../context/global';
39
40
 
40
41
  const ChatDetail = memo(
41
42
  ({
@@ -107,17 +108,23 @@ const ChatDetail = memo(
107
108
  hasMoreNew,
108
109
  } = useMessages({ conversationId });
109
110
 
111
+ const setShowFLashMessage = useSetIsShowFLashMessage();
112
+
110
113
  const { pinnedMessages, pinMessage, unpinMessage } =
111
114
  usePinMessage(conversationId);
112
115
 
113
116
  useEffect(() => {
114
- if (conversationId) {
115
- useConversationStore.getState().setCurrentConversation(conversationId);
116
- }
117
+ useConversationStore
118
+ .getState()
119
+ .setCurrentConversation(conversationId, targetUserId, targetGroupId);
120
+ setShowFLashMessage(false);
117
121
  return () => {
118
- useConversationStore.getState().setCurrentConversation(undefined);
122
+ useConversationStore
123
+ .getState()
124
+ .setCurrentConversation(undefined, undefined, undefined);
125
+ setShowFLashMessage(true);
119
126
  };
120
- }, [conversationId]);
127
+ }, [conversationId, targetUserId, targetGroupId, setShowFLashMessage]);
121
128
 
122
129
  const resolvedShowAddMember =
123
130
  showAddMember ?? shouldShowAddMember(conversation);
@@ -160,6 +167,7 @@ const ChatDetail = memo(
160
167
  <KContainer.Page flex edges={['bottom']}>
161
168
  <ChatDetailHeader
162
169
  conversationId={conversationId}
170
+ targetUserId={targetUserId}
163
171
  showAddMember={resolvedShowAddMember}
164
172
  onBack={onBack ?? handleBackDefault}
165
173
  onPressSearch={onPressSearch}
@@ -11,11 +11,13 @@ import { NamePrefixIcon } from '../../components/ThreadCard/NamePrefixIcon';
11
11
  import type { ChatDetailHeaderProps } from './types';
12
12
  import { useConversation } from '../../store';
13
13
  import { getConversationSubtitle } from './conversationHeader.utils';
14
+ import { useGetUsersInfo } from '../../hooks/users/useGetUsersInfo';
14
15
 
15
16
  const ChatDetailHeader = memo(
16
17
  ({
17
18
  showAddMember = false,
18
19
  conversationId,
20
+ targetUserId,
19
21
  onBack,
20
22
  onPressSearch,
21
23
  onPressAddMember,
@@ -23,6 +25,20 @@ const ChatDetailHeader = memo(
23
25
  onPressAvatar,
24
26
  }: ChatDetailHeaderProps) => {
25
27
  const conversation = useConversation(conversationId);
28
+ const { data: usersInfo } = useGetUsersInfo(
29
+ targetUserId ? [targetUserId] : []
30
+ );
31
+
32
+ const { faceURL, showName } = useMemo(() => {
33
+ const userInfo = usersInfo?.[0];
34
+ const resolvedFaceURL = conversation?.faceURL || userInfo?.faceURL;
35
+ const resolvedShowName = conversation?.showName || userInfo?.nickname;
36
+
37
+ return {
38
+ faceURL: resolvedFaceURL,
39
+ showName: resolvedShowName,
40
+ };
41
+ }, [conversation, usersInfo]);
26
42
  const rightButtons = useMemo(() => {
27
43
  const buttons = [
28
44
  {
@@ -76,8 +92,8 @@ const ChatDetailHeader = memo(
76
92
  activeOpacity={0.7}
77
93
  >
78
94
  <AvatarSection
79
- avatar={conversation?.faceURL ?? null}
80
- fullName={conversation?.showName || ''}
95
+ avatar={faceURL || null}
96
+ fullName={showName || ''}
81
97
  peerType={conversation?.peerType}
82
98
  />
83
99
 
@@ -90,7 +106,7 @@ const ChatDetailHeader = memo(
90
106
  numberOfLines={1}
91
107
  flex
92
108
  >
93
- {conversation?.showName || ''}
109
+ {showName || ''}
94
110
  </KLabel.Text>
95
111
  </KContainer.View>
96
112
  {!!conversation && (
@@ -106,7 +122,7 @@ const ChatDetailHeader = memo(
106
122
  </KContainer.Touchable>
107
123
  </KContainer.View>
108
124
  ),
109
- [onBack, onPressAvatar, conversation]
125
+ [onBack, onPressAvatar, conversation, faceURL, showName]
110
126
  );
111
127
 
112
128
  return (
@@ -45,6 +45,7 @@ export interface ChatDetailHeaderProps {
45
45
  onPressMenu?: () => void;
46
46
  onPressAvatar?: () => void;
47
47
  conversationId: string;
48
+ targetUserId?: string;
48
49
  }
49
50
 
50
51
  export interface ChatQuickActionsRenderParams {
@@ -8,13 +8,18 @@ interface InboxProps {
8
8
  onPressThread?: (item: string) => void;
9
9
  onPressSearch?: () => void;
10
10
  onPressEdit?: () => void;
11
- onPressGroup?: () => void;
11
+ onPressContact?: () => void;
12
12
  }
13
13
 
14
14
  type TabRoute = { key: string; title: string };
15
15
 
16
16
  const Inbox = memo(
17
- ({ onPressThread, onPressSearch, onPressEdit, onPressGroup }: InboxProps) => {
17
+ ({
18
+ onPressThread,
19
+ onPressSearch,
20
+ onPressEdit,
21
+ onPressContact,
22
+ }: InboxProps) => {
18
23
  const [index, setIndex] = useState(0);
19
24
  const [routes] = useState<TabRoute[]>([
20
25
  { key: 'messages', title: 'Tin nhắn' },
@@ -91,7 +96,7 @@ const Inbox = memo(
91
96
  />
92
97
  </KContainer.Touchable>
93
98
  <KContainer.Touchable
94
- onPress={onPressGroup}
99
+ onPress={onPressContact}
95
100
  padding="0.5rem"
96
101
  activeOpacity={0.7}
97
102
  >
@@ -105,7 +110,7 @@ const Inbox = memo(
105
110
  </KContainer.View>
106
111
  </KContainer.View>
107
112
  ),
108
- [onPressSearch, onPressEdit, onPressGroup]
113
+ [onPressSearch, onPressEdit, onPressContact]
109
114
  );
110
115
 
111
116
  return (
@@ -16,18 +16,21 @@ export const ENDPOINTS = {
16
16
  get urlMetadata() {
17
17
  return getApiEndpoint('urlMetadata');
18
18
  },
19
- conversationFlow: (id: string) =>
20
- `${getApiEndpoint('conversationFlow')}/${id}`,
19
+ get uploadFcmToken() {
20
+ return getApiEndpoint('uploadFcmToken');
21
+ },
22
+ conversationFlow: (id: string) => `${getApiEndpoint('conversation')}/${id}`,
21
23
  resetConversationFlow: (id: string) =>
22
- `${getApiEndpoint('conversationFlow')}/${id}/bot-flows/reset`,
24
+ `${getApiEndpoint('conversation')}/${id}/bot-flows/reset`,
23
25
  runConversationFlow: (id: string) =>
24
- `${getApiEndpoint('conversationFlow')}/${id}/bot-flows/run`,
26
+ `${getApiEndpoint('conversation')}/${id}/bot-flows/run`,
25
27
  getConversationByBotId: (botId: string) =>
26
28
  `${getApiEndpoint('bot')}/${botId}/conversation`,
27
29
  conversationFlowMainMenu: (id: string) =>
28
- `${getApiEndpoint('conversationFlow')}/${id}/bot-flows/main-menu`,
30
+ `${getApiEndpoint('conversation')}/${id}/bot-flows/main-menu`,
29
31
  getConversationStatusByBotId: (botId: string) =>
30
32
  `${getApiEndpoint('bot')}/${botId}`,
33
+ createConversation: () => getApiEndpoint('conversation'),
31
34
  },
32
35
  uploaderService: {
33
36
  get uploadImage() {
@@ -1,12 +1,27 @@
1
1
  import { create } from 'zustand';
2
2
  import { sortConversation } from '../utils/conversation';
3
3
  import type { ConversationItem } from '@droppii/openim-rn-client-sdk';
4
+ import type {
5
+ CreateConversationParams,
6
+ CreateConversationResponse,
7
+ } from '../types/chat';
8
+ import { apiInstance } from '../services/apis';
9
+ import { ENDPOINTS } from '../services/endpoints';
10
+ import type { BaseResponse } from '../types/auth';
4
11
 
5
12
  type ConversationID = string;
6
13
 
7
14
  export interface ConversationStore {
8
15
  currentConversationId?: ConversationID;
9
- setCurrentConversation(id?: ConversationID): void;
16
+ currentUserId?: string;
17
+ currentGroupId?: string;
18
+ isCreatingConversation?: boolean;
19
+ createConversation: (params: CreateConversationParams) => Promise<void>;
20
+ setCurrentConversation(
21
+ id?: ConversationID,
22
+ userId?: string,
23
+ groupId?: string
24
+ ): void;
10
25
  getCurrentConversation(): ConversationItem | undefined;
11
26
  list: ConversationID[];
12
27
  map: Record<ConversationID, ConversationItem>;
@@ -26,8 +41,12 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
26
41
  list: [],
27
42
  map: {},
28
43
  newConversationSignal: 0,
29
- setCurrentConversation(id) {
30
- set({ currentConversationId: id });
44
+ setCurrentConversation(id, userId, groupId) {
45
+ set({
46
+ currentConversationId: id,
47
+ currentUserId: userId,
48
+ currentGroupId: groupId,
49
+ });
31
50
  },
32
51
  getCurrentConversation() {
33
52
  const id = get().currentConversationId;
@@ -68,12 +87,27 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
68
87
  list: get().list.filter((item) => item !== id),
69
88
  });
70
89
  },
90
+ createConversation: async (params) => {
91
+ set({ isCreatingConversation: true });
92
+ const res = await apiInstance?.post<
93
+ BaseResponse<CreateConversationResponse>
94
+ >(ENDPOINTS.chatService.createConversation(), params);
95
+ if (res?.data?.data?.conversationId) {
96
+ set({
97
+ currentConversationId: res?.data?.data?.conversationId,
98
+ isCreatingConversation: false,
99
+ });
100
+ } else {
101
+ set({ isCreatingConversation: false });
102
+ }
103
+ },
71
104
  reset() {
72
105
  set({
73
106
  currentConversationId: undefined,
74
107
  list: [],
75
108
  map: {},
76
109
  newConversationSignal: 0,
110
+ isCreatingConversation: false,
77
111
  });
78
112
  },
79
113
  }));
package/src/store/user.ts CHANGED
@@ -5,6 +5,8 @@ import { ConnectStatus, SyncStatus } from '../types/chat';
5
5
  export interface UserStore {
6
6
  user?: SelfUserInfo;
7
7
  setUser: (user: SelfUserInfo) => void;
8
+ token?: string;
9
+ setToken: (token: string) => void;
8
10
  connectStatus: ConnectStatus;
9
11
  updateConnectStatus: (status: ConnectStatus) => void;
10
12
  syncStatus: SyncStatus;
@@ -17,6 +19,9 @@ export const useUserStore = create<UserStore>()((set) => ({
17
19
  setUser(user) {
18
20
  set({ user });
19
21
  },
22
+ setToken(token) {
23
+ set({ token });
24
+ },
20
25
  updateConnectStatus(connectStatus) {
21
26
  set({ connectStatus });
22
27
  },
package/src/types/chat.ts CHANGED
@@ -53,6 +53,17 @@ export enum DChatApplicationType {
53
53
  OBEFE = 'OBEFE',
54
54
  }
55
55
 
56
+ export enum DChatPlatform {
57
+ IOS = 1,
58
+ ANDROID = 2,
59
+ WEB = 5,
60
+ }
61
+
62
+ export enum DChatProject {
63
+ BIZ = 'BIZ',
64
+ CUS = 'CUS',
65
+ }
66
+
56
67
  export enum DChatCategory {
57
68
  BIZ_BIZ = 'BIZ_BIZ',
58
69
  BIZ_MALL = 'BIZ_MALL',
@@ -218,3 +229,24 @@ export type CustomElemOrderData = {
218
229
  export type CustomMessagePayload =
219
230
  | { type: typeof CustomMessageType.Order; data: CustomElemOrderData }
220
231
  | { type: typeof CustomMessageType.Product; data: Record<string, unknown> };
232
+
233
+ export interface CreateConversationParams {
234
+ peerUserId: string;
235
+ chatCategory: DChatCategory;
236
+ applicationType: DChatApplicationType;
237
+ chatType: 'SINGLE' | 'GROUP';
238
+ }
239
+
240
+ export interface CreateConversationResponse {
241
+ conversationId: string;
242
+ groupId?: string;
243
+ groupName?: string;
244
+ groupAvatar?: string;
245
+ chatCategory: DChatCategory;
246
+ applicationType: DChatApplicationType;
247
+ chatType: 'SINGLE' | 'GROUP';
248
+ members?: {
249
+ userId: string;
250
+ memberRole?: string;
251
+ }[];
252
+ }
@@ -100,7 +100,8 @@ export const hasNewHistoryMessages = (
100
100
 
101
101
  export const belongsToConversation = (
102
102
  message: MessageItem,
103
- conversationId: string
103
+ conversationId?: string,
104
+ targetUserId?: string
104
105
  ): boolean => {
105
106
  switch (message.sessionType) {
106
107
  case SessionType.Single: {
@@ -114,7 +115,11 @@ export const belongsToConversation = (
114
115
  message.recvID,
115
116
  message.sendID
116
117
  );
117
- return conversationId === forwardId || conversationId === reverseId;
118
+ return conversationId
119
+ ? conversationId === forwardId || conversationId === reverseId
120
+ : targetUserId
121
+ ? message.sendID === targetUserId || message.recvID === targetUserId
122
+ : false;
118
123
  }
119
124
 
120
125
  case SessionType.Group: {