react-native-chatbot-ai 0.1.22 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/components/Drawer/DeleteSessionPopup.js +5 -5
- package/lib/module/components/Drawer/DeleteSessionPopup.js.map +1 -1
- package/lib/module/components/Drawer/RenameSessionPopup.js +24 -21
- package/lib/module/components/Drawer/RenameSessionPopup.js.map +1 -1
- package/lib/module/components/Drawer/SessionItem.js +6 -5
- package/lib/module/components/Drawer/SessionItem.js.map +1 -1
- package/lib/module/components/Drawer/SessionList.js +5 -4
- package/lib/module/components/Drawer/SessionList.js.map +1 -1
- package/lib/module/components/Drawer/SessionOptionsBottomSheet.js +7 -6
- package/lib/module/components/Drawer/SessionOptionsBottomSheet.js.map +1 -1
- package/lib/module/components/Drawer/ShareSessionPopup.js +6 -6
- package/lib/module/components/Drawer/ShareSessionPopup.js.map +1 -1
- package/lib/module/components/chat/item/MessageActionsBar.js +8 -8
- package/lib/module/components/chat/item/MessageActionsBar.js.map +1 -1
- package/lib/module/hooks/messageActions/useAudioPlayer.js +105 -20
- package/lib/module/hooks/messageActions/useAudioPlayer.js.map +1 -1
- package/lib/typescript/src/components/Drawer/SessionItem.d.ts.map +1 -1
- package/lib/typescript/src/components/Drawer/SessionList.d.ts.map +1 -1
- package/lib/typescript/src/components/Drawer/SessionOptionsBottomSheet.d.ts.map +1 -1
- package/lib/typescript/src/hooks/messageActions/useAudioPlayer.d.ts +1 -0
- package/lib/typescript/src/hooks/messageActions/useAudioPlayer.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/Drawer/DeleteSessionPopup.tsx +5 -5
- package/src/components/Drawer/RenameSessionPopup.tsx +38 -38
- package/src/components/Drawer/SessionItem.tsx +5 -4
- package/src/components/Drawer/SessionList.tsx +8 -3
- package/src/components/Drawer/SessionOptionsBottomSheet.tsx +6 -5
- package/src/components/Drawer/ShareSessionPopup.tsx +6 -6
- package/src/components/chat/item/MessageActionsBar.tsx +8 -8
- package/src/hooks/messageActions/useAudioPlayer.ts +112 -18
|
@@ -159,8 +159,8 @@ const MessageActionsBar = ({
|
|
|
159
159
|
]}
|
|
160
160
|
>
|
|
161
161
|
<KImage.VectorIcons
|
|
162
|
-
name="like-
|
|
163
|
-
size={
|
|
162
|
+
name="thumb-like-o"
|
|
163
|
+
size={20}
|
|
164
164
|
color={
|
|
165
165
|
isLikeActive ? KColors.primary.normal : KColors.gray.light
|
|
166
166
|
}
|
|
@@ -215,8 +215,8 @@ const MessageActionsBar = ({
|
|
|
215
215
|
]}
|
|
216
216
|
>
|
|
217
217
|
<KImage.VectorIcons
|
|
218
|
-
name="
|
|
219
|
-
size={
|
|
218
|
+
name="thumb-dislike-o"
|
|
219
|
+
size={20}
|
|
220
220
|
color={
|
|
221
221
|
isDislikeActive
|
|
222
222
|
? KColors.primary.normal
|
|
@@ -253,7 +253,7 @@ const MessageActionsBar = ({
|
|
|
253
253
|
{/* Audio Button */}
|
|
254
254
|
<ActionButton
|
|
255
255
|
icon={audioButtonProps.icon}
|
|
256
|
-
iconSize={
|
|
256
|
+
iconSize={20}
|
|
257
257
|
iconColor={audioButtonProps.iconColor}
|
|
258
258
|
isActive={audioButtonProps.isActive}
|
|
259
259
|
isLoading={isAudioLoading}
|
|
@@ -264,7 +264,7 @@ const MessageActionsBar = ({
|
|
|
264
264
|
{/* Copy Button */}
|
|
265
265
|
<ActionButton
|
|
266
266
|
icon="copy-o"
|
|
267
|
-
iconSize={
|
|
267
|
+
iconSize={20}
|
|
268
268
|
isLoading={isCopying}
|
|
269
269
|
onPress={handleCopy}
|
|
270
270
|
testID="copy-button"
|
|
@@ -272,8 +272,8 @@ const MessageActionsBar = ({
|
|
|
272
272
|
|
|
273
273
|
{/* Share Button */}
|
|
274
274
|
<ActionButton
|
|
275
|
-
icon="share-o"
|
|
276
|
-
iconSize={
|
|
275
|
+
icon="text-share-o"
|
|
276
|
+
iconSize={20}
|
|
277
277
|
isLoading={isSharing}
|
|
278
278
|
onPress={shareMessage}
|
|
279
279
|
testID="share-button"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useAudioPlayer Hook - react-native-track-player version
|
|
3
3
|
*/
|
|
4
|
-
import { useCallback, useEffect } from 'react';
|
|
4
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
5
5
|
import TrackPlayer, {
|
|
6
6
|
Event,
|
|
7
7
|
Capability,
|
|
8
8
|
State,
|
|
9
|
+
AppKilledPlaybackBehavior,
|
|
9
10
|
} from 'react-native-track-player';
|
|
10
11
|
|
|
11
12
|
import ReactNativeBlobUtil from 'react-native-blob-util';
|
|
@@ -33,14 +34,25 @@ interface UseAudioPlayerReturn {
|
|
|
33
34
|
// Register playback service BEFORE setupPlayer
|
|
34
35
|
TrackPlayer.registerPlaybackService(() => PlaybackService);
|
|
35
36
|
|
|
37
|
+
export const DefaultAudioServiceBehaviour =
|
|
38
|
+
AppKilledPlaybackBehavior.StopPlaybackAndRemoveNotification;
|
|
39
|
+
|
|
36
40
|
// --- INIT PLAYER ONE TIME ---
|
|
37
41
|
let playerInitialized = false;
|
|
38
42
|
const setupPlayer = async () => {
|
|
39
43
|
if (playerInitialized) return;
|
|
40
44
|
|
|
41
45
|
try {
|
|
42
|
-
|
|
46
|
+
try {
|
|
47
|
+
await TrackPlayer.setupPlayer({
|
|
48
|
+
autoHandleInterruptions: true,
|
|
49
|
+
});
|
|
50
|
+
} catch (setupError) {
|
|
51
|
+
console.warn('Setup with options failed, trying without options...');
|
|
52
|
+
await TrackPlayer.setupPlayer();
|
|
53
|
+
}
|
|
43
54
|
|
|
55
|
+
// Update options
|
|
44
56
|
await TrackPlayer.updateOptions({
|
|
45
57
|
capabilities: [Capability.Play, Capability.Pause, Capability.Stop],
|
|
46
58
|
compactCapabilities: [Capability.Play, Capability.Pause],
|
|
@@ -49,10 +61,21 @@ const setupPlayer = async () => {
|
|
|
49
61
|
Capability.Pause,
|
|
50
62
|
Capability.Stop,
|
|
51
63
|
],
|
|
64
|
+
android: {
|
|
65
|
+
appKilledPlaybackBehavior: DefaultAudioServiceBehaviour,
|
|
66
|
+
},
|
|
67
|
+
progressUpdateEventInterval: 2,
|
|
52
68
|
});
|
|
69
|
+
|
|
53
70
|
playerInitialized = true;
|
|
54
71
|
} catch (e) {
|
|
55
72
|
console.error('Error initializing TrackPlayer:', e);
|
|
73
|
+
|
|
74
|
+
// Log more details for debugging
|
|
75
|
+
if (e instanceof Error) {
|
|
76
|
+
console.error('Error message:', e.message);
|
|
77
|
+
}
|
|
78
|
+
throw e; // Re-throw to indicate setup failed
|
|
56
79
|
}
|
|
57
80
|
};
|
|
58
81
|
|
|
@@ -76,6 +99,10 @@ export const useAudioPlayer = ({
|
|
|
76
99
|
stopPlayback: storeStopPlayback,
|
|
77
100
|
} = useAudioPlayerStore();
|
|
78
101
|
|
|
102
|
+
// Track if we're in the middle of a user action
|
|
103
|
+
// This prevents event listener from overriding state immediately after user click
|
|
104
|
+
const isUserActionInProgressRef = useRef(false);
|
|
105
|
+
|
|
79
106
|
const isThisMessagePlaying = currentMessageId === messageId && isPlaying;
|
|
80
107
|
const isThisMessageLoading = currentMessageId === messageId && isLoading;
|
|
81
108
|
const isThisMessagePaused =
|
|
@@ -142,6 +169,8 @@ export const useAudioPlayer = ({
|
|
|
142
169
|
const playAudio = useCallback(
|
|
143
170
|
async (audioPath: string) => {
|
|
144
171
|
try {
|
|
172
|
+
isUserActionInProgressRef.current = true;
|
|
173
|
+
|
|
145
174
|
await setupPlayer();
|
|
146
175
|
await TrackPlayer.reset();
|
|
147
176
|
|
|
@@ -151,11 +180,24 @@ export const useAudioPlayer = ({
|
|
|
151
180
|
title: `Message Audio`,
|
|
152
181
|
artist: 'Assistant',
|
|
153
182
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
183
|
+
try {
|
|
184
|
+
await TrackPlayer.add([track]);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
console.log('Failed to add track:', err);
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
await TrackPlayer.play();
|
|
190
|
+
} catch (err) {
|
|
191
|
+
console.log('Failed to play track:', err);
|
|
192
|
+
}
|
|
157
193
|
setIsPlaying(true);
|
|
194
|
+
|
|
195
|
+
// Release lock after a short delay
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
isUserActionInProgressRef.current = false;
|
|
198
|
+
}, 300);
|
|
158
199
|
} catch (err) {
|
|
200
|
+
isUserActionInProgressRef.current = false;
|
|
159
201
|
console.error('Failed to play audio:', err);
|
|
160
202
|
throw new Error('Không thể phát âm thanh');
|
|
161
203
|
}
|
|
@@ -166,9 +208,17 @@ export const useAudioPlayer = ({
|
|
|
166
208
|
// === PAUSE ===
|
|
167
209
|
const pauseAudio = useCallback(async () => {
|
|
168
210
|
try {
|
|
211
|
+
isUserActionInProgressRef.current = true;
|
|
212
|
+
|
|
169
213
|
await TrackPlayer.pause();
|
|
170
214
|
setIsPlaying(false);
|
|
215
|
+
|
|
216
|
+
// Release lock after a short delay
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
isUserActionInProgressRef.current = false;
|
|
219
|
+
}, 300);
|
|
171
220
|
} catch (e) {
|
|
221
|
+
isUserActionInProgressRef.current = false;
|
|
172
222
|
console.warn('Pause failed:', e);
|
|
173
223
|
}
|
|
174
224
|
}, [setIsPlaying]);
|
|
@@ -184,6 +234,24 @@ export const useAudioPlayer = ({
|
|
|
184
234
|
}
|
|
185
235
|
}, [storeStopPlayback]);
|
|
186
236
|
|
|
237
|
+
// === RESUME (for paused audio) ===
|
|
238
|
+
const resumeAudio = useCallback(async () => {
|
|
239
|
+
try {
|
|
240
|
+
isUserActionInProgressRef.current = true;
|
|
241
|
+
|
|
242
|
+
await TrackPlayer.play();
|
|
243
|
+
setIsPlaying(true);
|
|
244
|
+
|
|
245
|
+
// Release lock after a short delay
|
|
246
|
+
setTimeout(() => {
|
|
247
|
+
isUserActionInProgressRef.current = false;
|
|
248
|
+
}, 300);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
isUserActionInProgressRef.current = false;
|
|
251
|
+
console.warn('Resume failed:', e);
|
|
252
|
+
}
|
|
253
|
+
}, [setIsPlaying]);
|
|
254
|
+
|
|
187
255
|
// === TOGGLE ===
|
|
188
256
|
const togglePlayback = useCallback(async () => {
|
|
189
257
|
try {
|
|
@@ -203,8 +271,7 @@ export const useAudioPlayer = ({
|
|
|
203
271
|
|
|
204
272
|
// Case 2: Click on the SAME message that was paused → Resume it
|
|
205
273
|
if (currentMessageId === messageId && !isPlaying) {
|
|
206
|
-
await
|
|
207
|
-
setIsPlaying(true);
|
|
274
|
+
await resumeAudio();
|
|
208
275
|
logGA(GAEvents.chatDetailVoiceBtnTap, {
|
|
209
276
|
conversation_id: sessionId,
|
|
210
277
|
message_id: messageId,
|
|
@@ -260,12 +327,15 @@ export const useAudioPlayer = ({
|
|
|
260
327
|
fetchAudio,
|
|
261
328
|
playAudio,
|
|
262
329
|
pauseAudio,
|
|
330
|
+
resumeAudio,
|
|
263
331
|
stopPlayback,
|
|
264
332
|
logGA,
|
|
265
333
|
]);
|
|
266
334
|
|
|
267
335
|
// === PLAYER EVENTS ===
|
|
268
336
|
useEffect(() => {
|
|
337
|
+
let stateUpdateTimeout: NodeJS.Timeout | null = null;
|
|
338
|
+
|
|
269
339
|
// Listen for queue end
|
|
270
340
|
const queueEndSubscription = TrackPlayer.addEventListener(
|
|
271
341
|
Event.PlaybackQueueEnded,
|
|
@@ -281,26 +351,50 @@ export const useAudioPlayer = ({
|
|
|
281
351
|
const stateSubscription = TrackPlayer.addEventListener(
|
|
282
352
|
Event.PlaybackState,
|
|
283
353
|
async () => {
|
|
284
|
-
|
|
354
|
+
// Skip if we're in the middle of a user action
|
|
355
|
+
if (isUserActionInProgressRef.current) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
285
358
|
|
|
286
|
-
//
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
setIsPlaying(true);
|
|
290
|
-
} else if (state === State.Paused || state === State.Ready) {
|
|
291
|
-
setIsPlaying(false);
|
|
292
|
-
} else if (state === State.Stopped) {
|
|
293
|
-
storeStopPlayback();
|
|
294
|
-
}
|
|
359
|
+
// Clear previous timeout
|
|
360
|
+
if (stateUpdateTimeout) {
|
|
361
|
+
clearTimeout(stateUpdateTimeout);
|
|
295
362
|
}
|
|
363
|
+
|
|
364
|
+
// Add small delay to let the state stabilize
|
|
365
|
+
stateUpdateTimeout = setTimeout(async () => {
|
|
366
|
+
// Double check if user action finished during timeout
|
|
367
|
+
if (isUserActionInProgressRef.current) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const state = await TrackPlayer.getState();
|
|
371
|
+
|
|
372
|
+
// Only update if this is the current message
|
|
373
|
+
if (currentMessageId === messageId) {
|
|
374
|
+
// Prevent overriding state if it's already correct
|
|
375
|
+
if (state === State.Playing && !isPlaying) {
|
|
376
|
+
setIsPlaying(true);
|
|
377
|
+
} else if (
|
|
378
|
+
(state === State.Paused || state === State.Ready) &&
|
|
379
|
+
isPlaying
|
|
380
|
+
) {
|
|
381
|
+
setIsPlaying(false);
|
|
382
|
+
} else if (state === State.Stopped) {
|
|
383
|
+
storeStopPlayback();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}, 150); // 150ms delay to let state stabilize
|
|
296
387
|
}
|
|
297
388
|
);
|
|
298
389
|
|
|
299
390
|
return () => {
|
|
391
|
+
if (stateUpdateTimeout) {
|
|
392
|
+
clearTimeout(stateUpdateTimeout);
|
|
393
|
+
}
|
|
300
394
|
queueEndSubscription?.remove?.();
|
|
301
395
|
stateSubscription?.remove?.();
|
|
302
396
|
};
|
|
303
|
-
}, [currentMessageId, messageId, storeStopPlayback, setIsPlaying]);
|
|
397
|
+
}, [currentMessageId, messageId, storeStopPlayback, setIsPlaying, isPlaying]);
|
|
304
398
|
|
|
305
399
|
// === CLEANUP CACHE WHEN SESSION CHANGES ===
|
|
306
400
|
const cleanupAudioCache = useCallback(async () => {
|