@droppii-org/chat-mobile 0.2.18 → 0.2.19

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 (104) hide show
  1. package/lib/module/components/ThreadCard/AvatarSection.js.map +1 -1
  2. package/lib/module/components/ThreadCard/ThreadCard.js +17 -9
  3. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  4. package/lib/module/components/flows/inputButtons/index.js +7 -3
  5. package/lib/module/components/flows/inputButtons/index.js.map +1 -1
  6. package/lib/module/context/ChatContext.js +27 -4
  7. package/lib/module/context/ChatContext.js.map +1 -1
  8. package/lib/module/context/ChatDetailContext.js.map +1 -1
  9. package/lib/module/core/useChatListener.js +2 -4
  10. package/lib/module/core/useChatListener.js.map +1 -1
  11. package/lib/module/core/useUserListener.js +0 -7
  12. package/lib/module/core/useUserListener.js.map +1 -1
  13. package/lib/module/hooks/flows/useFlow.js +14 -3
  14. package/lib/module/hooks/flows/useFlow.js.map +1 -1
  15. package/lib/module/hooks/message/useSendMessage.js +1 -1
  16. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  17. package/lib/module/hooks/useConversationList.js +3 -27
  18. package/lib/module/hooks/useConversationList.js.map +1 -1
  19. package/lib/module/hooks/useLinkPreview/useLinkPreview.js +1 -1
  20. package/lib/module/hooks/useLinkPreview/useLinkPreview.js.map +1 -1
  21. package/lib/module/screens/chat-detail/ChatDetail.js +1 -6
  22. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  23. package/lib/module/screens/chat-detail/ChatDetailHeader.js +6 -6
  24. package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
  25. package/lib/module/screens/chat-detail/ChatLinkPreview.js +3 -1
  26. package/lib/module/screens/chat-detail/ChatLinkPreview.js.map +1 -1
  27. package/lib/module/screens/chat-detail/ChatListLegend.js +2 -2
  28. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  29. package/lib/module/screens/chat-detail/conversationHeader.utils.js +9 -0
  30. package/lib/module/screens/chat-detail/conversationHeader.utils.js.map +1 -1
  31. package/lib/module/screens/inbox/Inbox.js +5 -1
  32. package/lib/module/screens/inbox/Inbox.js.map +1 -1
  33. package/lib/module/services/apis.js +0 -20
  34. package/lib/module/services/apis.js.map +1 -1
  35. package/lib/module/store/conversation.js +1 -38
  36. package/lib/module/store/conversation.js.map +1 -1
  37. package/lib/module/types/auth.js.map +1 -1
  38. package/lib/module/types/chat.js.map +1 -1
  39. package/lib/module/utils/conversation.js +1 -28
  40. package/lib/module/utils/conversation.js.map +1 -1
  41. package/lib/module/utils/message.js.map +1 -1
  42. package/lib/module/utils/messageUtils.js +5 -3
  43. package/lib/module/utils/messageUtils.js.map +1 -1
  44. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +2 -2
  45. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -1
  46. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  47. package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
  48. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  49. package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
  50. package/lib/typescript/src/hooks/flows/useFlow.d.ts.map +1 -1
  51. package/lib/typescript/src/hooks/useConversationList.d.ts +2 -2
  52. package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -1
  53. package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts.map +1 -1
  54. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  55. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  56. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
  57. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
  58. package/lib/typescript/src/screens/chat-detail/ChatLinkPreview.d.ts.map +1 -1
  59. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  60. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts +7 -5
  61. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts.map +1 -1
  62. package/lib/typescript/src/screens/chat-detail/types.d.ts +6 -9
  63. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  64. package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
  65. package/lib/typescript/src/services/apis.d.ts +0 -4
  66. package/lib/typescript/src/services/apis.d.ts.map +1 -1
  67. package/lib/typescript/src/store/conversation.d.ts +5 -8
  68. package/lib/typescript/src/store/conversation.d.ts.map +1 -1
  69. package/lib/typescript/src/types/auth.d.ts +1 -0
  70. package/lib/typescript/src/types/auth.d.ts.map +1 -1
  71. package/lib/typescript/src/types/chat.d.ts +15 -13
  72. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  73. package/lib/typescript/src/utils/conversation.d.ts +2 -5
  74. package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
  75. package/lib/typescript/src/utils/message.d.ts +3 -3
  76. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  77. package/lib/typescript/src/utils/messageUtils.d.ts +2 -1
  78. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  79. package/package.json +2 -2
  80. package/src/components/ThreadCard/AvatarSection.tsx +2 -3
  81. package/src/components/ThreadCard/ThreadCard.tsx +22 -15
  82. package/src/components/flows/inputButtons/index.tsx +4 -1
  83. package/src/context/ChatContext.tsx +35 -3
  84. package/src/context/ChatDetailContext.tsx +1 -1
  85. package/src/core/useChatListener.ts +2 -4
  86. package/src/core/useUserListener.ts +0 -8
  87. package/src/hooks/flows/useFlow.ts +12 -3
  88. package/src/hooks/message/useSendMessage.ts +1 -1
  89. package/src/hooks/useConversationList.ts +6 -40
  90. package/src/hooks/useLinkPreview/useLinkPreview.ts +6 -1
  91. package/src/screens/chat-detail/ChatDetail.tsx +1 -7
  92. package/src/screens/chat-detail/ChatDetailHeader.tsx +6 -7
  93. package/src/screens/chat-detail/ChatLinkPreview.tsx +16 -12
  94. package/src/screens/chat-detail/ChatListLegend.tsx +2 -6
  95. package/src/screens/chat-detail/conversationHeader.utils.ts +16 -6
  96. package/src/screens/chat-detail/types.ts +6 -13
  97. package/src/screens/inbox/Inbox.tsx +5 -1
  98. package/src/services/apis.ts +0 -38
  99. package/src/store/conversation.ts +6 -55
  100. package/src/types/auth.ts +1 -0
  101. package/src/types/chat.ts +16 -20
  102. package/src/utils/conversation.ts +2 -40
  103. package/src/utils/message.ts +3 -2
  104. package/src/utils/messageUtils.ts +11 -3
@@ -47,7 +47,7 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
47
47
  return conversation?.peerType === PeerType.Bot;
48
48
  }, [conversation]);
49
49
 
50
- const setConverationFlowStep = useCallback((step: IConversationFlowStep) => {
50
+ const setConverationFlowStep = useCallback((step?: IConversationFlowStep) => {
51
51
  setConverationFlow(
52
52
  (prev) =>
53
53
  ({
@@ -10,16 +10,14 @@ export const useChatListener = () => {
10
10
  const onConversationChanged = useCallback(
11
11
  (conversations: ConversationItem[]) => {
12
12
  console.log('onConversationChanged', conversations);
13
- useConversationStore.getState().mergeOpenIMConversations(conversations);
13
+ useConversationStore.getState().updateConversations(conversations);
14
14
  },
15
15
  []
16
16
  );
17
17
 
18
18
  const onNewConversation = useCallback((conversations: ConversationItem[]) => {
19
19
  console.log('onNewConversation', conversations);
20
- // mergeOpenIMConversations increments newConversationSignal when a conversation
21
- // isn't in the store yet, which triggers a full refetch in useConversationList.
22
- useConversationStore.getState().mergeOpenIMConversations(conversations);
20
+ useConversationStore.getState().updateConversations(conversations);
23
21
  }, []);
24
22
 
25
23
  const onGroupMemberAdded = useCallback((member: GroupMemberItem) => {
@@ -21,14 +21,6 @@ export const useUserListener = () => {
21
21
  }
22
22
  }, []);
23
23
 
24
- useEffect(() => {
25
- ChatAPI.getLoginStatus().then((value) => {
26
- if (value) {
27
- useUserStore.getState().updateConnectStatus(LoginStatus.Logged);
28
- }
29
- });
30
- }, []);
31
-
32
24
  useEffect(() => {
33
25
  if (isLoggedIn) {
34
26
  fetchAndUpdateUser();
@@ -11,14 +11,25 @@ import type {
11
11
  } from '../../types/flows';
12
12
  import { useSendMessage } from '../message/useSendMessage';
13
13
  import { useChatDetailContext } from '../../context/ChatDetailContext';
14
+ import { trans } from '../../translation';
14
15
 
15
16
  export const useResetFlow = () => {
16
17
  const conversationId =
17
18
  useConversationStore((state) => state.currentConversationId) || '';
18
19
  const { mutate: runFlow } = useRunFlow();
20
+ const { sendMessage } = useSendMessage();
21
+ const conversationFlow = useChatDetailContext().conversationFlow;
19
22
  return useMutation({
20
23
  mutationKey: conversationQueryKeys.flow(conversationId),
21
24
  mutationFn: async () => {
25
+ await sendMessage({
26
+ plainText: trans('restart'),
27
+ flow: {
28
+ groupId: conversationFlow?.flowCurrentStep?.id || '',
29
+ childId: null,
30
+ actionId: null,
31
+ },
32
+ });
22
33
  const res = await apiInstance?.post<BaseResponse<IConversationFlow>>(
23
34
  ENDPOINTS.chatService.resetConversationFlow(conversationId),
24
35
  {}
@@ -55,9 +66,7 @@ export const useRunFlow = () => {
55
66
  return res?.data?.data || undefined;
56
67
  },
57
68
  onSuccess: (data) => {
58
- if (data?.flowNextStep) {
59
- setConverationFlowStep(data?.flowNextStep);
60
- }
69
+ setConverationFlowStep(data?.flowNextStep || undefined);
61
70
  },
62
71
  });
63
72
  };
@@ -106,7 +106,7 @@ const generateOfflinePushInfo = (
106
106
  desc,
107
107
  ex: JSON.stringify({
108
108
  icon: conversationData?.faceURL || '',
109
- conversationId: conversationData?.conversationId || '',
109
+ conversationId: conversationData?.conversationID || '',
110
110
  title,
111
111
  desc,
112
112
  }),
@@ -1,15 +1,9 @@
1
1
  import { useEffect } from 'react';
2
2
  import { useQuery } from '@tanstack/react-query';
3
- import OpenIMSDK, {
4
- type ConversationItem,
5
- } from '@droppii/openim-rn-client-sdk';
3
+ import OpenIMSDK from '@droppii/openim-rn-client-sdk';
6
4
  import { conversationQueryKeys } from './query-keys';
7
5
  import { useConversationStore } from '../store';
8
- import type {
9
- DChatApplicationType,
10
- DConversationItem,
11
- DMessageItem,
12
- } from '../types/chat';
6
+ import type { DChatApplicationType } from '../types/chat';
13
7
 
14
8
  type Params = {
15
9
  applicationType: DChatApplicationType;
@@ -17,34 +11,6 @@ type Params = {
17
11
  enabled?: boolean;
18
12
  };
19
13
 
20
- function toConversationItem(raw: ConversationItem): DConversationItem {
21
- let lastMessage: DMessageItem | undefined;
22
- if (raw.latestMsg) {
23
- try {
24
- lastMessage = JSON.parse(raw.latestMsg) as DMessageItem;
25
- } catch {
26
- // leave undefined
27
- }
28
- }
29
-
30
- let applicationType: DChatApplicationType | undefined;
31
- if (raw.ex) {
32
- try {
33
- applicationType = (
34
- JSON.parse(raw.ex) as { applicationType?: DChatApplicationType }
35
- ).applicationType;
36
- } catch {
37
- // leave undefined
38
- }
39
- }
40
-
41
- return {
42
- ...(raw as DConversationItem),
43
- lastMessage,
44
- applicationType: applicationType as DChatApplicationType,
45
- };
46
- }
47
-
48
14
  export function useConversationList({
49
15
  applicationType,
50
16
  page = 1,
@@ -58,13 +24,13 @@ export function useConversationList({
58
24
  queryKey: [...conversationQueryKeys.list(applicationType), page],
59
25
  enabled: enabled !== false,
60
26
  queryFn: async () => {
61
- const offset = (page - 1) * 20;
62
- const rawList = await OpenIMSDK.getConversationListSplitApp({
27
+ const offset = (page - 1) * 500;
28
+ const conversations = await OpenIMSDK.getConversationListSplitApp({
63
29
  offset,
64
- count: 20,
30
+ count: 500,
65
31
  applicationType,
66
32
  });
67
- const conversations = rawList.map(toConversationItem);
33
+
68
34
  useConversationStore.getState().updateConversations(conversations);
69
35
  return conversations;
70
36
  },
@@ -27,5 +27,10 @@ export function useLinkPreview(value: string | undefined) {
27
27
 
28
28
  const isVisible = !!detectedUrl && !isDismissed && (isLoading || !!metadata);
29
29
 
30
- return { isVisible, metadata: metadata || undefined, isLoading, dismiss };
30
+ return {
31
+ isVisible,
32
+ metadata: !isLoading ? metadata || undefined : undefined,
33
+ isLoading,
34
+ dismiss,
35
+ };
31
36
  }
@@ -43,8 +43,6 @@ const ChatDetail = memo(
43
43
  subtitle,
44
44
  avatarUri,
45
45
  avatarFullName,
46
- peerType,
47
- applicationType,
48
46
  showAddMember,
49
47
  getSubtitle,
50
48
  onBack,
@@ -139,9 +137,6 @@ const ChatDetail = memo(
139
137
  const resolvedAvatarUri =
140
138
  avatarUri ?? getConversationAvatarUri(conversation);
141
139
  const resolvedAvatarFullName = avatarFullName ?? resolvedTitle;
142
- const resolvedPeerType = peerType;
143
- const resolvedApplicationType =
144
- applicationType ?? conversation?.applicationType;
145
140
  const resolvedShowAddMember =
146
141
  showAddMember ?? shouldShowAddMember(conversation);
147
142
  const resolvedSubtitle = useMemo(() => {
@@ -234,12 +229,11 @@ const ChatDetail = memo(
234
229
  <ChatDetailProvider>
235
230
  <KContainer.Page flex edges={['bottom']}>
236
231
  <ChatDetailHeader
232
+ conversationId={conversationId}
237
233
  title={resolvedTitle}
238
234
  subtitle={resolvedSubtitle}
239
235
  avatarUri={resolvedAvatarUri}
240
236
  avatarFullName={resolvedAvatarFullName}
241
- peerType={resolvedPeerType}
242
- applicationType={resolvedApplicationType}
243
237
  showAddMember={resolvedShowAddMember}
244
238
  onBack={onBack ?? handleBackDefault}
245
239
  onPressSearch={
@@ -9,6 +9,7 @@ import {
9
9
  import { AvatarSection } from '../../components/ThreadCard/AvatarSection';
10
10
  import { NamePrefixIcon } from '../../components/ThreadCard/NamePrefixIcon';
11
11
  import type { ChatDetailHeaderProps } from './types';
12
+ import { useConversation } from '../../store';
12
13
 
13
14
  const ChatDetailHeader = memo(
14
15
  ({
@@ -16,15 +17,15 @@ const ChatDetailHeader = memo(
16
17
  subtitle,
17
18
  avatarUri,
18
19
  avatarFullName,
19
- peerType,
20
- applicationType,
21
20
  showAddMember = false,
21
+ conversationId,
22
22
  onBack,
23
23
  onPressSearch,
24
24
  onPressAddMember,
25
25
  onPressMenu,
26
26
  onPressAvatar,
27
27
  }: ChatDetailHeaderProps) => {
28
+ const conversation = useConversation(conversationId);
28
29
  const rightButtons = useMemo(() => {
29
30
  const buttons = [
30
31
  {
@@ -80,13 +81,12 @@ const ChatDetailHeader = memo(
80
81
  <AvatarSection
81
82
  avatar={avatarUri ?? null}
82
83
  fullName={avatarFullName ?? title}
83
- peerType={peerType}
84
- applicationType={applicationType}
84
+ peerType={conversation?.peerType}
85
85
  />
86
86
 
87
87
  <KContainer.View flex marginL="0.5rem">
88
88
  <KContainer.View row alignItems>
89
- <NamePrefixIcon peerType={peerType} />
89
+ <NamePrefixIcon peerType={conversation?.peerType} />
90
90
  <KLabel.Text
91
91
  typo="TextLgBold"
92
92
  color={KColors.gray.dark}
@@ -112,12 +112,11 @@ const ChatDetailHeader = memo(
112
112
  [
113
113
  avatarFullName,
114
114
  avatarUri,
115
- peerType,
116
- applicationType,
117
115
  onBack,
118
116
  onPressAvatar,
119
117
  subtitle,
120
118
  title,
119
+ conversation?.peerType,
121
120
  ]
122
121
  );
123
122
 
@@ -34,18 +34,22 @@ export const ChatLinkPreview = memo(
34
34
  />
35
35
  )}
36
36
  </KContainer.View>
37
- <KContainer.View style={styles.center}>
38
- <KLabel.Text typo="TextNmMedium" numberOfLines={1}>
39
- {metadata?.title}
40
- </KLabel.Text>
41
- <KLabel.Text
42
- typo="TextSmNormal"
43
- color={KColors.gray.light}
44
- numberOfLines={1}
45
- >
46
- {metadata?.description}
47
- </KLabel.Text>
48
- </KContainer.View>
37
+ {metadata ? (
38
+ <KContainer.View style={styles.center}>
39
+ <KLabel.Text typo="TextNmMedium" numberOfLines={1}>
40
+ {metadata?.title}
41
+ </KLabel.Text>
42
+ <KLabel.Text
43
+ typo="TextSmNormal"
44
+ color={KColors.gray.light}
45
+ numberOfLines={1}
46
+ >
47
+ {metadata?.description}
48
+ </KLabel.Text>
49
+ </KContainer.View>
50
+ ) : (
51
+ <KContainer.View flex />
52
+ )}
49
53
  <KContainer.Touchable onPress={onDismiss} style={styles.right}>
50
54
  <KImage.VectorIcons name="close-o" size={16} />
51
55
  </KContainer.Touchable>
@@ -349,12 +349,8 @@ export const ChatListLegend = memo(
349
349
  renderItem={renderItem}
350
350
  estimatedItemSize={avgItemHeightRef.current}
351
351
  keyExtractor={(item) => String(item.clientMsgID || item.serverMsgID)}
352
- recycleItems={!isBotConversation}
353
- extraData={
354
- !isBotConversation
355
- ? `${conversationFlow?.flowCurrentStep?.id ?? ''}-${lastMessageId ?? ''}`
356
- : undefined
357
- }
352
+ recycleItems={false}
353
+ extraData={`${conversationFlow?.flowCurrentStep?.id ?? ''}-${lastMessageId ?? ''}`}
358
354
  alignItemsAtEnd
359
355
  maintainVisibleContentPosition={maintainVisiblePositionConfig}
360
356
  maintainScrollAtEnd={maintainScrollConfig}
@@ -1,8 +1,8 @@
1
- import { PeerType } from '@droppii/openim-rn-client-sdk';
2
- import { type DConversationItem } from '../../types/chat';
1
+ import { PeerType, type ConversationItem } from '@droppii/openim-rn-client-sdk';
2
+ import type { IConversationItemEx } from '../../types/chat';
3
3
 
4
4
  export const getConversationTitle = (
5
- conversation?: DConversationItem
5
+ conversation?: ConversationItem
6
6
  ): string => {
7
7
  if (!conversation) {
8
8
  return 'Tin nhắn';
@@ -12,13 +12,13 @@ export const getConversationTitle = (
12
12
  };
13
13
 
14
14
  export const getConversationAvatarUri = (
15
- conversation?: DConversationItem
15
+ conversation?: ConversationItem
16
16
  ): string | null => {
17
17
  return conversation?.faceURL ?? null;
18
18
  };
19
19
 
20
20
  export const getConversationSubtitle = (
21
- conversation?: DConversationItem
21
+ conversation?: ConversationItem
22
22
  ): string | undefined => {
23
23
  if (!conversation) {
24
24
  return undefined;
@@ -40,7 +40,17 @@ export const getConversationSubtitle = (
40
40
  };
41
41
 
42
42
  export const shouldShowAddMember = (
43
- conversation?: DConversationItem
43
+ conversation?: ConversationItem
44
44
  ): boolean => {
45
45
  return conversation?.peerType === PeerType.Group;
46
46
  };
47
+
48
+ export const getConversationEx = (ex?: string): IConversationItemEx | {} => {
49
+ let result: IConversationItemEx | {} = {};
50
+ try {
51
+ result = JSON.parse(ex || '{}');
52
+ } catch {
53
+ ///
54
+ }
55
+ return result;
56
+ };
@@ -1,13 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
3
  import type { MediaItem } from '../../hooks/useMediaViewer';
4
- import type {
5
- DChatApplicationType,
6
- DConversationItem,
7
- DMessageItem,
8
- } from '../../types/chat';
4
+ import type { DMessageItem } from '../../types/chat';
9
5
  import type { IUrlMetadata } from '../../types/common';
10
- import type { PeerType } from '@droppii/openim-rn-client-sdk';
6
+ import type { ConversationItem } from '@droppii/openim-rn-client-sdk';
11
7
 
12
8
  export type DChatActionIconProvider = 'MaterialCommunityIcons' | 'DroppiiNew';
13
9
 
@@ -29,14 +25,13 @@ export interface ChatDetailHeaderProps {
29
25
  subtitle?: string;
30
26
  avatarUri?: string | null;
31
27
  avatarFullName?: string;
32
- peerType?: PeerType;
33
- applicationType?: DConversationItem['applicationType'];
34
28
  showAddMember?: boolean;
35
29
  onBack?: () => void;
36
30
  onPressSearch?: () => void;
37
31
  onPressAddMember?: () => void;
38
32
  onPressMenu?: () => void;
39
33
  onPressAvatar?: () => void;
34
+ conversationId: string;
40
35
  }
41
36
 
42
37
  export interface ChatQuickActionsRenderParams {
@@ -103,9 +98,9 @@ export interface ChatDetailProps extends Omit<
103
98
  | 'subtitle'
104
99
  | 'avatarUri'
105
100
  | 'avatarFullName'
106
- | 'peerType'
107
101
  | 'applicationType'
108
102
  | 'showAddMember'
103
+ | 'conversationId'
109
104
  > {
110
105
  conversationId: string;
111
106
  enabled?: boolean;
@@ -113,16 +108,14 @@ export interface ChatDetailProps extends Omit<
113
108
  subtitle?: string;
114
109
  avatarUri?: string | null;
115
110
  avatarFullName?: string;
116
- peerType?: PeerType;
117
- applicationType?: DChatApplicationType;
118
111
  showAddMember?: boolean;
119
- getSubtitle?: (conversation: DConversationItem) => string | undefined;
112
+ getSubtitle?: (conversation: ConversationItem) => string | undefined;
120
113
  renderChat?: (item: DMessageItem) => ReactNode;
121
114
  showQuickActions?: boolean;
122
115
  showAttachmentActions?: boolean;
123
116
  quickActions?: DChatQuickAction[];
124
117
  getQuickActions?: (
125
- conversation: DConversationItem
118
+ conversation: ConversationItem
126
119
  ) => DChatQuickAction[] | undefined;
127
120
  attachmentActions?: DChatAttachmentAction[];
128
121
  inputPlaceholder?: string;
@@ -3,6 +3,8 @@ import { StyleSheet } from 'react-native';
3
3
  import { TabView } from 'react-native-tab-view';
4
4
  import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
5
5
  import { MessagesTab } from './MessagesTab';
6
+ import { useUserStore } from '../../store';
7
+ import { LoginStatus } from '@droppii/openim-rn-client-sdk';
6
8
 
7
9
  interface InboxProps {
8
10
  onPressThread?: (item: string) => void;
@@ -15,6 +17,7 @@ type TabRoute = { key: string; title: string };
15
17
 
16
18
  const Inbox = memo(
17
19
  ({ onPressThread, onPressSearch, onPressEdit, onPressGroup }: InboxProps) => {
20
+ const connectStatus = useUserStore((state) => state.connectStatus);
18
21
  const [index, setIndex] = useState(0);
19
22
  const [routes] = useState<TabRoute[]>([
20
23
  { key: 'messages', title: 'Tin nhắn' },
@@ -22,12 +25,13 @@ const Inbox = memo(
22
25
 
23
26
  const renderScene = useCallback(
24
27
  ({ route }: { route: TabRoute }) => {
28
+ if (connectStatus !== LoginStatus.Logged) return null;
25
29
  if (route.key === 'messages') {
26
30
  return <MessagesTab onPressThread={onPressThread} />;
27
31
  }
28
32
  return null;
29
33
  },
30
- [onPressThread]
34
+ [onPressThread, connectStatus]
31
35
  );
32
36
 
33
37
  const renderTabBar = useCallback(
@@ -1,11 +1,5 @@
1
1
  import { type AxiosInstance } from 'axios';
2
- import type {
3
- DConversationItem,
4
- DConversationQueryParams,
5
- } from '../types/chat';
6
2
  import OpenIMSDK, { LoginStatus } from '@droppii/openim-rn-client-sdk';
7
- import type { BaseResponse, GetOpenIMTokenResponse } from '../types/auth';
8
- import { Platform } from 'react-native';
9
3
  import { useConversationStore } from '../store';
10
4
 
11
5
  export let apiInstance: AxiosInstance | null = null;
@@ -19,25 +13,6 @@ export namespace ChatAPI {
19
13
  return (await OpenIMSDK.getLoginStatus(operationID)) === LoginStatus.Logged;
20
14
  };
21
15
 
22
- export const login = async (
23
- instance: AxiosInstance,
24
- operationID?: string
25
- ) => {
26
- const isLoggedIn = await getLoginStatus(operationID);
27
-
28
- if (isLoggedIn) return;
29
-
30
- const response = await instance.post<BaseResponse<GetOpenIMTokenResponse>>(
31
- `/chat-service/v1/auth/token?platform=${Platform.OS.toUpperCase()}`
32
- );
33
-
34
- const token = response?.data?.data;
35
-
36
- if (token == null) return;
37
-
38
- return OpenIMSDK.login({ userID: token.id, token: token.token });
39
- };
40
-
41
16
  export const logout = async (operationID?: string) => {
42
17
  useConversationStore.getState().reset();
43
18
  return OpenIMSDK.logout(operationID);
@@ -46,17 +21,4 @@ export namespace ChatAPI {
46
21
  export const getUser = async (operationID?: string | undefined) => {
47
22
  return OpenIMSDK.getSelfUserInfo(operationID);
48
23
  };
49
-
50
- export async function queryConversations(params: DConversationQueryParams) {
51
- if (!apiInstance) {
52
- throw new Error(
53
- 'apiInstance is not initialized. Call initApiInstance() first.'
54
- );
55
- }
56
- const response = await apiInstance.post<BaseResponse<DConversationItem[]>>(
57
- '/chat-service/v1/app/conversations/query',
58
- params
59
- );
60
- return response.data;
61
- }
62
24
  }
@@ -1,32 +1,23 @@
1
1
  import { create } from 'zustand';
2
- import {
3
- sortConversation,
4
- mergeOpenIMIntoConversation,
5
- } from '../utils/conversation';
6
- import type { DConversationItem } from '../types/chat';
7
- import type {
8
- ConversationItem,
9
- MessageItem,
10
- } from '@droppii/openim-rn-client-sdk';
2
+ import { sortConversation } from '../utils/conversation';
3
+ import type { ConversationItem } from '@droppii/openim-rn-client-sdk';
11
4
 
12
5
  type ConversationID = string;
13
6
 
14
7
  export interface ConversationStore {
15
8
  currentConversationId?: ConversationID;
16
9
  setCurrentConversation(id: ConversationID): void;
17
- getCurrentConversation(): DConversationItem | undefined;
10
+ getCurrentConversation(): ConversationItem | undefined;
18
11
  list: ConversationID[];
19
- map: Record<ConversationID, DConversationItem>;
12
+ map: Record<ConversationID, ConversationItem>;
20
13
  /** Signal incremented when a truly new conversation arrives and a full refetch is needed. */
21
14
  newConversationSignal: number;
22
- updateConversations(conversations: DConversationItem[]): void;
15
+ updateConversations(conversations: ConversationItem[]): void;
23
16
  /**
24
17
  * Patches existing conversations with real-time data from OpenIM events.
25
18
  * Returns true if any conversation was not found in the store (triggers refetch).
26
19
  */
27
- mergeOpenIMConversations(conversations: ConversationItem[]): boolean;
28
20
  removeConversation(id: ConversationID): void;
29
- updateLastMessage(id: ConversationID, message: MessageItem): void;
30
21
  reset(): void;
31
22
  }
32
23
 
@@ -56,35 +47,6 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
56
47
  )
57
48
  );
58
49
  },
59
- mergeOpenIMConversations(conversations) {
60
- const currentMap = get().map;
61
- let hasUnknown = false;
62
- let changed = false;
63
-
64
- const nextMap = { ...currentMap };
65
- for (const openim of conversations) {
66
- const existing = currentMap[openim.conversationID];
67
- if (existing == null) {
68
- hasUnknown = true;
69
- continue;
70
- }
71
- nextMap[openim.conversationID] = mergeOpenIMIntoConversation(
72
- existing,
73
- openim
74
- );
75
- changed = true;
76
- }
77
-
78
- if (changed) {
79
- set(sortConversation(nextMap));
80
- }
81
-
82
- if (hasUnknown) {
83
- set((s) => ({ newConversationSignal: s.newConversationSignal + 1 }));
84
- }
85
-
86
- return hasUnknown;
87
- },
88
50
  removeConversation(id) {
89
51
  const newMap = { ...get().map };
90
52
  delete newMap[id];
@@ -94,17 +56,6 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
94
56
  list: get().list.filter((item) => item !== id),
95
57
  });
96
58
  },
97
- updateLastMessage(id, message) {
98
- const conversation = get().map[id];
99
- if (conversation == null) return;
100
-
101
- set(
102
- sortConversation({
103
- [id]: { ...conversation, lastMessage: message },
104
- ...get().map,
105
- })
106
- );
107
- },
108
59
  reset() {
109
60
  set({
110
61
  currentConversationId: undefined,
@@ -115,6 +66,6 @@ export const useConversationStore = create<ConversationStore>()((set, get) => ({
115
66
  },
116
67
  }));
117
68
 
118
- export const useConversation = (id: string): DConversationItem | undefined => {
69
+ export const useConversation = (id: string): ConversationItem | undefined => {
119
70
  return useConversationStore((state) => state.map[id]);
120
71
  };
package/src/types/auth.ts CHANGED
@@ -16,6 +16,7 @@ type Pageable = {
16
16
  export type GetOpenIMTokenResponse = {
17
17
  id: string;
18
18
  token: string;
19
+ expiresIn: number;
19
20
  };
20
21
 
21
22
  export enum SyncStatus {