2026-04-15
This commit is contained in:
1 parent
d01e5ba860
commit
96de59e9be
46 files changed
+1299
-1191
No files matched your search
@@ -39,7 +39,7 @@ class ChatProfileScreen extends ConsumerWidget {
|
||||
title: const Text(
|
||||
"Profile",
|
||||
style: TextStyle(
|
||||
fontFamily: "Gilroy-SemiBold",
|
||||
fontFamily: "Gilroy",
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 20,
|
||||
height: 1.4,
|
||||
@@ -113,7 +113,7 @@ class ChatProfileScreen extends ConsumerWidget {
|
||||
const Text(
|
||||
"Shared By Taxglide",
|
||||
style: TextStyle(
|
||||
fontFamily: "Gilroy-SemiBold",
|
||||
fontFamily: "Gilroy",
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20,
|
||||
height: 1.4,
|
||||
@@ -213,7 +213,7 @@ class ChatProfileScreen extends ConsumerWidget {
|
||||
const Text(
|
||||
"Shared By Client",
|
||||
style: TextStyle(
|
||||
fontFamily: "Gilroy-SemiBold",
|
||||
fontFamily: "Gilroy",
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20,
|
||||
height: 1.4,
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:taxglide/consts/download_helper.dart';
|
||||
import 'package:taxglide/controller/api_contoller.dart';
|
||||
import 'package:taxglide/controller/api_repository.dart';
|
||||
import 'package:taxglide/model/chat_model.dart';
|
||||
import 'package:taxglide/view/Mahi_chat/live_chat_screen.dart';
|
||||
|
||||
// ⭐⭐⭐ CHAT INPUT BOX WITH FILE TAG SUPPORT ⭐⭐⭐
|
||||
@@ -246,49 +247,61 @@ class _ChatInputBoxState extends ConsumerState<ChatInputBox> {
|
||||
|
||||
setState(() => _isSending = true);
|
||||
|
||||
// Show loading dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: const Center(
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 16),
|
||||
Text("Sending message..."),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// ⭐ OPTIMISTIC UI: Create a temporary message and add it immediately
|
||||
final tempId = DateTime.now().millisecondsSinceEpoch;
|
||||
final tempMsg = MessageModel(
|
||||
id: tempId,
|
||||
chatBy: "user", // User
|
||||
message: text,
|
||||
type: selectedFiles.isNotEmpty ? "file" : "text",
|
||||
isDelivered: 0,
|
||||
isRead: 0,
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
userId: 0,
|
||||
tagId: tagId > 0 ? tagId : null,
|
||||
parentTag: taggedMessage != null ? ParentTagModel(
|
||||
id: taggedMessage.id,
|
||||
message: taggedMessage.message,
|
||||
type: taggedMessage.type,
|
||||
fileName: taggedMessage.fileName,
|
||||
) : null,
|
||||
uploadedDocuments: selectedFiles.map((file) => DocumentModel(
|
||||
filePath: file.path,
|
||||
fileName: file.path.split('/').last,
|
||||
fileType: "image",
|
||||
)).toList(),
|
||||
documents: [],
|
||||
);
|
||||
|
||||
// Add to UI immediately
|
||||
final notifier = ref.read(chatMessagesProvider(widget.chatId).notifier);
|
||||
notifier.addNewMessage(tempMsg);
|
||||
|
||||
// Clear input immediately for snappy feel
|
||||
final savedSelectedFiles = List<File>.from(selectedFiles);
|
||||
messageCtrl.clear();
|
||||
selectedFiles.clear();
|
||||
ref.read(taggedMessageProvider.notifier).state = null;
|
||||
|
||||
// Notify parent to scroll to bottom instantly
|
||||
if (widget.onMessageSent != null) {
|
||||
widget.onMessageSent!();
|
||||
}
|
||||
|
||||
try {
|
||||
List<File> filesToUpload = [];
|
||||
|
||||
// ⭐⭐⭐ Save files to Send folder
|
||||
if (selectedFiles.isNotEmpty) {
|
||||
print("📤 Saving ${selectedFiles.length} files to Send folder...");
|
||||
|
||||
for (File file in selectedFiles) {
|
||||
if (savedSelectedFiles.isNotEmpty) {
|
||||
for (File file in savedSelectedFiles) {
|
||||
try {
|
||||
// Try to save to Send folder, fallback to original if fails
|
||||
final savedPath = await DownloadHelper.saveToSendFolder(file);
|
||||
filesToUpload.add(File(savedPath));
|
||||
} catch (e) {
|
||||
print("⚠️ Could not save file, using original: $e");
|
||||
filesToUpload.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
print("✅ Files prepared for upload: ${filesToUpload.length}");
|
||||
}
|
||||
|
||||
// Send message with files
|
||||
@@ -299,37 +312,22 @@ class _ChatInputBoxState extends ConsumerState<ChatInputBox> {
|
||||
files: filesToUpload,
|
||||
);
|
||||
|
||||
// Refresh chat messages
|
||||
await ref.read(chatMessagesProvider(widget.chatId).notifier).refresh();
|
||||
// Refresh chat messages (this will replace the temp message with the real one)
|
||||
await notifier.refresh();
|
||||
ref.invalidate(chatDocumentProvider);
|
||||
print("✅ Message sent successfully with tagId: $tagId");
|
||||
|
||||
// Close loading dialog
|
||||
if (mounted) Navigator.pop(context);
|
||||
|
||||
// Clear input
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
messageCtrl.clear();
|
||||
selectedFiles.clear();
|
||||
_isSending = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Clear the tagged message
|
||||
ref.read(taggedMessageProvider.notifier).state = null;
|
||||
|
||||
// Notify parent to scroll to bottom
|
||||
if (widget.onMessageSent != null) {
|
||||
widget.onMessageSent!();
|
||||
setState(() => _isSending = false);
|
||||
}
|
||||
} catch (e) {
|
||||
print("❌ Error sending message: $e");
|
||||
|
||||
if (mounted) {
|
||||
setState(() => _isSending = false);
|
||||
}
|
||||
|
||||
// Close loading dialog
|
||||
if (mounted) Navigator.pop(context);
|
||||
|
||||
setState(() => _isSending = false);
|
||||
// If it failed, delete the temporary message
|
||||
notifier.deleteMessage(tempId);
|
||||
|
||||
// Show error message
|
||||
if (mounted) {
|
||||
@@ -361,6 +359,7 @@ class _ChatInputBoxState extends ConsumerState<ChatInputBox> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final taggedMessage = ref.watch(taggedMessageProvider);
|
||||
|
||||
@@ -270,7 +270,7 @@ class _DownloadsScreenState extends State<DownloadsScreen>
|
||||
title: const Text(
|
||||
"Downloads",
|
||||
style: TextStyle(
|
||||
fontFamily: "Gilroy-SemiBold",
|
||||
fontFamily: "Gilroy",
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -9,7 +9,7 @@ class SwipeableMessageBubble extends StatefulWidget {
|
||||
final MessageModel message;
|
||||
final DateTime msgDate;
|
||||
final String Function(DateTime) formatTime;
|
||||
final Widget Function(int, int) buildTick;
|
||||
final Widget Function(MessageModel) buildTick;
|
||||
final VoidCallback onSwipe;
|
||||
final Function(int?)? onParentTagTap;
|
||||
final Function(List<String> imageUrls, int initialIndex)?
|
||||
@@ -669,10 +669,7 @@ class SwipeableMessageBubbleState extends State<SwipeableMessageBubble>
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
widget.buildTick(
|
||||
widget.message.isDelivered,
|
||||
widget.message.isRead,
|
||||
),
|
||||
widget.buildTick(widget.message),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -160,8 +160,11 @@ class _LiveChatScreenState extends ConsumerState<LiveChatScreen>
|
||||
try {
|
||||
final event = message['event'] as String?;
|
||||
|
||||
if (event == 'message.sent' || event == 'message.received') {
|
||||
debugPrint("🔔 New message detected!");
|
||||
if (event == 'message.sent' ||
|
||||
event == 'message.received' ||
|
||||
event == 'message.read' ||
|
||||
event == 'message.delivered') {
|
||||
debugPrint("🔔 Message status/new message event: $event");
|
||||
|
||||
if (WidgetsBinding.instance.lifecycleState ==
|
||||
AppLifecycleState.resumed) {
|
||||
@@ -409,7 +412,16 @@ class _LiveChatScreenState extends ConsumerState<LiveChatScreen>
|
||||
return "$hour:$minute $period";
|
||||
}
|
||||
|
||||
Widget buildTick(int isDelivered, int isRead) {
|
||||
Widget buildTick(MessageModel message) {
|
||||
// ⭐ Status 0/1/pending
|
||||
final isRead = message.isRead;
|
||||
final isDelivered = message.isDelivered;
|
||||
|
||||
// Check if it's an optimistic pending message (huge ID)
|
||||
if (message.id > 1000000000000) {
|
||||
return const Icon(Icons.schedule, size: 14, color: Colors.grey);
|
||||
}
|
||||
|
||||
if (isRead == 1) {
|
||||
return const Icon(Icons.done_all, size: 16, color: Colors.blue);
|
||||
} else if (isDelivered == 1) {
|
||||
@@ -445,7 +457,7 @@ class _LiveChatScreenState extends ConsumerState<LiveChatScreen>
|
||||
? 'File ID : ${widget.fileid.toString()}'
|
||||
: "Live Chat",
|
||||
style: const TextStyle(
|
||||
fontFamily: "Gilroy-SemiBold",
|
||||
fontFamily: "Gilroy",
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user