| .. | .. |
|---|
| 71 | 71 | if (!mounted) return; |
|---|
| 72 | 72 | |
|---|
| 73 | 73 | // Listen for playback state changes to reset play button UI |
|---|
| 74 | + // Use a brief delay to avoid race between queue transitions |
|---|
| 74 | 75 | AudioService.onPlaybackStateChanged = () { |
|---|
| 75 | | - if (mounted) { |
|---|
| 76 | | - setState(() { |
|---|
| 77 | | - if (!AudioService.isPlaying) { |
|---|
| 78 | | - _playingMessageId = null; |
|---|
| 76 | + if (mounted && !AudioService.isPlaying) { |
|---|
| 77 | + Future.delayed(const Duration(milliseconds: 100), () { |
|---|
| 78 | + if (mounted && !AudioService.isPlaying) { |
|---|
| 79 | + setState(() => _playingMessageId = null); |
|---|
| 79 | 80 | } |
|---|
| 80 | 81 | }); |
|---|
| 81 | 82 | } |
|---|
| .. | .. |
|---|
| 382 | 383 | _scrollToBottom(); |
|---|
| 383 | 384 | |
|---|
| 384 | 385 | if (audioData != null && !AudioService.isBackgrounded && !_isCatchingUp && !_isRecording) { |
|---|
| 386 | + setState(() => _playingMessageId = storedMessage.id); |
|---|
| 385 | 387 | AudioService.queueBase64(audioData); |
|---|
| 386 | 388 | } |
|---|
| 387 | 389 | } |
|---|
| .. | .. |
|---|
| 483 | 485 | String? _recordingSessionId; // Capture session at recording start |
|---|
| 484 | 486 | |
|---|
| 485 | 487 | Future<void> _startRecording() async { |
|---|
| 488 | + // Stop any playing audio before recording |
|---|
| 489 | + if (AudioService.isPlaying) { |
|---|
| 490 | + await AudioService.stopPlayback(); |
|---|
| 491 | + setState(() => _playingMessageId = null); |
|---|
| 492 | + } |
|---|
| 486 | 493 | _recordingSessionId = ref.read(activeSessionIdProvider); |
|---|
| 487 | 494 | final path = await AudioService.startRecording(); |
|---|
| 488 | 495 | if (path != null) { |
|---|
| .. | .. |
|---|
| 554 | 561 | } |
|---|
| 555 | 562 | } |
|---|
| 556 | 563 | |
|---|
| 557 | | - void _playMessage(Message message) { |
|---|
| 564 | + void _playMessage(Message message) async { |
|---|
| 558 | 565 | if (message.audioUri == null) return; |
|---|
| 559 | 566 | |
|---|
| 560 | 567 | // Toggle: if this message is already playing, stop it |
|---|
| 561 | 568 | if (_playingMessageId == message.id) { |
|---|
| 562 | | - AudioService.stopPlayback(); |
|---|
| 569 | + await AudioService.stopPlayback(); |
|---|
| 563 | 570 | setState(() => _playingMessageId = null); |
|---|
| 564 | 571 | return; |
|---|
| 565 | 572 | } |
|---|
| 566 | 573 | |
|---|
| 567 | | - // Stop any current playback first |
|---|
| 568 | | - if (_playingMessageId != null) { |
|---|
| 569 | | - AudioService.stopPlayback(); |
|---|
| 570 | | - } |
|---|
| 574 | + // Stop any current playback first, then set playing ID AFTER stop completes |
|---|
| 575 | + // (stopPlayback triggers onPlaybackStateChanged which clears _playingMessageId) |
|---|
| 576 | + await AudioService.stopPlayback(); |
|---|
| 571 | 577 | |
|---|
| 578 | + if (!mounted) return; |
|---|
| 572 | 579 | setState(() => _playingMessageId = message.id); |
|---|
| 573 | 580 | |
|---|
| 574 | 581 | if (message.audioUri!.startsWith('/')) { |
|---|