2026-04-15
This commit is contained in:
1 parent
d01e5ba860
commit
96de59e9be
46 files changed
+1299
-1191
No files matched your search
@@ -185,10 +185,14 @@ class ServiceHistoryNotifier
|
||||
fetchServiceHistory();
|
||||
}
|
||||
|
||||
Future<void> fetchServiceHistory() async {
|
||||
Future<void> fetchServiceHistory({bool isSilent = false}) async {
|
||||
try {
|
||||
if (!mounted) return;
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
// 🔄 Skip loading state for silent refresh
|
||||
if (!isSilent) {
|
||||
state = const AsyncValue.loading();
|
||||
}
|
||||
|
||||
final repo = ref.read(apiRepositoryProvider);
|
||||
|
||||
@@ -238,8 +242,6 @@ final notificationTriggerProvider = StateProvider<int>((ref) => 0);
|
||||
// 🔥 fetch notification count API
|
||||
final notificationCountProvider =
|
||||
FutureProvider.autoDispose<NotificationCountModel>((ref) async {
|
||||
ref.watch(notificationTriggerProvider); // listen for refresh trigger
|
||||
|
||||
final repo = ref.read(apiRepositoryProvider);
|
||||
return await repo.fetchNotificationCount();
|
||||
});
|
||||
@@ -409,34 +411,55 @@ class ChatMessagesNotifier
|
||||
return dateA.compareTo(dateB);
|
||||
});
|
||||
|
||||
// ⭐ Check if we have any new messages
|
||||
final lastMessageId = _messages.isNotEmpty ? _messages.last.id : -1;
|
||||
final latestMessageId = result.last.id;
|
||||
// ⭐ Merge strategy:
|
||||
// 1. Update existing messages (read/delivered status)
|
||||
// 2. Add new messages
|
||||
bool hasChanges = false;
|
||||
|
||||
if (latestMessageId != lastMessageId) {
|
||||
// ⭐ New messages found - merge them smoothly
|
||||
print("✅ Found new messages, updating list");
|
||||
for (var incomingMsg in result) {
|
||||
final existingIndex = _messages.indexWhere((m) => m.id == incomingMsg.id);
|
||||
|
||||
if (existingIndex != -1) {
|
||||
// Check if status changed
|
||||
final existingMsg = _messages[existingIndex];
|
||||
if (existingMsg.isRead != incomingMsg.isRead ||
|
||||
existingMsg.isDelivered != incomingMsg.isDelivered) {
|
||||
print("♻️ Updating status for message ID ${incomingMsg.id}");
|
||||
_messages[existingIndex] = incomingMsg;
|
||||
hasChanges = true;
|
||||
}
|
||||
} else {
|
||||
// ⭐ NEW: Check if this server message replaces an optimistic one
|
||||
final optimisticIndex = _messages.indexWhere(
|
||||
(m) => (m.id > 1000000000000 &&
|
||||
m.message == incomingMsg.message &&
|
||||
m.chatBy == incomingMsg.chatBy)
|
||||
);
|
||||
|
||||
// Add only new messages that don't exist in current list
|
||||
for (var newMsg in result) {
|
||||
final exists = _messages.any((m) => m.id == newMsg.id);
|
||||
if (!exists) {
|
||||
_messages.add(newMsg);
|
||||
if (optimisticIndex != -1) {
|
||||
print("🔄 refresh: Replacing optimistic message with ID ${incomingMsg.id}");
|
||||
_messages[optimisticIndex] = incomingMsg;
|
||||
hasChanges = true;
|
||||
} else {
|
||||
// Truly new message (e.g. incoming from someone else)
|
||||
print("➕ Adding new message ID ${incomingMsg.id}");
|
||||
_messages.add(incomingMsg);
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the entire list
|
||||
if (hasChanges) {
|
||||
// Sort the entire list to be sure
|
||||
_messages.sort((a, b) {
|
||||
final dateA =
|
||||
DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
|
||||
final dateB =
|
||||
DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
|
||||
final dateA = DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
|
||||
final dateB = DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
|
||||
return dateA.compareTo(dateB);
|
||||
});
|
||||
|
||||
|
||||
if (mounted) state = AsyncData(List.from(_messages));
|
||||
} else {
|
||||
print("✅ No new messages");
|
||||
print("✅ No new messages or status changes");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -472,10 +495,8 @@ class ChatMessagesNotifier
|
||||
|
||||
// 2. Check if this is a server confirmation of an optimistic message
|
||||
// Optimistic messages have large positive IDs from DateTime.now().millisecondsSinceEpoch
|
||||
// OR content match for very recent messages
|
||||
final optimisticIndex = _messages.indexWhere(
|
||||
(m) =>
|
||||
// Match by content and sender if it's a very recent "local" message
|
||||
(m.id > 1000000000000 &&
|
||||
m.message == msg.message &&
|
||||
m.chatBy == msg.chatBy),
|
||||
@@ -489,6 +510,13 @@ class ChatMessagesNotifier
|
||||
_messages.add(msg);
|
||||
}
|
||||
|
||||
// Sort to ensure order (important if multiple local messages are sent)
|
||||
_messages.sort((a, b) {
|
||||
final dateA = DateTime.tryParse(a.createdAt ?? '') ?? DateTime(1970);
|
||||
final dateB = DateTime.tryParse(b.createdAt ?? '') ?? DateTime(1970);
|
||||
return dateA.compareTo(dateB);
|
||||
});
|
||||
|
||||
if (mounted) state = AsyncData(List.from(_messages));
|
||||
}
|
||||
|
||||
|
||||
@@ -458,7 +458,7 @@ class ApiRepository {
|
||||
headers: await _authorizedHeaders(),
|
||||
);
|
||||
});
|
||||
|
||||
debugPrint("response: ${response.body}");
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
return DetailModel.fromJson(data);
|
||||
@@ -635,17 +635,17 @@ class ApiRepository {
|
||||
File? panFile,
|
||||
File? gstFile,
|
||||
File? incorporationFile,
|
||||
required int countryId,
|
||||
required int stateId,
|
||||
required int cityId,
|
||||
required String companyPincode,
|
||||
required String companyAddress,
|
||||
required String panNumber,
|
||||
required String gstNumber,
|
||||
required String tanNumber,
|
||||
required String cinNumber,
|
||||
required String yearOfIncorporation,
|
||||
required String address,
|
||||
int? countryId,
|
||||
int? stateId,
|
||||
int? cityId,
|
||||
String? companyPincode,
|
||||
String? companyAddress,
|
||||
String? panNumber,
|
||||
String? gstNumber,
|
||||
String? tanNumber,
|
||||
String? cinNumber,
|
||||
String? yearOfIncorporation,
|
||||
String? address,
|
||||
}) async {
|
||||
try {
|
||||
final token = await _localStore.getToken();
|
||||
@@ -658,17 +658,17 @@ class ApiRepository {
|
||||
'Connection': 'keep-alive',
|
||||
})
|
||||
..fields.addAll({
|
||||
'country_id': countryId.toString(),
|
||||
'state_id': stateId.toString(),
|
||||
'district_id': cityId.toString(),
|
||||
'company_pincode': companyPincode,
|
||||
'company_address': companyAddress,
|
||||
'pan_number': panNumber,
|
||||
'gst_number': gstNumber,
|
||||
'tan_number': tanNumber,
|
||||
'cin': cinNumber,
|
||||
'year_of_incorporation': yearOfIncorporation,
|
||||
'address': address,
|
||||
if (countryId != null) 'country_id': countryId.toString(),
|
||||
if (stateId != null) 'state_id': stateId.toString(),
|
||||
if (cityId != null) 'district_id': cityId.toString(),
|
||||
if (companyPincode != null) 'company_pincode': companyPincode,
|
||||
if (companyAddress != null) 'company_address': companyAddress,
|
||||
if (panNumber != null) 'pan_number': panNumber,
|
||||
if (gstNumber != null) 'gst_number': gstNumber,
|
||||
if (tanNumber != null) 'tan_number': tanNumber,
|
||||
if (cinNumber != null) 'cin': cinNumber,
|
||||
if (yearOfIncorporation != null) 'year_of_incorporation': yearOfIncorporation,
|
||||
if (address != null) 'address': address,
|
||||
});
|
||||
|
||||
await Future.wait([
|
||||
@@ -686,7 +686,8 @@ class ApiRepository {
|
||||
);
|
||||
|
||||
final response = await http.Response.fromStream(streamedResponse);
|
||||
|
||||
debugPrint('📦 KYC Response Status: ${response.statusCode}');
|
||||
debugPrint('📦 KYC Response Body: ${response.body}');
|
||||
// Handle token expiry
|
||||
if (response.statusCode == 401 || response.statusCode == 403) {
|
||||
await _handleUnauthorized();
|
||||
|
||||
Reference in new issue
Block a user