feat: complete interactive audio player, contact resolver, unread clearance, and media sending
This commit is contained in:
@@ -5,6 +5,7 @@ import 'package:intl/intl.dart';
|
||||
import '../services/whatsapp_service.dart';
|
||||
import '../models/conversation_model.dart';
|
||||
import '../models/message_model.dart';
|
||||
import 'conversations_controller.dart';
|
||||
|
||||
class ChatController extends GetxController {
|
||||
final ConversationModel conversation;
|
||||
@@ -26,6 +27,11 @@ class ChatController extends GetxController {
|
||||
super.onInit();
|
||||
_svc.activeChatId.value = conversation.id;
|
||||
|
||||
// Instantly clear the unread count badge in the UI
|
||||
try {
|
||||
Get.find<ConversationsController>().clearUnreadCount(conversation.id);
|
||||
} catch (_) {}
|
||||
|
||||
loadMessages();
|
||||
markAsRead();
|
||||
|
||||
@@ -93,6 +99,35 @@ class ChatController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Send Media Message ───────────────────────────────────────────────────
|
||||
Future<void> sendMediaMessage(String base64, String mimetype, String filename, {String? caption}) async {
|
||||
isSending.value = true;
|
||||
try {
|
||||
final res = await _svc.sendMedia(conversation.id, base64, mimetype, filename, caption: caption);
|
||||
if (res['type'] == 'message_sent') {
|
||||
final sentMsg = MessageModel.fromJson(res['data'] as Map<String, dynamic>);
|
||||
messages.add(sentMsg);
|
||||
_scrollToBottom();
|
||||
|
||||
// Also pre-cache local base64 in mediaCache to display instantly
|
||||
_svc.mediaCache[sentMsg.id] = base64;
|
||||
} else {
|
||||
Get.snackbar('Error', res['message'] ?? 'Failed to send media',
|
||||
backgroundColor: Colors.redAccent.withOpacity(0.8),
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[SEND MEDIA ERROR] $e');
|
||||
Get.snackbar('Error', 'Failed to send media: $e',
|
||||
backgroundColor: Colors.redAccent.withOpacity(0.8),
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isSending.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mark Chat as Read ────────────────────────────────────────────────────
|
||||
Future<void> markAsRead() async {
|
||||
try {
|
||||
@@ -144,17 +179,23 @@ class ChatController extends GetxController {
|
||||
|
||||
// ── Helper: Scroll to Bottom ─────────────────────────────────────────────
|
||||
void _scrollToBottom() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (scrollCtrl.hasClients) {
|
||||
scrollCtrl.animateTo(
|
||||
scrollCtrl.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _performScroll());
|
||||
// Also trigger after a short delay to account for async layout calculations
|
||||
Future.delayed(const Duration(milliseconds: 150), () {
|
||||
_performScroll();
|
||||
});
|
||||
}
|
||||
|
||||
void _performScroll() {
|
||||
if (scrollCtrl.hasClients) {
|
||||
scrollCtrl.animateTo(
|
||||
scrollCtrl.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Date Separator Logic ─────────────────────────────────────────────────
|
||||
List<dynamic> get groupedMessages {
|
||||
final list = <dynamic>[];
|
||||
|
||||
Reference in New Issue
Block a user