🐛 Fixes in chat subscribe

This commit is contained in:
2026-01-02 16:14:55 +08:00
parent 3b60fcb87c
commit c24d13461b
2 changed files with 69 additions and 27 deletions

View File

@@ -7,6 +7,7 @@ import "package:island/pods/chat/chat_room.dart";
import "package:island/pods/lifecycle.dart"; import "package:island/pods/lifecycle.dart";
import "package:island/pods/chat/messages_notifier.dart"; import "package:island/pods/chat/messages_notifier.dart";
import "package:island/pods/websocket.dart"; import "package:island/pods/websocket.dart";
import "package:island/talker.dart";
import "package:island/widgets/chat/call_button.dart"; import "package:island/widgets/chat/call_button.dart";
import "package:riverpod_annotation/riverpod_annotation.dart"; import "package:riverpod_annotation/riverpod_annotation.dart";
@@ -35,6 +36,22 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
Timer? _typingCooldownTimer; Timer? _typingCooldownTimer;
Timer? _periodicSubscribeTimer; Timer? _periodicSubscribeTimer;
StreamSubscription? _wsSubscription; StreamSubscription? _wsSubscription;
Function? _sendMessage;
void _cleanupResources() {
if (_wsSubscription != null) {
_wsSubscription!.cancel();
_wsSubscription = null;
}
if (_typingCleanupTimer != null) {
_typingCleanupTimer!.cancel();
_typingCleanupTimer = null;
}
if (_periodicSubscribeTimer != null) {
_periodicSubscribeTimer!.cancel();
_periodicSubscribeTimer = null;
}
}
@override @override
List<SnChatMember> build(String roomId) { List<SnChatMember> build(String roomId) {
@@ -43,6 +60,8 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
final chatIdentityAsync = ref.watch(chatRoomIdentityProvider(roomId)); final chatIdentityAsync = ref.watch(chatRoomIdentityProvider(roomId));
_messagesNotifier = ref.watch(messagesProvider(roomId).notifier); _messagesNotifier = ref.watch(messagesProvider(roomId).notifier);
_cleanupResources();
if (chatRoomAsync.isLoading || chatIdentityAsync.isLoading) { if (chatRoomAsync.isLoading || chatIdentityAsync.isLoading) {
return []; return [];
} }
@@ -56,7 +75,9 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
// Subscribe to messages // Subscribe to messages
final wsState = ref.read(websocketStateProvider.notifier); final wsState = ref.read(websocketStateProvider.notifier);
wsState.sendMessage( _sendMessage = wsState.sendMessage;
talker.info('[MessageSubscriber] Subscribing room $roomId');
_sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.subscribe', type: 'messages.subscribe',
@@ -93,7 +114,7 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
// Set up periodic subscribe timer (every 5 minutes) // Set up periodic subscribe timer (every 5 minutes)
_periodicSubscribeTimer = Timer.periodic(const Duration(minutes: 5), (_) { _periodicSubscribeTimer = Timer.periodic(const Duration(minutes: 5), (_) {
wsState.sendMessage( _sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.subscribe', type: 'messages.subscribe',
@@ -104,14 +125,13 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
); );
}); });
// Listen to app lifecycle changes
ref.listen(appLifecycleStateProvider, (previous, next) { ref.listen(appLifecycleStateProvider, (previous, next) {
final lifecycleState = next.value; final lifecycleState = next.value;
if (lifecycleState == AppLifecycleState.paused || if (lifecycleState == AppLifecycleState.paused ||
lifecycleState == AppLifecycleState.inactive) { lifecycleState == AppLifecycleState.inactive) {
// Unsubscribe when app goes to background // Unsubscribe when app goes to background
final wsState = ref.read(websocketStateProvider.notifier); talker.info('[MessageSubscriber] Unsubscribing room $roomId');
wsState.sendMessage( _sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.unsubscribe', type: 'messages.unsubscribe',
@@ -122,8 +142,8 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
); );
} else if (lifecycleState == AppLifecycleState.resumed) { } else if (lifecycleState == AppLifecycleState.resumed) {
// Resubscribe when app comes back to foreground // Resubscribe when app comes back to foreground
final wsState = ref.read(websocketStateProvider.notifier); talker.info('[MessageSubscriber] Subscribing room $roomId');
wsState.sendMessage( _sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.subscribe', type: 'messages.subscribe',
@@ -135,10 +155,15 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
} }
}); });
// Cleanup on dispose final subscribedNotifier = ref.watch(
ref.onDispose(() { currentSubscribedChatIdProvider.notifier,
ref.read(currentSubscribedChatIdProvider.notifier).set(null); );
wsState.sendMessage(
ref.onCancel(() {
talker.info('[MessageSubscriber] Unsubscribing room $roomId');
subscribedNotifier.set(null);
try {
_sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.unsubscribe', type: 'messages.unsubscribe',
@@ -147,10 +172,27 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
), ),
), ),
); );
_wsSubscription?.cancel(); } catch (e, stackTrace) {
_typingCleanupTimer?.cancel(); talker.error(
_typingCooldownTimer?.cancel(); '[MessageSubscriber] Error sending unsubscribe message for room $roomId: $e\n$stackTrace',
_periodicSubscribeTimer?.cancel(); );
}
try {
_cleanupResources();
} catch (e, stackTrace) {
talker.error(
'[MessageSubscriber] Error during cleanup for room $roomId: $e\n$stackTrace',
);
}
try {
if (_typingCooldownTimer != null) {
_typingCooldownTimer!.cancel();
}
} catch (e, stackTrace) {
talker.error(
'[MessageSubscriber] Error cancelling typing cooldown timer for room $roomId: $e\n$stackTrace',
);
}
}); });
return _typingStatuses; return _typingStatuses;
@@ -201,8 +243,8 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
void sendReadReceipt() { void sendReadReceipt() {
// Send websocket packet // Send websocket packet
final wsState = ref.read(websocketStateProvider.notifier); if (_sendMessage == null) return;
wsState.sendMessage( _sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.read', type: 'messages.read',
@@ -218,8 +260,8 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
if (_typingCooldownTimer != null) return; if (_typingCooldownTimer != null) return;
// Send typing status immediately // Send typing status immediately
final wsState = ref.read(websocketStateProvider.notifier); if (_sendMessage == null) return;
wsState.sendMessage( _sendMessage!(
jsonEncode( jsonEncode(
WebSocketPacket( WebSocketPacket(
type: 'messages.typing', type: 'messages.typing',

View File

@@ -59,7 +59,7 @@ final class ChatSubscribeNotifierProvider
} }
String _$chatSubscribeNotifierHash() => String _$chatSubscribeNotifierHash() =>
r'a05739450e6d23eb3d8c0a96078887b2b58ffd10'; r'7d720afd7e9f1bc4cc0e4308415e60a4781157db';
final class ChatSubscribeNotifierFamily extends $Family final class ChatSubscribeNotifierFamily extends $Family
with with