Files
m8chat-app2/lib/features/chat/presentation/chat_controller.dart
help4bis f12a7ac1fd feat: Phase 2 complete — calls, media, spaces, persistence, chat improvements
- LiveKit/MatrixRTC voice+video calls with full call screen UI
- Incoming call overlay (accept/decline)
- Media upload/download — file picker, image rendering, file download
- Spaces navigation — space list + expandable child rooms
- Drift persistence — rooms + messages written on every sync
- Sync persistence auto-starts on login and session restore
- Chat: typing indicators, long-press menu, reply, emoji reactions
- User search dialog + start DM from rooms screen
- Android: INTERNET + CAMERA + RECORD_AUDIO permissions in main manifest
- Emoji picker for reactions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 06:48:03 +10:00

105 lines
2.6 KiB
Dart

// 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<List<MessageModel>> 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<String?> 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<void> 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<String?> 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<String?> 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;
}
}
}