🐛 Add well defined mounted check in messages notifier

This commit is contained in:
2026-01-11 12:53:57 +08:00
parent eec181da55
commit bf59108569

View File

@@ -128,7 +128,7 @@ class MessagesNotifier extends _$MessagesNotifier {
uniqueMessages.add(message); uniqueMessages.add(message);
} }
} }
state = AsyncValue.data(uniqueMessages); if (ref.mounted) state = AsyncValue.data(uniqueMessages);
} finally { } finally {
_isUpdatingState = false; _isUpdatingState = false;
} }
@@ -350,7 +350,7 @@ class MessagesNotifier extends _$MessagesNotifier {
offset: 0, offset: 0,
take: _pageSize, take: _pageSize,
); );
state = AsyncValue.data(newMessages); if (ref.mounted) state = AsyncValue.data(newMessages);
return; return;
} }
@@ -408,7 +408,9 @@ class MessagesNotifier extends _$MessagesNotifier {
} finally { } finally {
talker.log('Finished message sync'); talker.log('Finished message sync');
// Always reset global syncing state, regardless of disposal // Always reset global syncing state, regardless of disposal
Future.microtask(() => ref.read(chatSyncingProvider.notifier).set(false)); Future.microtask(() {
if (ref.mounted) ref.read(chatSyncingProvider.notifier).set(false);
});
_isSyncing = false; _isSyncing = false;
} }
} }
@@ -498,7 +500,7 @@ class MessagesNotifier extends _$MessagesNotifier {
_hasMore = messages.length == _pageSize; _hasMore = messages.length == _pageSize;
state = AsyncValue.data(messages); if (ref.mounted) state = AsyncValue.data(messages);
} }
Future<void> loadMore() async { Future<void> loadMore() async {
@@ -509,7 +511,7 @@ class MessagesNotifier extends _$MessagesNotifier {
Future.microtask(() => ref.read(chatSyncingProvider.notifier).set(true)); Future.microtask(() => ref.read(chatSyncingProvider.notifier).set(true));
} }
try { try {
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
final offset = currentMessages.length; final offset = currentMessages.length;
final newMessages = await listMessages(offset: offset, take: _pageSize); final newMessages = await listMessages(offset: offset, take: _pageSize);
@@ -518,9 +520,11 @@ class MessagesNotifier extends _$MessagesNotifier {
_hasMore = false; _hasMore = false;
} }
if (ref.mounted) {
state = AsyncValue.data( state = AsyncValue.data(
_sortMessages([...currentMessages, ...newMessages]), _sortMessages([...currentMessages, ...newMessages]),
); );
}
} catch (err, stackTrace) { } catch (err, stackTrace) {
talker.log( talker.log(
'Error loading more messages', 'Error loading more messages',
@@ -531,12 +535,14 @@ class MessagesNotifier extends _$MessagesNotifier {
showErrorAlert(err); showErrorAlert(err);
} finally { } finally {
// Always reset global syncing state, regardless of disposal // Always reset global syncing state, regardless of disposal
Future.microtask(() => ref.read(chatSyncingProvider.notifier).set(false)); Future.microtask(() {
if (ref.mounted) ref.read(chatSyncingProvider.notifier).set(false);
});
} }
} }
Future<void> sendMessage( Future<void> sendMessage(
WidgetRef ref, WidgetRef outerRef,
String content, String content,
List<UniversalFile> attachments, { List<UniversalFile> attachments, {
SnPoll? poll, SnPoll? poll,
@@ -569,14 +575,14 @@ class MessagesNotifier extends _$MessagesNotifier {
_fileUploadProgress[localMessage.id] = {}; _fileUploadProgress[localMessage.id] = {};
await _database.saveMessageWithSender(localMessage); await _database.saveMessageWithSender(localMessage);
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
state = AsyncValue.data([localMessage, ...currentMessages]); state = AsyncValue.data([localMessage, ...currentMessages]);
try { try {
var cloudAttachments = List.empty(growable: true); var cloudAttachments = List.empty(growable: true);
for (var idx = 0; idx < attachments.length; idx++) { for (var idx = 0; idx < attachments.length; idx++) {
final cloudFile = await FileUploader.createCloudFile( final cloudFile = await FileUploader.createCloudFile(
ref: ref, ref: outerRef,
fileData: attachments[idx], fileData: attachments[idx],
onProgress: (progress, _) { onProgress: (progress, _) {
_fileUploadProgress[localMessage.id]?[idx] = progress ?? 0.0; _fileUploadProgress[localMessage.id]?[idx] = progress ?? 0.0;
@@ -619,6 +625,7 @@ class MessagesNotifier extends _$MessagesNotifier {
await _database.deleteMessage(localMessage.id); await _database.deleteMessage(localMessage.id);
await _database.saveMessageWithSender(updatedMessage); await _database.saveMessageWithSender(updatedMessage);
if (ref.mounted) {
final currentMessages = state.value ?? []; final currentMessages = state.value ?? [];
if (editingTo != null) { if (editingTo != null) {
final newMessages = currentMessages final newMessages = currentMessages
@@ -637,6 +644,8 @@ class MessagesNotifier extends _$MessagesNotifier {
}).toList(); }).toList();
state = AsyncValue.data(newMessages); state = AsyncValue.data(newMessages);
} }
}
talker.log('Message with nonce $nonce sent successfully'); talker.log('Message with nonce $nonce sent successfully');
} catch (e, stackTrace) { } catch (e, stackTrace) {
talker.log( talker.log(
@@ -651,6 +660,7 @@ class MessagesNotifier extends _$MessagesNotifier {
localMessage.id, localMessage.id,
MessageStatus.failed, MessageStatus.failed,
); );
if (ref.mounted) {
final newMessages = (state.value ?? []).map((m) { final newMessages = (state.value ?? []).map((m) {
if (m.id == localMessage.id) { if (m.id == localMessage.id) {
return m..status = MessageStatus.failed; return m..status = MessageStatus.failed;
@@ -658,6 +668,7 @@ class MessagesNotifier extends _$MessagesNotifier {
return m; return m;
}).toList(); }).toList();
state = AsyncValue.data(newMessages); state = AsyncValue.data(newMessages);
}
showErrorAlert(e); showErrorAlert(e);
} }
} }
@@ -698,6 +709,7 @@ class MessagesNotifier extends _$MessagesNotifier {
await _database.deleteMessage(pendingMessageId); await _database.deleteMessage(pendingMessageId);
await _database.saveMessageWithSender(updatedMessage); await _database.saveMessageWithSender(updatedMessage);
if (ref.mounted) {
final newMessages = (state.value ?? []).map((m) { final newMessages = (state.value ?? []).map((m) {
if (m.id == pendingMessageId) { if (m.id == pendingMessageId) {
return updatedMessage; return updatedMessage;
@@ -705,6 +717,7 @@ class MessagesNotifier extends _$MessagesNotifier {
return m; return m;
}).toList(); }).toList();
state = AsyncValue.data(newMessages); state = AsyncValue.data(newMessages);
}
} catch (e, stackTrace) { } catch (e, stackTrace) {
talker.log( talker.log(
'Failed to retry message $pendingMessageId', 'Failed to retry message $pendingMessageId',
@@ -718,6 +731,7 @@ class MessagesNotifier extends _$MessagesNotifier {
pendingMessageId, pendingMessageId,
MessageStatus.failed, MessageStatus.failed,
); );
if (ref.mounted) {
final newMessages = (state.value ?? []).map((m) { final newMessages = (state.value ?? []).map((m) {
if (m.id == pendingMessageId) { if (m.id == pendingMessageId) {
return m..status = MessageStatus.failed; return m..status = MessageStatus.failed;
@@ -725,6 +739,7 @@ class MessagesNotifier extends _$MessagesNotifier {
return m; return m;
}).toList(); }).toList();
state = AsyncValue.data(_sortMessages(newMessages)); state = AsyncValue.data(_sortMessages(newMessages));
}
showErrorAlert(e); showErrorAlert(e);
} }
} }
@@ -756,13 +771,14 @@ class MessagesNotifier extends _$MessagesNotifier {
if (!isSilentMessage) { if (!isSilentMessage) {
await _database.saveMessageWithSender(localMessage); await _database.saveMessageWithSender(localMessage);
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
final existingIndex = currentMessages.indexWhere( final existingIndex = currentMessages.indexWhere(
(m) => (m) =>
m.id == localMessage.id || m.id == localMessage.id ||
(localMessage.nonce != null && m.nonce == localMessage.nonce), (localMessage.nonce != null && m.nonce == localMessage.nonce),
); );
if (ref.mounted) {
if (existingIndex >= 0) { if (existingIndex >= 0) {
final newList = [...currentMessages]; final newList = [...currentMessages];
newList[existingIndex] = localMessage; newList[existingIndex] = localMessage;
@@ -773,6 +789,7 @@ class MessagesNotifier extends _$MessagesNotifier {
); );
} }
} }
}
switch (remoteMessage.type) { switch (remoteMessage.type) {
case "messages.delete": case "messages.delete":
@@ -837,15 +854,17 @@ class MessagesNotifier extends _$MessagesNotifier {
await _database.updateMessage(_database.messageToCompanion(updatedMessage)); await _database.updateMessage(_database.messageToCompanion(updatedMessage));
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
final index = currentMessages.indexWhere((m) => m.id == updatedMessage.id); final index = currentMessages.indexWhere((m) => m.id == updatedMessage.id);
if (ref.mounted) {
if (index >= 0) { if (index >= 0) {
final newList = [...currentMessages]; final newList = [...currentMessages];
newList[index] = updatedMessage; newList[index] = updatedMessage;
state = AsyncValue.data(_sortMessages(newList)); state = AsyncValue.data(_sortMessages(newList));
} }
} }
}
Future<void> receiveMessageDeletion(String messageId) async { Future<void> receiveMessageDeletion(String messageId) async {
// Block message deletions during jumps to prevent list resets // Block message deletions during jumps to prevent list resets
@@ -857,7 +876,7 @@ class MessagesNotifier extends _$MessagesNotifier {
talker.log('Received message deletion $messageId'); talker.log('Received message deletion $messageId');
_pendingMessages.remove(messageId); _pendingMessages.remove(messageId);
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
final messageIndex = currentMessages.indexWhere((m) => m.id == messageId); final messageIndex = currentMessages.indexWhere((m) => m.id == messageId);
LocalChatMessage? messageToUpdate; LocalChatMessage? messageToUpdate;
@@ -883,12 +902,14 @@ class MessagesNotifier extends _$MessagesNotifier {
await _database.saveMessageWithSender(deletedMessage); await _database.saveMessageWithSender(deletedMessage);
if (ref.mounted) {
if (messageIndex != -1) { if (messageIndex != -1) {
final newList = [...currentMessages]; final newList = [...currentMessages];
newList[messageIndex] = deletedMessage; newList[messageIndex] = deletedMessage;
state = AsyncValue.data(newList); state = AsyncValue.data(newList);
} }
} }
}
Future<void> deleteMessage(String messageId) async { Future<void> deleteMessage(String messageId) async {
talker.log('Deleting message $messageId'); talker.log('Deleting message $messageId');
@@ -907,7 +928,7 @@ class MessagesNotifier extends _$MessagesNotifier {
_pendingMessages.remove(messageId); _pendingMessages.remove(messageId);
await _database.deleteMessage(messageId); await _database.deleteMessage(messageId);
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
final newMessages = currentMessages final newMessages = currentMessages
.where((m) => m.id != messageId) .where((m) => m.id != messageId)
.toList(); .toList();
@@ -1063,7 +1084,7 @@ class MessagesNotifier extends _$MessagesNotifier {
} }
// Check if message is already in current state to avoid duplicate loading // Check if message is already in current state to avoid duplicate loading
final currentMessages = state.value ?? []; final currentMessages = (ref.mounted ? state.value : null) ?? [];
final existingIndex = currentMessages.indexWhere( final existingIndex = currentMessages.indexWhere(
(m) => m.id == messageId, (m) => m.id == messageId,
); );