| .. | .. |
|---|
| 678 | 678 | final bytes = await File(file.path!).readAsBytes(); |
|---|
| 679 | 679 | final b64 = base64Encode(bytes); |
|---|
| 680 | 680 | final mimeType = _guessMimeType(file.name); |
|---|
| 681 | | - |
|---|
| 682 | 681 | attachments.add({ |
|---|
| 683 | 682 | 'data': b64, |
|---|
| 684 | 683 | 'mimeType': mimeType, |
|---|
| 685 | 684 | 'fileName': file.name, |
|---|
| 686 | 685 | }); |
|---|
| 686 | + } |
|---|
| 687 | + if (attachments.isEmpty) return; |
|---|
| 687 | 688 | |
|---|
| 688 | | - // Show in chat |
|---|
| 689 | | - if (mimeType.startsWith('image/')) { |
|---|
| 690 | | - ref.read(messagesProvider.notifier).addMessage(Message.image( |
|---|
| 691 | | - role: MessageRole.user, |
|---|
| 692 | | - imageBase64: b64, |
|---|
| 693 | | - content: file.name, |
|---|
| 694 | | - status: MessageStatus.sent, |
|---|
| 695 | | - )); |
|---|
| 696 | | - } else { |
|---|
| 697 | | - ref.read(messagesProvider.notifier).addMessage(Message.text( |
|---|
| 698 | | - role: MessageRole.user, |
|---|
| 699 | | - content: 'š ${file.name} (${_formatSize(bytes.length)})', |
|---|
| 700 | | - status: MessageStatus.sent, |
|---|
| 701 | | - )); |
|---|
| 689 | + // Show caption dialog |
|---|
| 690 | + final fileNames = result.files.map((f) => f.name).join(', '); |
|---|
| 691 | + final caption = await _showCaptionDialog(result.files.length); |
|---|
| 692 | + if (caption == null) return; |
|---|
| 693 | + |
|---|
| 694 | + // Handle voice caption |
|---|
| 695 | + String textCaption = caption; |
|---|
| 696 | + String? voiceB64; |
|---|
| 697 | + if (caption.startsWith('__voice__:')) { |
|---|
| 698 | + final voicePath = caption.substring('__voice__:'.length); |
|---|
| 699 | + final voiceFile = File(voicePath); |
|---|
| 700 | + if (await voiceFile.exists()) { |
|---|
| 701 | + voiceB64 = base64Encode(await voiceFile.readAsBytes()); |
|---|
| 702 | 702 | } |
|---|
| 703 | + textCaption = ''; |
|---|
| 704 | + } |
|---|
| 705 | + |
|---|
| 706 | + // Send voice first if present |
|---|
| 707 | + if (voiceB64 != null) { |
|---|
| 708 | + final voiceMsg = Message.voice( |
|---|
| 709 | + role: MessageRole.user, |
|---|
| 710 | + audioUri: caption.substring('__voice__:'.length), |
|---|
| 711 | + status: MessageStatus.sent, |
|---|
| 712 | + ); |
|---|
| 713 | + ref.read(messagesProvider.notifier).addMessage(voiceMsg); |
|---|
| 714 | + _ws?.send({ |
|---|
| 715 | + 'type': 'voice', |
|---|
| 716 | + 'audioBase64': voiceB64, |
|---|
| 717 | + 'content': '', |
|---|
| 718 | + 'messageId': voiceMsg.id, |
|---|
| 719 | + 'sessionId': targetSessionId, |
|---|
| 720 | + }); |
|---|
| 703 | 721 | } |
|---|
| 704 | 722 | |
|---|
| 705 | 723 | // Send all files as one atomic bundle |
|---|
| 706 | 724 | _ws?.send({ |
|---|
| 707 | 725 | 'type': 'bundle', |
|---|
| 708 | | - 'caption': '', |
|---|
| 726 | + 'caption': textCaption, |
|---|
| 709 | 727 | 'attachments': attachments, |
|---|
| 710 | 728 | 'sessionId': targetSessionId, |
|---|
| 711 | 729 | }); |
|---|
| 712 | 730 | |
|---|
| 731 | + // Show in chat |
|---|
| 732 | + for (final att in attachments) { |
|---|
| 733 | + final mime = att['mimeType'] as String; |
|---|
| 734 | + final name = att['fileName'] as String? ?? 'file'; |
|---|
| 735 | + if (mime.startsWith('image/')) { |
|---|
| 736 | + ref.read(messagesProvider.notifier).addMessage(Message.image( |
|---|
| 737 | + role: MessageRole.user, |
|---|
| 738 | + imageBase64: att['data'] as String, |
|---|
| 739 | + content: name, |
|---|
| 740 | + status: MessageStatus.sent, |
|---|
| 741 | + )); |
|---|
| 742 | + } else { |
|---|
| 743 | + final size = base64Decode(att['data'] as String).length; |
|---|
| 744 | + ref.read(messagesProvider.notifier).addMessage(Message.text( |
|---|
| 745 | + role: MessageRole.user, |
|---|
| 746 | + content: textCaption.isNotEmpty |
|---|
| 747 | + ? '$textCaption\nš $name (${_formatSize(size)})' |
|---|
| 748 | + : 'š $name (${_formatSize(size)})', |
|---|
| 749 | + status: MessageStatus.sent, |
|---|
| 750 | + )); |
|---|
| 751 | + } |
|---|
| 752 | + } |
|---|
| 753 | + |
|---|
| 713 | 754 | _scrollToBottom(); |
|---|
| 714 | 755 | } |
|---|
| 715 | 756 | |
|---|