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.
Files changed (30) hide show
  1. package/lib/module/components/Drawer/DeleteSessionPopup.js +5 -5
  2. package/lib/module/components/Drawer/DeleteSessionPopup.js.map +1 -1
  3. package/lib/module/components/Drawer/RenameSessionPopup.js +24 -21
  4. package/lib/module/components/Drawer/RenameSessionPopup.js.map +1 -1
  5. package/lib/module/components/Drawer/SessionItem.js +6 -5
  6. package/lib/module/components/Drawer/SessionItem.js.map +1 -1
  7. package/lib/module/components/Drawer/SessionList.js +5 -4
  8. package/lib/module/components/Drawer/SessionList.js.map +1 -1
  9. package/lib/module/components/Drawer/SessionOptionsBottomSheet.js +7 -6
  10. package/lib/module/components/Drawer/SessionOptionsBottomSheet.js.map +1 -1
  11. package/lib/module/components/Drawer/ShareSessionPopup.js +6 -6
  12. package/lib/module/components/Drawer/ShareSessionPopup.js.map +1 -1
  13. package/lib/module/components/chat/item/MessageActionsBar.js +8 -8
  14. package/lib/module/components/chat/item/MessageActionsBar.js.map +1 -1
  15. package/lib/module/hooks/messageActions/useAudioPlayer.js +105 -20
  16. package/lib/module/hooks/messageActions/useAudioPlayer.js.map +1 -1
  17. package/lib/typescript/src/components/Drawer/SessionItem.d.ts.map +1 -1
  18. package/lib/typescript/src/components/Drawer/SessionList.d.ts.map +1 -1
  19. package/lib/typescript/src/components/Drawer/SessionOptionsBottomSheet.d.ts.map +1 -1
  20. package/lib/typescript/src/hooks/messageActions/useAudioPlayer.d.ts +1 -0
  21. package/lib/typescript/src/hooks/messageActions/useAudioPlayer.d.ts.map +1 -1
  22. package/package.json +2 -2
  23. package/src/components/Drawer/DeleteSessionPopup.tsx +5 -5
  24. package/src/components/Drawer/RenameSessionPopup.tsx +38 -38
  25. package/src/components/Drawer/SessionItem.tsx +5 -4
  26. package/src/components/Drawer/SessionList.tsx +8 -3
  27. package/src/components/Drawer/SessionOptionsBottomSheet.tsx +6 -5
  28. package/src/components/Drawer/ShareSessionPopup.tsx +6 -6
  29. package/src/components/chat/item/MessageActionsBar.tsx +8 -8
  30. 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-thumbup-b"
163
- size={16}
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="like-thumbdown-b"
219
- size={16}
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={16}
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={16}
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={16}
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
- await TrackPlayer.setupPlayer();
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
- await TrackPlayer.add([track]);
156
- await TrackPlayer.play();
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 TrackPlayer.play();
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
- const state = await TrackPlayer.getState();
354
+ // Skip if we're in the middle of a user action
355
+ if (isUserActionInProgressRef.current) {
356
+ return;
357
+ }
285
358
 
286
- // Only update if this is the current message
287
- if (currentMessageId === messageId) {
288
- if (state === State.Playing) {
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 () => {