chat support download updated

This commit is contained in:
Abhishek Mali
2025-12-18 11:03:25 +05:30
parent bbde34fae4
commit b9fb9455e7
11 changed files with 416 additions and 131 deletions

View File

@@ -24,6 +24,7 @@ class ChatScreen extends StatefulWidget {
class _ChatScreenState extends State<ChatScreen> {
final TextEditingController _messageCtrl = TextEditingController();
final ScrollController _scrollCtrl = ScrollController();
Map<String, dynamic>? uploadingMessage;
late ChatService _chatService;
final ReverbSocketService _socket = ReverbSocketService();
@@ -48,18 +49,54 @@ class _ChatScreenState extends State<ChatScreen> {
_initChat();
}
String _guessMimeType(String path) {
final lower = path.toLowerCase();
if (lower.endsWith('.jpg') || lower.endsWith('.png')) return 'image/*';
if (lower.endsWith('.mp4')) return 'video/*';
if (lower.endsWith('.pdf')) return 'application/pdf';
return 'application/octet-stream';
}
Future<void> _pickAndSendFile() async {
final XFile? file = await openFile();
final XFile? picked = await openFile();
if (picked == null || ticketId == null) return;
if (file == null || ticketId == null) return;
final file = File(picked.path);
final dartFile = File(file.path);
// 1⃣ Show uploading UI
setState(() {
uploadingMessage = {
'local_file': file,
'file_type': _guessMimeType(file.path),
'progress': 0.0,
};
});
await _chatService.sendFile(ticketId!, dartFile);
// 2⃣ Upload (NO adding message)
await _chatService.sendFile(
ticketId!,
file,
onProgress: (progress) {
if (!mounted) return;
setState(() {
uploadingMessage!['progress'] = progress;
});
},
);
// 3⃣ Remove sending bubble ONLY
if (!mounted) return;
setState(() {
uploadingMessage = null;
});
// 🚫 DO NOT add message here
// WebSocket will handle it
}
// ============================
// INIT CHAT
// ============================
@@ -77,10 +114,23 @@ class _ChatScreenState extends State<ChatScreen> {
context: context,
ticketId: ticketId!,
onMessage: (msg) {
if (!mounted) return;
setState(() => messages.add(msg));
final incomingClientId = msg['client_id'];
setState(() {
// 🧹 Remove local temp message with same client_id
messages.removeWhere(
(m) => m['client_id'] != null &&
m['client_id'] == incomingClientId,
);
// ✅ Add confirmed socket message
messages.add(msg);
});
_scrollToBottom();
},
onAdminMessage: () {
if (!mounted) {
context.read<ChatUnreadProvider>().increment();
@@ -112,16 +162,35 @@ class _ChatScreenState extends State<ChatScreen> {
// ============================
Future<void> _sendMessage() async {
final text = _messageCtrl.text.trim();
if (text.isEmpty) return;
if (text.isEmpty || ticketId == null) return;
_messageCtrl.clear();
final clientId = DateTime.now().millisecondsSinceEpoch.toString();
// 1⃣ ADD LOCAL MESSAGE IMMEDIATELY
setState(() {
messages.add({
'client_id': clientId,
'sender_type': 'App\\Models\\User',
'message': text,
'file_path': null,
'file_type': null,
'sending': true,
});
});
_scrollToBottom();
// 2⃣ SEND TO SERVER
await _chatService.sendMessage(
ticketId!,
message: text,
clientId: clientId,
);
}
// ============================
// DISPOSE
// ============================
@@ -226,47 +295,83 @@ class _ChatScreenState extends State<ChatScreen> {
Widget _buildMessages() {
return ListView.builder(
return ListView(
controller: _scrollCtrl,
padding: const EdgeInsets.all(12),
itemCount: messages.length,
itemBuilder: (_, index) {
final msg = messages[index];
final isUser = msg['sender_type'] == 'App\\Models\\User';
children: [
// EXISTING MESSAGES
...messages.map((msg) {
final isUser = msg['sender_type'] == 'App\\Models\\User';
final String? filePath = msg['file_path'];
final String? fileType = msg['file_type'];
final String? message = msg['message'];
return Align(
alignment: isUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.symmetric(vertical: 6),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: isUser ? Colors.blue : Colors.grey.shade300,
borderRadius: BorderRadius.circular(12),
),
// 🔽 ONLY THIS PART CHANGED
child: filePath == null
? Text(
message ?? '',
style: TextStyle(
color: isUser ? Colors.white : Colors.black,
return Align(
alignment: isUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.symmetric(vertical: 6),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: isUser ? Colors.blue : Colors.grey.shade300,
borderRadius: BorderRadius.circular(12),
),
child: msg['file_path'] == null
? Text(
msg['message'] ?? '',
style: TextStyle(
color: isUser ? Colors.white : Colors.black,
),
)
: ChatFilePreview(
filePath: msg['file_path'],
fileType: msg['file_type'] ?? '',
isUser: isUser,
),
),
);
}),
// ⏳ UPLOADING MESSAGE
if (uploadingMessage != null)
Align(
alignment: Alignment.centerRight,
child: Container(
margin: const EdgeInsets.symmetric(vertical: 6),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ChatFilePreview(
filePath: uploadingMessage!['local_file'].path,
fileType: uploadingMessage!['file_type'],
isUser: true,
isLocal: true,
),
const SizedBox(height: 8),
LinearProgressIndicator(
value: uploadingMessage!['progress'],
backgroundColor: Colors.white24,
valueColor:
const AlwaysStoppedAnimation(Colors.white),
),
const SizedBox(height: 4),
const Text(
"Sending…",
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
],
),
)
: ChatFilePreview(
filePath: filePath,
fileType: fileType ?? '',
isUser: isUser,
),
),
);
},
],
);
}
Widget _buildInput() {
return SafeArea(
child: Row(