feat: Implement conversation mute and favorite features with UI integration and state management.
This commit is contained in:
@@ -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) {
|
final messagingRepositoryProvider = Provider<MessagingRepository>((ref) {
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ class Conversation {
|
|||||||
final int userUnreadCount;
|
final int userUnreadCount;
|
||||||
final int agentUnreadCount;
|
final int agentUnreadCount;
|
||||||
final int unreadCount;
|
final int unreadCount;
|
||||||
|
final bool userMuted;
|
||||||
|
final bool agentMuted;
|
||||||
|
final bool userFavorited;
|
||||||
|
final bool agentFavorited;
|
||||||
final String createdAt;
|
final String createdAt;
|
||||||
final String updatedAt;
|
final String updatedAt;
|
||||||
final OtherParty otherParty;
|
final OtherParty otherParty;
|
||||||
@@ -104,6 +108,10 @@ class Conversation {
|
|||||||
this.userUnreadCount = 0,
|
this.userUnreadCount = 0,
|
||||||
this.agentUnreadCount = 0,
|
this.agentUnreadCount = 0,
|
||||||
this.unreadCount = 0,
|
this.unreadCount = 0,
|
||||||
|
this.userMuted = false,
|
||||||
|
this.agentMuted = false,
|
||||||
|
this.userFavorited = false,
|
||||||
|
this.agentFavorited = false,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.otherParty,
|
required this.otherParty,
|
||||||
@@ -120,6 +128,10 @@ class Conversation {
|
|||||||
userUnreadCount: json['userUnreadCount'] as int? ?? 0,
|
userUnreadCount: json['userUnreadCount'] as int? ?? 0,
|
||||||
agentUnreadCount: json['agentUnreadCount'] as int? ?? 0,
|
agentUnreadCount: json['agentUnreadCount'] as int? ?? 0,
|
||||||
unreadCount: json['unreadCount'] 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,
|
createdAt: json['createdAt'] as String,
|
||||||
updatedAt: json['updatedAt'] as String,
|
updatedAt: json['updatedAt'] as String,
|
||||||
otherParty:
|
otherParty:
|
||||||
@@ -134,6 +146,10 @@ class Conversation {
|
|||||||
int? unreadCount,
|
int? unreadCount,
|
||||||
String? lastMessageAt,
|
String? lastMessageAt,
|
||||||
String? lastMessageText,
|
String? lastMessageText,
|
||||||
|
bool? userMuted,
|
||||||
|
bool? agentMuted,
|
||||||
|
bool? userFavorited,
|
||||||
|
bool? agentFavorited,
|
||||||
OtherParty? otherParty,
|
OtherParty? otherParty,
|
||||||
List<ChatMessage>? messages,
|
List<ChatMessage>? messages,
|
||||||
}) {
|
}) {
|
||||||
@@ -146,6 +162,10 @@ class Conversation {
|
|||||||
userUnreadCount: userUnreadCount,
|
userUnreadCount: userUnreadCount,
|
||||||
agentUnreadCount: agentUnreadCount,
|
agentUnreadCount: agentUnreadCount,
|
||||||
unreadCount: unreadCount ?? this.unreadCount,
|
unreadCount: unreadCount ?? this.unreadCount,
|
||||||
|
userMuted: userMuted ?? this.userMuted,
|
||||||
|
agentMuted: agentMuted ?? this.agentMuted,
|
||||||
|
userFavorited: userFavorited ?? this.userFavorited,
|
||||||
|
agentFavorited: agentFavorited ?? this.agentFavorited,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
otherParty: otherParty ?? this.otherParty,
|
otherParty: otherParty ?? this.otherParty,
|
||||||
|
|||||||
@@ -569,6 +569,55 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
|
|||||||
state = state.copyWith(error: 'Failed to delete conversation');
|
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 =
|
final messagingProvider =
|
||||||
|
|||||||
@@ -45,6 +45,17 @@ class _ChatScreenState extends ConsumerState<ChatScreen>
|
|||||||
notifier.setActiveConversation(widget.conversationId);
|
notifier.setActiveConversation(widget.conversationId);
|
||||||
notifier.loadMessages(widget.conversationId);
|
notifier.loadMessages(widget.conversationId);
|
||||||
notifier.markAsRead(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
|
// Suppress push notifications for this conversation while viewing
|
||||||
PushNotificationService().activeConversationId = widget.conversationId;
|
PushNotificationService().activeConversationId = widget.conversationId;
|
||||||
// Ensure socket is connected and join room for real-time updates
|
// Ensure socket is connected and join room for real-time updates
|
||||||
@@ -159,10 +170,12 @@ class _ChatScreenState extends ConsumerState<ChatScreen>
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'mute':
|
case 'mute':
|
||||||
setState(() => _isMuted = !_isMuted);
|
final newMuted = !_isMuted;
|
||||||
|
setState(() => _isMuted = newMuted);
|
||||||
|
ref.read(messagingProvider.notifier).toggleMute(widget.conversationId, newMuted);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(_isMuted ? 'Notifications muted' : 'Notifications unmuted'),
|
content: Text(newMuted ? 'Notifications muted' : 'Notifications unmuted'),
|
||||||
duration: const Duration(seconds: 2),
|
duration: const Duration(seconds: 2),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -540,7 +553,11 @@ class _ChatScreenState extends ConsumerState<ChatScreen>
|
|||||||
),
|
),
|
||||||
// Star icon
|
// Star icon
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => setState(() => _isStarred = !_isStarred),
|
onTap: () {
|
||||||
|
final newStarred = !_isStarred;
|
||||||
|
setState(() => _isStarred = newStarred);
|
||||||
|
ref.read(messagingProvider.notifier).toggleFavorite(widget.conversationId, newStarred);
|
||||||
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 4),
|
padding: const EdgeInsets.only(right: 4),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
|
|||||||
Reference in New Issue
Block a user