diff --git a/lib/features/messaging/data/messaging_repository.dart b/lib/features/messaging/data/messaging_repository.dart index d1e4965..150e889 100644 --- a/lib/features/messaging/data/messaging_repository.dart +++ b/lib/features/messaging/data/messaging_repository.dart @@ -186,6 +186,38 @@ class MessagingRepository { ); } } + + /// Toggle mute status for a conversation + Future toggleMute(String conversationId, bool muted) async { + try { + await _dio.patch( + '${ApiConstants.conversations}/$conversationId/mute', + data: {'muted': muted}, + ); + } on DioException catch (e) { + if (e.error is ApiException) throw e.error as ApiException; + throw ApiException( + message: e.message ?? 'Failed to toggle mute', + statusCode: e.response?.statusCode, + ); + } + } + + /// Toggle favorite status for a conversation + Future toggleFavorite(String conversationId, bool favorited) async { + try { + await _dio.patch( + '${ApiConstants.conversations}/$conversationId/favorite', + data: {'favorited': favorited}, + ); + } on DioException catch (e) { + if (e.error is ApiException) throw e.error as ApiException; + throw ApiException( + message: e.message ?? 'Failed to toggle favorite', + statusCode: e.response?.statusCode, + ); + } + } } final messagingRepositoryProvider = Provider((ref) { diff --git a/lib/features/messaging/data/models/messaging_models.dart b/lib/features/messaging/data/models/messaging_models.dart index 85ddf40..9cc6127 100644 --- a/lib/features/messaging/data/models/messaging_models.dart +++ b/lib/features/messaging/data/models/messaging_models.dart @@ -90,6 +90,10 @@ class Conversation { final int userUnreadCount; final int agentUnreadCount; final int unreadCount; + final bool userMuted; + final bool agentMuted; + final bool userFavorited; + final bool agentFavorited; final String createdAt; final String updatedAt; final OtherParty otherParty; @@ -104,6 +108,10 @@ class Conversation { this.userUnreadCount = 0, this.agentUnreadCount = 0, this.unreadCount = 0, + this.userMuted = false, + this.agentMuted = false, + this.userFavorited = false, + this.agentFavorited = false, required this.createdAt, required this.updatedAt, required this.otherParty, @@ -120,6 +128,10 @@ class Conversation { userUnreadCount: json['userUnreadCount'] as int? ?? 0, agentUnreadCount: json['agentUnreadCount'] as int? ?? 0, unreadCount: json['unreadCount'] as int? ?? 0, + userMuted: json['userMuted'] as bool? ?? false, + agentMuted: json['agentMuted'] as bool? ?? false, + userFavorited: json['userFavorited'] as bool? ?? false, + agentFavorited: json['agentFavorited'] as bool? ?? false, createdAt: json['createdAt'] as String, updatedAt: json['updatedAt'] as String, otherParty: @@ -134,6 +146,10 @@ class Conversation { int? unreadCount, String? lastMessageAt, String? lastMessageText, + bool? userMuted, + bool? agentMuted, + bool? userFavorited, + bool? agentFavorited, OtherParty? otherParty, List? messages, }) { @@ -146,6 +162,10 @@ class Conversation { userUnreadCount: userUnreadCount, agentUnreadCount: agentUnreadCount, unreadCount: unreadCount ?? this.unreadCount, + userMuted: userMuted ?? this.userMuted, + agentMuted: agentMuted ?? this.agentMuted, + userFavorited: userFavorited ?? this.userFavorited, + agentFavorited: agentFavorited ?? this.agentFavorited, createdAt: createdAt, updatedAt: updatedAt, otherParty: otherParty ?? this.otherParty, diff --git a/lib/features/messaging/presentation/providers/messaging_provider.dart b/lib/features/messaging/presentation/providers/messaging_provider.dart index d0343e9..171f7ce 100644 --- a/lib/features/messaging/presentation/providers/messaging_provider.dart +++ b/lib/features/messaging/presentation/providers/messaging_provider.dart @@ -569,6 +569,55 @@ class MessagingNotifier extends StateNotifier { state = state.copyWith(error: 'Failed to delete conversation'); } } + + Future toggleMute(String conversationId, bool muted) async { + try { + await _repository.toggleMute(conversationId, muted); + final currentUserId = _ref.read(currentUserIdProvider); + final updatedConversations = state.conversations.map((c) { + if (c.id == conversationId) { + final isUser = c.userId == currentUserId; + return isUser + ? c.copyWith(userMuted: muted) + : c.copyWith(agentMuted: muted); + } + return c; + }).toList(); + state = state.copyWith(conversations: updatedConversations); + } catch (_) { + state = state.copyWith(error: 'Failed to toggle mute'); + } + } + + Future toggleFavorite(String conversationId, bool favorited) async { + try { + await _repository.toggleFavorite(conversationId, favorited); + final currentUserId = _ref.read(currentUserIdProvider); + var updatedConversations = state.conversations.map((c) { + if (c.id == conversationId) { + final isUser = c.userId == currentUserId; + return isUser + ? c.copyWith(userFavorited: favorited) + : c.copyWith(agentFavorited: favorited); + } + return c; + }).toList(); + // Sort: favorites first, then by lastMessageAt + updatedConversations.sort((a, b) { + final aFav = a.userId == currentUserId ? a.userFavorited : a.agentFavorited; + final bFav = b.userId == currentUserId ? b.userFavorited : b.agentFavorited; + if (aFav != bFav) { + return aFav ? -1 : 1; + } + final aTime = a.lastMessageAt ?? ''; + final bTime = b.lastMessageAt ?? ''; + return bTime.compareTo(aTime); + }); + state = state.copyWith(conversations: updatedConversations); + } catch (_) { + state = state.copyWith(error: 'Failed to toggle favorite'); + } + } } final messagingProvider = diff --git a/lib/features/messaging/presentation/screens/chat_screen.dart b/lib/features/messaging/presentation/screens/chat_screen.dart index 52a1552..debd13a 100644 --- a/lib/features/messaging/presentation/screens/chat_screen.dart +++ b/lib/features/messaging/presentation/screens/chat_screen.dart @@ -45,6 +45,17 @@ class _ChatScreenState extends ConsumerState notifier.setActiveConversation(widget.conversationId); notifier.loadMessages(widget.conversationId); notifier.markAsRead(widget.conversationId); + // Initialize mute/star state from conversation data + final conversations = ref.read(messagingProvider).conversations; + final conv = conversations.where((c) => c.id == widget.conversationId).firstOrNull; + if (conv != null) { + final currentUserId = ref.read(currentUserIdProvider); + final isUser = conv.userId == currentUserId; + setState(() { + _isMuted = isUser ? conv.userMuted : conv.agentMuted; + _isStarred = isUser ? conv.userFavorited : conv.agentFavorited; + }); + } // Suppress push notifications for this conversation while viewing PushNotificationService().activeConversationId = widget.conversationId; // Ensure socket is connected and join room for real-time updates @@ -159,10 +170,12 @@ class _ChatScreenState extends ConsumerState ); break; case 'mute': - setState(() => _isMuted = !_isMuted); + final newMuted = !_isMuted; + setState(() => _isMuted = newMuted); + ref.read(messagingProvider.notifier).toggleMute(widget.conversationId, newMuted); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(_isMuted ? 'Notifications muted' : 'Notifications unmuted'), + content: Text(newMuted ? 'Notifications muted' : 'Notifications unmuted'), duration: const Duration(seconds: 2), ), ); @@ -540,7 +553,11 @@ class _ChatScreenState extends ConsumerState ), // Star icon GestureDetector( - onTap: () => setState(() => _isStarred = !_isStarred), + onTap: () { + final newStarred = !_isStarred; + setState(() => _isStarred = newStarred); + ref.read(messagingProvider.notifier).toggleFavorite(widget.conversationId, newStarred); + }, child: Padding( padding: const EdgeInsets.only(right: 4), child: Icon(