feat: Implement conversation mute and favorite features with UI integration and state management.

This commit is contained in:
pradeepkumar
2026-03-19 17:03:44 +05:30
parent 32ec839016
commit 858c912df9
4 changed files with 121 additions and 3 deletions

View File

@@ -186,6 +186,38 @@ class MessagingRepository {
);
}
}
/// Toggle mute status for a conversation
Future<void> 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<void> 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<MessagingRepository>((ref) {

View File

@@ -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<ChatMessage>? 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,

View File

@@ -569,6 +569,55 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
state = state.copyWith(error: 'Failed to delete conversation');
}
}
Future<void> 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<void> 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 =

View File

@@ -45,6 +45,17 @@ class _ChatScreenState extends ConsumerState<ChatScreen>
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<ChatScreen>
);
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<ChatScreen>
),
// 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(