// Version: 1.1.0 | Created: 2026-04-01 // Riverpod providers for chat timeline, send, upload, react, reply. import 'package:matrix/matrix.dart' show MatrixFile; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../data/chat_repository.dart'; import '../domain/message_model.dart'; part 'chat_controller.g.dart'; /// Streams the message list for [roomId]. @riverpod Stream> chatTimeline(Ref ref, String roomId) { final repo = ref.watch(chatRepositoryProvider); return repo.watchTimeline(roomId); } /// Sends a text message. Returns an error string on failure, null on success. /// Also handles sending replies when [inReplyToEventId] is set. @riverpod class SendMessage extends _$SendMessage { @override bool build() => false; // isSending Future send( String roomId, String text, { String? inReplyToEventId, }) async { if (text.trim().isEmpty) return null; state = true; try { await ref .read(chatRepositoryProvider) .sendTextMessage(roomId, text, inReplyToEventId: inReplyToEventId); return null; } on Exception catch (e) { return e.toString(); } finally { state = false; } } } /// Uploads a file and sends it as a room message. /// State: null = idle, empty string = uploading, non-empty = error message. @riverpod class UploadFile extends _$UploadFile { @override String? build() => null; // null = idle Future upload(String roomId, MatrixFile file) async { state = ''; // uploading try { await ref.read(chatRepositoryProvider).sendFile(roomId, file); state = null; // success — back to idle } on Exception catch (e) { state = e.toString(); // error } } void clearError() => state = null; } /// Sends an emoji reaction to [eventId]. @riverpod class SendReaction extends _$SendReaction { @override bool build() => false; Future react(String roomId, String eventId, String emoji) async { state = true; try { await ref .read(chatRepositoryProvider) .sendReaction(roomId, eventId, emoji); return null; } on Exception catch (e) { return e.toString(); } finally { state = false; } } } /// Deletes (redacts) a message by eventId. @riverpod class DeleteMessage extends _$DeleteMessage { @override bool build() => false; Future delete(String roomId, String eventId) async { state = true; try { await ref.read(chatRepositoryProvider).redactEvent(roomId, eventId); return null; } on Exception catch (e) { return e.toString(); } finally { state = false; } } }