@droppii-org/chat-mobile 0.2.28 → 0.2.30

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 (182) hide show
  1. package/lib/module/components/ThreadCard/MuteThread.js +86 -0
  2. package/lib/module/components/ThreadCard/MuteThread.js.map +1 -0
  3. package/lib/module/components/ThreadCard/ThreadCard.js +93 -73
  4. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  5. package/lib/module/components/flows/inputButtons/index.js +6 -3
  6. package/lib/module/components/flows/inputButtons/index.js.map +1 -1
  7. package/lib/module/config/api-endpoints.js +2 -1
  8. package/lib/module/config/api-endpoints.js.map +1 -1
  9. package/lib/module/config/uploadLimits.js +1 -1
  10. package/lib/module/context/ChatDetailContext.js +35 -2
  11. package/lib/module/context/ChatDetailContext.js.map +1 -1
  12. package/lib/module/context/index.js +1 -0
  13. package/lib/module/context/index.js.map +1 -1
  14. package/lib/module/hooks/conversation/useGetConversationDetail.js +58 -0
  15. package/lib/module/hooks/conversation/useGetConversationDetail.js.map +1 -0
  16. package/lib/module/hooks/flows/useFlow.js +1 -1
  17. package/lib/module/hooks/flows/useFlow.js.map +1 -1
  18. package/lib/module/hooks/flows/useGetConversationFlowMainMenu.js +17 -0
  19. package/lib/module/hooks/flows/useGetConversationFlowMainMenu.js.map +1 -0
  20. package/lib/module/hooks/message/useSendMessage.js +32 -7
  21. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  22. package/lib/module/hooks/query-keys.js +4 -1
  23. package/lib/module/hooks/query-keys.js.map +1 -1
  24. package/lib/module/hooks/useChatCompose.js +0 -1
  25. package/lib/module/hooks/useChatCompose.js.map +1 -1
  26. package/lib/module/hooks/useMuteConversation.js +34 -0
  27. package/lib/module/hooks/useMuteConversation.js.map +1 -0
  28. package/lib/module/index.js +4 -2
  29. package/lib/module/index.js.map +1 -1
  30. package/lib/module/screens/chat-detail/ChatAttachmentPanel.js +4 -14
  31. package/lib/module/screens/chat-detail/ChatAttachmentPanel.js.map +1 -1
  32. package/lib/module/screens/chat-detail/ChatComposer.js +26 -15
  33. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  34. package/lib/module/screens/chat-detail/ChatDetail.js +58 -71
  35. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  36. package/lib/module/screens/chat-detail/ChatDetailHeader.js +7 -10
  37. package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
  38. package/lib/module/screens/chat-detail/ChatListLegend.js +1 -1
  39. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  40. package/lib/module/screens/chat-detail/ChatLoadEarlier.js.map +1 -1
  41. package/lib/module/screens/chat-detail/ChatQuickActions.js +2 -1
  42. package/lib/module/screens/chat-detail/ChatQuickActions.js.map +1 -1
  43. package/lib/module/screens/chat-detail/constants.js +30 -24
  44. package/lib/module/screens/chat-detail/constants.js.map +1 -1
  45. package/lib/module/screens/chat-detail/index.js +2 -2
  46. package/lib/module/screens/chat-detail/index.js.map +1 -1
  47. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js +3 -4
  48. package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js.map +1 -1
  49. package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js +5 -3
  50. package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js.map +1 -1
  51. package/lib/module/screens/chat-detail/types.js +15 -1
  52. package/lib/module/screens/chat-detail/types.js.map +1 -1
  53. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js +2 -2
  54. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js.map +1 -1
  55. package/lib/module/services/endpoints.js +3 -1
  56. package/lib/module/services/endpoints.js.map +1 -1
  57. package/lib/module/translation/resources/i18n.js +15 -1
  58. package/lib/module/translation/resources/i18n.js.map +1 -1
  59. package/lib/module/types/chat.js +9 -0
  60. package/lib/module/types/chat.js.map +1 -1
  61. package/lib/module/types/flows.js +5 -0
  62. package/lib/module/types/flows.js.map +1 -1
  63. package/lib/module/utils/device.js +1 -1
  64. package/lib/module/utils/device.js.map +1 -1
  65. package/lib/module/utils/flows.js +5 -0
  66. package/lib/module/utils/flows.js.map +1 -1
  67. package/lib/module/utils/imageUtils.js +0 -3
  68. package/lib/module/utils/imageUtils.js.map +1 -1
  69. package/lib/module/utils/messageUtils.js +5 -19
  70. package/lib/module/utils/messageUtils.js.map +1 -1
  71. package/lib/typescript/src/components/ThreadCard/MuteThread.d.ts +10 -0
  72. package/lib/typescript/src/components/ThreadCard/MuteThread.d.ts.map +1 -0
  73. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  74. package/lib/typescript/src/components/flows/inputButtons/index.d.ts.map +1 -1
  75. package/lib/typescript/src/config/api-endpoints.d.ts +1 -0
  76. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
  77. package/lib/typescript/src/context/ChatDetailContext.d.ts +1 -1
  78. package/lib/typescript/src/context/ChatDetailContext.d.ts.map +1 -1
  79. package/lib/typescript/src/context/index.d.ts +1 -0
  80. package/lib/typescript/src/context/index.d.ts.map +1 -1
  81. package/lib/typescript/src/hooks/conversation/useGetConversationDetail.d.ts +17 -0
  82. package/lib/typescript/src/hooks/conversation/useGetConversationDetail.d.ts.map +1 -0
  83. package/lib/typescript/src/hooks/flows/useGetConversationFlowMainMenu.d.ts +3 -0
  84. package/lib/typescript/src/hooks/flows/useGetConversationFlowMainMenu.d.ts.map +1 -0
  85. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +5 -4
  86. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  87. package/lib/typescript/src/hooks/query-keys.d.ts +3 -0
  88. package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
  89. package/lib/typescript/src/hooks/useMuteConversation.d.ts +6 -0
  90. package/lib/typescript/src/hooks/useMuteConversation.d.ts.map +1 -0
  91. package/lib/typescript/src/index.d.ts +6 -4
  92. package/lib/typescript/src/index.d.ts.map +1 -1
  93. package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts +2 -2
  94. package/lib/typescript/src/screens/chat-detail/ChatAttachmentPanel.d.ts.map +1 -1
  95. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +3 -1
  96. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  97. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  98. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  99. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
  100. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
  101. package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts +3 -2
  102. package/lib/typescript/src/screens/chat-detail/ChatLoadEarlier.d.ts.map +1 -1
  103. package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts +1 -1
  104. package/lib/typescript/src/screens/chat-detail/ChatQuickActions.d.ts.map +1 -1
  105. package/lib/typescript/src/screens/chat-detail/constants.d.ts +3 -3
  106. package/lib/typescript/src/screens/chat-detail/constants.d.ts.map +1 -1
  107. package/lib/typescript/src/screens/chat-detail/index.d.ts +3 -3
  108. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
  109. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts +1 -1
  110. package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts.map +1 -1
  111. package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts +0 -2
  112. package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts.map +1 -1
  113. package/lib/typescript/src/screens/chat-detail/types.d.ts +28 -19
  114. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  115. package/lib/typescript/src/services/endpoints.d.ts +2 -0
  116. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  117. package/lib/typescript/src/store/conversation.d.ts +1 -1
  118. package/lib/typescript/src/store/conversation.d.ts.map +1 -1
  119. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  120. package/lib/typescript/src/types/chat.d.ts +26 -2
  121. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  122. package/lib/typescript/src/types/flows.d.ts +3 -0
  123. package/lib/typescript/src/types/flows.d.ts.map +1 -1
  124. package/lib/typescript/src/utils/flows.d.ts +2 -0
  125. package/lib/typescript/src/utils/flows.d.ts.map +1 -1
  126. package/lib/typescript/src/utils/imageUtils.d.ts +0 -1
  127. package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -1
  128. package/lib/typescript/src/utils/messageUtils.d.ts +1 -1
  129. package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
  130. package/package.json +3 -5
  131. package/src/components/ThreadCard/MuteThread.tsx +100 -0
  132. package/src/components/ThreadCard/ThreadCard.tsx +104 -71
  133. package/src/components/flows/inputButtons/index.tsx +11 -3
  134. package/src/config/api-endpoints.ts +2 -0
  135. package/src/config/uploadLimits.ts +1 -1
  136. package/src/context/ChatDetailContext.tsx +57 -4
  137. package/src/context/index.ts +1 -0
  138. package/src/hooks/conversation/useGetConversationDetail.ts +78 -0
  139. package/src/hooks/flows/useFlow.ts +1 -1
  140. package/src/hooks/flows/useGetConversationFlowMainMenu.ts +19 -0
  141. package/src/hooks/message/useSendMessage.ts +43 -14
  142. package/src/hooks/query-keys.ts +6 -0
  143. package/src/hooks/useChatCompose.ts +1 -1
  144. package/src/hooks/useMuteConversation.ts +44 -0
  145. package/src/index.tsx +15 -6
  146. package/src/screens/chat-detail/ChatAttachmentPanel.tsx +8 -25
  147. package/src/screens/chat-detail/ChatComposer.tsx +26 -21
  148. package/src/screens/chat-detail/ChatDetail.tsx +61 -77
  149. package/src/screens/chat-detail/ChatDetailHeader.tsx +7 -18
  150. package/src/screens/chat-detail/ChatListLegend.tsx +1 -1
  151. package/src/screens/chat-detail/ChatLoadEarlier.tsx +1 -2
  152. package/src/screens/chat-detail/ChatQuickActions.tsx +6 -2
  153. package/src/screens/chat-detail/constants.ts +33 -27
  154. package/src/screens/chat-detail/index.ts +5 -8
  155. package/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.tsx +2 -2
  156. package/src/screens/chat-detail/messageToolbox/messageToolbox.utils.ts +4 -4
  157. package/src/screens/chat-detail/types.ts +30 -28
  158. package/src/services/attachmentHandlers/imageAttachmentHandler.ts +2 -2
  159. package/src/services/endpoints.ts +4 -0
  160. package/src/store/conversation.ts +1 -1
  161. package/src/translation/resources/i18n.ts +15 -0
  162. package/src/types/chat.ts +31 -1
  163. package/src/types/flows.ts +4 -0
  164. package/src/utils/device.ts +1 -1
  165. package/src/utils/flows.ts +7 -0
  166. package/src/utils/imageUtils.ts +0 -4
  167. package/src/utils/messageUtils.ts +7 -22
  168. package/lib/module/screens/chat-detail/ChatDay.js +0 -65
  169. package/lib/module/screens/chat-detail/ChatDay.js.map +0 -1
  170. package/lib/module/types/message.js +0 -17
  171. package/lib/module/types/message.js.map +0 -1
  172. package/lib/module/utils/resolveMessageType.js +0 -47
  173. package/lib/module/utils/resolveMessageType.js.map +0 -1
  174. package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts +0 -3
  175. package/lib/typescript/src/screens/chat-detail/ChatDay.d.ts.map +0 -1
  176. package/lib/typescript/src/types/message.d.ts +0 -16
  177. package/lib/typescript/src/types/message.d.ts.map +0 -1
  178. package/lib/typescript/src/utils/resolveMessageType.d.ts +0 -4
  179. package/lib/typescript/src/utils/resolveMessageType.d.ts.map +0 -1
  180. package/src/screens/chat-detail/ChatDay.tsx +0 -73
  181. package/src/types/message.ts +0 -18
  182. package/src/utils/resolveMessageType.ts +0 -51
@@ -1,5 +1,7 @@
1
1
  import { memo, useCallback, useMemo } from 'react';
2
2
  import { type GestureResponderEvent } from 'react-native';
3
+ import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
4
+ import type { SwipeableMethods } from 'react-native-gesture-handler/ReanimatedSwipeable';
3
5
  import { KContainer, KImage, KLabel, KColors } from '@droppii/libs';
4
6
  import { AvatarSection } from './AvatarSection';
5
7
  import { NamePrefixIcon } from './NamePrefixIcon';
@@ -7,7 +9,11 @@ import { UnreadBadge } from './UnreadBadge';
7
9
  import { formatTimestamp } from '../../utils/dateTimeUtils';
8
10
  import { getLastMessageText } from '../../utils/messageUtils';
9
11
  import { useConversation, useUserStore } from '../../store';
10
- import type { MessageItem } from '@droppii/openim-rn-client-sdk';
12
+ import {
13
+ MessageReceiveOptType,
14
+ type MessageItem,
15
+ } from '@droppii/openim-rn-client-sdk';
16
+ import { MuteThread } from './MuteThread';
11
17
 
12
18
  interface ThreadCardProps {
13
19
  item: string;
@@ -15,18 +21,19 @@ interface ThreadCardProps {
15
21
  }
16
22
 
17
23
  const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
18
- const consversation = useConversation(item);
24
+ const conversation = useConversation(item);
19
25
 
20
26
  const {
21
27
  peerType,
22
28
  showName,
23
29
  faceURL,
24
30
  unreadCount = 0,
25
- recvMsgOpt,
31
+ recvMsgOpt = MessageReceiveOptType.Normal,
26
32
  isPinned,
33
+ isNotInGroup,
27
34
  latestMsgSendTime,
28
35
  latestMsg,
29
- } = consversation || {};
36
+ } = conversation || {};
30
37
 
31
38
  const latestMsgData: MessageItem = useMemo(() => {
32
39
  let result = null;
@@ -40,7 +47,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
40
47
 
41
48
  const fullName = showName ?? '';
42
49
  const hasUnread = unreadCount > 0;
43
- const isMuted = (recvMsgOpt ?? 0) !== 0;
50
+ const isMuted = recvMsgOpt !== MessageReceiveOptType.Normal;
44
51
  const timestamp = formatTimestamp(
45
52
  latestMsgSendTime ?? latestMsgData?.sendTime ?? null
46
53
  );
@@ -48,7 +55,7 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
48
55
  const lastMessageText = getLastMessageText(
49
56
  latestMsgData,
50
57
  currentUserId,
51
- consversation
58
+ conversation
52
59
  );
53
60
 
54
61
  const handlePress = useCallback(
@@ -58,79 +65,105 @@ const ThreadCard = memo(({ item, onPress }: ThreadCardProps) => {
58
65
  [item, onPress]
59
66
  );
60
67
 
68
+ const renderRightActions = useCallback(
69
+ (
70
+ _progress: unknown,
71
+ _translation: unknown,
72
+ swipeableMethods: SwipeableMethods
73
+ ) => (
74
+ <>
75
+ <KContainer.VisibleView visible={!isNotInGroup}>
76
+ <MuteThread
77
+ conversation={conversation}
78
+ isMuted={isMuted}
79
+ swipeableMethods={swipeableMethods}
80
+ />
81
+ </KContainer.VisibleView>
82
+ </>
83
+ ),
84
+ [isMuted, conversation, isNotInGroup]
85
+ );
86
+
61
87
  return (
62
- <KContainer.Touchable
63
- row
64
- alignItems="center"
65
- paddingH="0.75rem"
66
- paddingV="0.75rem"
67
- onPress={handlePress}
68
- activeOpacity={0.7}
88
+ <ReanimatedSwipeable
89
+ friction={2}
90
+ rightThreshold={40}
91
+ renderRightActions={renderRightActions}
69
92
  >
70
- <AvatarSection
71
- avatar={faceURL ?? null}
72
- fullName={fullName}
73
- peerType={peerType}
74
- />
93
+ <KContainer.Touchable
94
+ row
95
+ alignItems="center"
96
+ paddingH="0.75rem"
97
+ paddingV="0.75rem"
98
+ onPress={handlePress}
99
+ activeOpacity={1}
100
+ background={KColors.white}
101
+ >
102
+ <AvatarSection
103
+ avatar={faceURL ?? null}
104
+ fullName={fullName}
105
+ peerType={peerType}
106
+ />
75
107
 
76
- {/* Chat-Content */}
77
- <KContainer.View flex marginL="0.5rem" paddingB={2}>
78
- {/* Row-Title */}
79
- <KContainer.View row alignItems="center">
80
- <NamePrefixIcon peerType={peerType} />
81
- <KLabel.Text
82
- typo={hasUnread ? 'TextMdBold' : 'TextMdNormal'}
83
- numberOfLines={1}
84
- color={KColors.black}
85
- flex
86
- >
87
- {fullName}
88
- </KLabel.Text>
89
- <KContainer.VisibleView visible={isMuted}>
90
- <KContainer.View marginL="0.25rem">
91
- <KImage.VectorIcons
92
- name="bell-off"
93
- provider="MaterialCommunityIcons"
94
- size={16}
95
- color={KColors.palette.gray.w500}
96
- />
97
- </KContainer.View>
98
- </KContainer.VisibleView>
99
- <KContainer.VisibleView visible={Boolean(timestamp)}>
108
+ {/* Chat-Content */}
109
+ <KContainer.View flex marginL="0.5rem" paddingB={2}>
110
+ {/* Row-Title */}
111
+ <KContainer.View row alignItems="center">
112
+ <NamePrefixIcon peerType={peerType} />
100
113
  <KLabel.Text
101
- typo="TextXsNormal"
102
- color={KColors.palette.gray.w400}
103
- marginL="0.25rem"
114
+ typo={hasUnread ? 'TextMdBold' : 'TextMdNormal'}
115
+ numberOfLines={1}
116
+ color={KColors.black}
117
+ flex
104
118
  >
105
- {timestamp}
119
+ {fullName}
106
120
  </KLabel.Text>
107
- </KContainer.VisibleView>
108
- </KContainer.View>
109
-
110
- {/* In-Text */}
111
- <KContainer.View row alignItems="center">
112
- <KLabel.Text
113
- typo={hasUnread ? 'TextNmMedium' : 'TextNmNormal'}
114
- color={hasUnread ? KColors.black : KColors.gray.normal}
115
- numberOfLines={1}
116
- flex
117
- >
118
- {lastMessageText}
119
- </KLabel.Text>
120
- <KContainer.VisibleView visible={Boolean(isPinned)}>
121
- <KContainer.View marginL="0.5rem">
122
- <KImage.VectorIcons
123
- name="pin-outline"
124
- provider="MaterialCommunityIcons"
125
- size={14}
121
+ <KContainer.VisibleView visible={isMuted}>
122
+ <KContainer.View marginL="0.25rem">
123
+ <KImage.VectorIcons
124
+ name="bell-off"
125
+ provider="MaterialCommunityIcons"
126
+ size={16}
127
+ color={KColors.palette.gray.w500}
128
+ />
129
+ </KContainer.View>
130
+ </KContainer.VisibleView>
131
+ <KContainer.VisibleView visible={Boolean(timestamp)}>
132
+ <KLabel.Text
133
+ typo="TextXsNormal"
126
134
  color={KColors.palette.gray.w400}
127
- />
128
- </KContainer.View>
129
- </KContainer.VisibleView>
130
- <UnreadBadge count={unreadCount} />
135
+ marginL="0.25rem"
136
+ >
137
+ {timestamp}
138
+ </KLabel.Text>
139
+ </KContainer.VisibleView>
140
+ </KContainer.View>
141
+
142
+ {/* In-Text */}
143
+ <KContainer.View row alignItems="center">
144
+ <KLabel.Text
145
+ typo={hasUnread ? 'TextNmMedium' : 'TextNmNormal'}
146
+ color={hasUnread ? KColors.black : KColors.gray.normal}
147
+ numberOfLines={1}
148
+ flex
149
+ >
150
+ {lastMessageText}
151
+ </KLabel.Text>
152
+ <KContainer.VisibleView visible={Boolean(isPinned)}>
153
+ <KContainer.View marginL="0.5rem">
154
+ <KImage.VectorIcons
155
+ name="pin-outline"
156
+ provider="MaterialCommunityIcons"
157
+ size={14}
158
+ color={KColors.palette.gray.w400}
159
+ />
160
+ </KContainer.View>
161
+ </KContainer.VisibleView>
162
+ <UnreadBadge count={unreadCount} />
163
+ </KContainer.View>
131
164
  </KContainer.View>
132
- </KContainer.View>
133
- </KContainer.Touchable>
165
+ </KContainer.Touchable>
166
+ </ReanimatedSwipeable>
134
167
  );
135
168
  });
136
169
 
@@ -6,7 +6,7 @@ import {
6
6
  KRadiusValue,
7
7
  KSpacingValue,
8
8
  } from '@droppii/libs';
9
- import { memo, useCallback } from 'react';
9
+ import { memo, useCallback, useMemo } from 'react';
10
10
  import { StyleSheet } from 'react-native';
11
11
  import { messageStyles } from '../../messages/styles';
12
12
  import type {
@@ -14,7 +14,7 @@ import type {
14
14
  InputButtonItem,
15
15
  IRunConversationFlowPayload,
16
16
  } from '../../../types/flows';
17
- import { DEFAULT_BUTTON_ID } from '../../../utils/flows';
17
+ import { DEFAULT_BUTTON_ID, isContextButton } from '../../../utils/flows';
18
18
  import { trans } from '../../../translation';
19
19
 
20
20
  interface IInputButtonFlow {
@@ -26,6 +26,14 @@ interface IInputButtonFlow {
26
26
  export const InputButtonFlow = memo((props: IInputButtonFlow) => {
27
27
  const { nodeData, onResetFlow, onRunFlow } = props || {};
28
28
 
29
+ const buttonList = useMemo(() => {
30
+ return (
31
+ nodeData?.data?.buttons?.filter(
32
+ (n) => !isContextButton(n?.label || '')
33
+ ) || []
34
+ );
35
+ }, [nodeData]);
36
+
29
37
  const onPressItem = useCallback(
30
38
  (item: InputButtonItem) => {
31
39
  if (item.id === DEFAULT_BUTTON_ID) {
@@ -71,7 +79,7 @@ export const InputButtonFlow = memo((props: IInputButtonFlow) => {
71
79
  return (
72
80
  <KContainer.View style={styles.bubble}>
73
81
  <KContainer.View style={styles.buttonsContainer}>
74
- {nodeData?.data?.buttons?.map((item) => renderButtonItem(item))}
82
+ {buttonList?.map((item) => renderButtonItem(item))}
75
83
  </KContainer.View>
76
84
  </KContainer.View>
77
85
  );
@@ -15,12 +15,14 @@ export interface ApiEndpoints {
15
15
  conversationFlow: string;
16
16
  // Image upload service (relative path - full URL constructed in imageUpload service)
17
17
  imageUpload: string;
18
+ bot: string;
18
19
  }
19
20
 
20
21
  const DEFAULT_ENDPOINTS: ApiEndpoints = {
21
22
  urlMetadata: '/chat-service/v1/utils/url-metadata',
22
23
  conversationFlow: '/chat-service/v1/app/conversations',
23
24
  imageUpload: '/uploader-service/v1/uploader/permanently',
25
+ bot: '/chat-service/v1/app/bots/',
24
26
  };
25
27
 
26
28
  let endpoints: ApiEndpoints = { ...DEFAULT_ENDPOINTS };
@@ -3,7 +3,7 @@ const MB = 1024 * 1024;
3
3
  export const UPLOAD_LIMITS = {
4
4
  image: 15 * MB,
5
5
  video: 200 * MB,
6
- file: 50 * MB,
6
+ file: 20 * MB,
7
7
  } as const;
8
8
 
9
9
  // Max concurrent thumbnail generations and file uploads.
@@ -6,20 +6,32 @@ import {
6
6
  useMemo,
7
7
  useState,
8
8
  } from 'react';
9
- import type {
10
- ChatDetailContextType,
11
- ChatDetailProviderProps,
9
+ import {
10
+ DFlowState,
11
+ type ChatDetailContextType,
12
+ type ChatDetailProviderProps,
12
13
  } from '../types/chat';
13
14
  import { fechtCurrentConversationFlow } from '../hooks/flows/useCurrentConversationFlow';
14
15
  import { useConversationStore } from '../store';
15
16
  import { PeerType } from '@droppii/openim-rn-client-sdk';
16
- import type { IConversationFlow, IConversationFlowStep } from '../types/flows';
17
+ import {
18
+ DFlowContext,
19
+ NodeTypes,
20
+ type IConversationFlow,
21
+ type IConversationFlowStep,
22
+ } from '../types/flows';
23
+ import { useGetConversationFlowMainMenu } from '../hooks/flows/useGetConversationFlowMainMenu';
24
+ import { useRunFlow } from '../hooks/flows/useFlow';
25
+ import { CONTEXT_BUTTON_PREFIX, isContextButton } from '../utils/flows';
17
26
 
18
27
  export const ChatDetailContext = createContext<ChatDetailContextType>({
19
28
  isBotConversation: false,
20
29
  conversationFlow: undefined,
21
30
  refetchConversationFlow: () => {},
22
31
  setConverationFlowStep: () => {},
32
+ flowState: undefined,
33
+ runButtonContext: () => Promise.resolve(false),
34
+ conversation: undefined,
23
35
  });
24
36
 
25
37
  export const useChatDetailContext = (): ChatDetailContextType => {
@@ -43,10 +55,26 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
43
55
  IConversationFlow | undefined
44
56
  >(undefined);
45
57
 
58
+ const flowState = useMemo(() => {
59
+ return conversationFlow
60
+ ? conversationFlow?.flowCurrentStep?.data?.children?.some(
61
+ (n) => n.type === NodeTypes.LogicCsTeam
62
+ )
63
+ ? DFlowState.CS
64
+ : DFlowState.BOT
65
+ : undefined;
66
+ }, [conversationFlow]);
67
+
46
68
  const isBotConversation = useMemo(() => {
47
69
  return conversation?.peerType === PeerType.Bot;
48
70
  }, [conversation]);
49
71
 
72
+ const { data: conversationFlowMainMenu } = useGetConversationFlowMainMenu(
73
+ isBotConversation ? conversationId : ''
74
+ );
75
+
76
+ const { mutateAsync: runFlow } = useRunFlow();
77
+
50
78
  const setConverationFlowStep = useCallback((step?: IConversationFlowStep) => {
51
79
  setConverationFlow(
52
80
  (prev) =>
@@ -64,6 +92,28 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
64
92
  }
65
93
  }, [conversationId]);
66
94
 
95
+ const runButtonContext = useCallback(
96
+ async (stringContext: DFlowContext) => {
97
+ const actionItem = conversationFlowMainMenu?.find(
98
+ (f) =>
99
+ isContextButton(f.label) &&
100
+ f.label.trim().slice(CONTEXT_BUTTON_PREFIX.length) ===
101
+ stringContext?.trim()
102
+ );
103
+ if (actionItem) {
104
+ const res = await runFlow({
105
+ actionId: actionItem.id,
106
+ groupId: '',
107
+ childId: '',
108
+ });
109
+ setConverationFlowStep(res?.flowNextStep || undefined);
110
+ return res ? true : false;
111
+ }
112
+ return false;
113
+ },
114
+ [runFlow, conversationFlowMainMenu, setConverationFlowStep]
115
+ );
116
+
67
117
  useEffect(() => {
68
118
  if (!!conversationId && !!isBotConversation) {
69
119
  getConversationFlow();
@@ -77,6 +127,9 @@ export const ChatDetailProvider = ({ children }: ChatDetailProviderProps) => {
77
127
  refetchConversationFlow: () => {},
78
128
  setConverationFlowStep,
79
129
  isBotConversation,
130
+ flowState,
131
+ runButtonContext,
132
+ conversation,
80
133
  }}
81
134
  >
82
135
  {children}
@@ -1 +1,2 @@
1
1
  export * from './ChatContext';
2
+ export * from './ChatDetailContext';
@@ -0,0 +1,78 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import OpenIMSDK, { SessionType } from '@droppii/openim-rn-client-sdk';
3
+ import { useConversationStore } from '../../store/conversation';
4
+ import { apiInstance } from '../../services/apis';
5
+ import type { BaseResponse } from '../../types/auth';
6
+ import { conversationQueryKeys } from '../query-keys';
7
+ import { ENDPOINTS } from '../../services/endpoints';
8
+
9
+ interface UseGetConversationDetailParams {
10
+ targetConversationId?: string;
11
+ targetUserId?: string;
12
+ targetGroupId?: string;
13
+ targetBotId?: string;
14
+ }
15
+
16
+ interface UseGetConversationDetailResult {
17
+ conversationId: string;
18
+ isLoading: boolean;
19
+ }
20
+
21
+ const useBotConversationData = (botId?: string) =>
22
+ useQuery({
23
+ queryKey: conversationQueryKeys.detailByBotId(botId || ''),
24
+ queryFn: async () => {
25
+ const res = await apiInstance?.get<
26
+ BaseResponse<{ groupId: string; conversationId: string }>
27
+ >(ENDPOINTS.chatService.getConversationByBotId(botId || ''));
28
+ return res?.data?.data || undefined;
29
+ },
30
+ enabled: !!botId,
31
+ });
32
+
33
+ export const useGetOneConversation = ({
34
+ targetUserId,
35
+ targetGroupId,
36
+ }: {
37
+ targetUserId?: string;
38
+ targetGroupId?: string;
39
+ }) => {
40
+ return useQuery({
41
+ queryKey: conversationQueryKeys.detailBySourceId(
42
+ targetUserId || targetGroupId || ''
43
+ ),
44
+ queryFn: async () => {
45
+ const res = await OpenIMSDK.getOneConversation({
46
+ sourceID: targetUserId || targetGroupId || '',
47
+ sessionType: targetUserId ? SessionType.Single : SessionType.Group,
48
+ });
49
+ if (res?.conversationID) {
50
+ useConversationStore.getState().updateConversations([res]);
51
+ }
52
+ return res || undefined;
53
+ },
54
+ enabled: !!targetUserId || !!targetGroupId,
55
+ });
56
+ };
57
+
58
+ export const useGetConversationDetail = ({
59
+ targetConversationId,
60
+ targetUserId,
61
+ targetGroupId,
62
+ targetBotId,
63
+ }: UseGetConversationDetailParams): UseGetConversationDetailResult => {
64
+ const { data: botData, isLoading: botIdLoading } =
65
+ useBotConversationData(targetBotId);
66
+
67
+ const { data: conversationData, isLoading: conversationLoading } =
68
+ useGetOneConversation({
69
+ targetUserId,
70
+ targetGroupId: targetGroupId || botData?.groupId,
71
+ });
72
+
73
+ return {
74
+ conversationId:
75
+ targetConversationId || conversationData?.conversationID || '',
76
+ isLoading: botIdLoading || conversationLoading,
77
+ };
78
+ };
@@ -20,7 +20,7 @@ export const useResetFlow = () => {
20
20
  const { sendMessage } = useSendMessage();
21
21
  const conversationFlow = useChatDetailContext().conversationFlow;
22
22
  return useMutation({
23
- mutationKey: conversationQueryKeys.flow(conversationId),
23
+ mutationKey: conversationQueryKeys.resetFlow(conversationId),
24
24
  mutationFn: async () => {
25
25
  await sendMessage({
26
26
  plainText: trans('restart'),
@@ -0,0 +1,19 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { conversationQueryKeys } from '../query-keys';
3
+ import { apiInstance } from '../../services/apis';
4
+ import type { BaseResponse } from '../../types/auth';
5
+ import type { InputButtonItem } from '../../types/flows';
6
+ import { ENDPOINTS } from '../../services/endpoints';
7
+
8
+ export const useGetConversationFlowMainMenu = (conversationId?: string) => {
9
+ return useQuery({
10
+ queryKey: [conversationQueryKeys.flowMainMenu(conversationId || '')],
11
+ queryFn: async () => {
12
+ const res = await apiInstance?.get<BaseResponse<InputButtonItem[]>>(
13
+ ENDPOINTS.chatService.conversationFlowMainMenu(conversationId || '')
14
+ );
15
+ return res?.data?.data || undefined;
16
+ },
17
+ enabled: !!conversationId,
18
+ });
19
+ };
@@ -1,14 +1,22 @@
1
1
  import { useCallback } from 'react';
2
- import type { UploadFileItem } from 'react-native-fs';
3
2
  import { useConversationStore } from '../../store/conversation';
4
- import type { MessageItem, OfflinePush } from '@droppii/openim-rn-client-sdk';
3
+ import type {
4
+ CustomMsgParams,
5
+ MessageItem,
6
+ OfflinePush,
7
+ } from '@droppii/openim-rn-client-sdk';
5
8
  import { MessageStatus, MessageType } from '@droppii/openim-rn-client-sdk';
6
9
  import { extractUrls } from '../../utils/url';
7
10
  import OpenIMSDK from '@droppii/openim-rn-client-sdk';
8
- import type { IMessageItemEx } from '../../types/chat';
11
+ import {
12
+ CustomMessageType,
13
+ type CustomMessagePayload,
14
+ type IMessageItemEx,
15
+ } from '../../types/chat';
9
16
  import { useChatContext } from '../../context';
10
17
  import { generateContentBasedOnMessageType } from '../../utils/conversation';
11
18
  import { markMessageRevoked, useMessageStore } from '../../store/message';
19
+ import uuid from 'react-native-uuid';
12
20
 
13
21
  import type { IUrlMetadata } from '../../types/common';
14
22
  import type { IRunConversationFlowPayload } from '../../types/flows';
@@ -63,14 +71,14 @@ export const useSendMessage = () => {
63
71
  const sendMessage = useCallback(
64
72
  async ({
65
73
  plainText,
66
- files,
67
74
  urlMetadata,
68
75
  flow,
76
+ customMessage,
69
77
  }: {
70
78
  plainText?: string;
71
- files?: UploadFileItem[];
72
79
  urlMetadata?: IUrlMetadata;
73
80
  flow?: IRunConversationFlowPayload;
81
+ customMessage?: CustomMessagePayload;
74
82
  }) => {
75
83
  const { userID: recvID, groupID } =
76
84
  useConversationStore.getState().getCurrentConversation() || {};
@@ -78,13 +86,10 @@ export const useSendMessage = () => {
78
86
 
79
87
  const messageList: MessageItem[] = [];
80
88
 
81
- for (const file of files || []) {
82
- if (!file) continue;
83
- }
84
-
85
89
  const hasText = !!plainText && plainText.trim() !== '';
86
90
  const hasPreview = !!urlMetadata?.url;
87
91
 
92
+ //text msg
88
93
  if (hasText || hasPreview) {
89
94
  const urls = hasText ? extractUrls(plainText) : [];
90
95
  const isUrlMessage = urls.length > 0 || hasPreview;
@@ -100,6 +105,21 @@ export const useSendMessage = () => {
100
105
  messageList.push(message);
101
106
  }
102
107
 
108
+ //custom msg
109
+ if (customMessage) {
110
+ let customElem: CustomMsgParams = {
111
+ data: JSON.stringify(customMessage.data),
112
+ extension: customMessage.type,
113
+ description:
114
+ customMessage?.type === CustomMessageType.Order
115
+ ? customMessage?.data?.orderCode
116
+ : '',
117
+ };
118
+ const message = await createCustomMessage(customElem);
119
+ if (!message) return;
120
+ messageList.push(message);
121
+ }
122
+
103
123
  for (const message of messageList) {
104
124
  const extendMessageInfo: IMessageItemEx = {
105
125
  applicationType,
@@ -212,10 +232,7 @@ const generateOfflinePushInfo = (
212
232
  };
213
233
 
214
234
  export const createTextMessage = async (text: string) => {
215
- let textMessage = await OpenIMSDK.createTextMessage(
216
- text,
217
- new Date().getTime().toString()
218
- )
235
+ let textMessage = await OpenIMSDK.createTextMessage(text, uuid.v4())
219
236
  .then((data) => {
220
237
  return data;
221
238
  })
@@ -230,7 +247,7 @@ export const createUrlTextMessage = async (text: string, urls: string[]) => {
230
247
  let urlTextMessage = await OpenIMSDK.createUrlTextMessage(
231
248
  text,
232
249
  urls,
233
- new Date().getTime().toString()
250
+ uuid.v4()
234
251
  )
235
252
  .then((data) => {
236
253
  return data;
@@ -241,3 +258,15 @@ export const createUrlTextMessage = async (text: string, urls: string[]) => {
241
258
  });
242
259
  return urlTextMessage;
243
260
  };
261
+
262
+ export const createCustomMessage = async (params: CustomMsgParams) => {
263
+ let customMessage = await OpenIMSDK.createCustomMessage(params, uuid.v4())
264
+ .then((data) => {
265
+ return data;
266
+ })
267
+ .catch(({ errCode, errMsg }) => {
268
+ console.error('createUrlTextMessage', errCode, errMsg);
269
+ return null;
270
+ });
271
+ return customMessage;
272
+ };
@@ -12,11 +12,17 @@ export const conversationQueryKeys = {
12
12
  detail: (conversationId: string) =>
13
13
  [...conversationQueryKeys.details(), conversationId] as const,
14
14
  flow: (conversationId: string) => ['conversationFlow', conversationId],
15
+ flowMainMenu: (conversationId: string) => [
16
+ 'conversationFlowMainMenu',
17
+ conversationId,
18
+ ],
15
19
  resetFlow: (conversationId: string) => [
16
20
  'resetConversationFlow',
17
21
  conversationId,
18
22
  ],
19
23
  runFlow: (conversationId: string) => ['runConversationFlow', conversationId],
24
+ detailByBotId: (botId: string) => ['conversationByBotId', botId],
25
+ detailBySourceId: (sourceId: string) => ['detailBySourceId', sourceId],
20
26
  };
21
27
 
22
28
  export type ConversationQueryKeys = typeof conversationQueryKeys;
@@ -151,7 +151,7 @@ export function useChatCompose() {
151
151
  // Clear composer immediately — message is already in chat list with Sending status.
152
152
  // User retries from the bubble, not the composer.
153
153
  setText('');
154
- await sendMessage({ files: [], plainText, urlMetadata: urlMeta });
154
+ await sendMessage({ plainText, urlMetadata: urlMeta });
155
155
  }
156
156
  }, [
157
157
  imageAttachment,