// Version: 1.1.0 | Created: 2026-04-01 // SpacesRepository — builds space list and child rooms from the Matrix SDK. // A space is a room where isSpace == true. import 'package:matrix/matrix.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../core/network/matrix_client.dart'; import '../domain/space_model.dart'; part 'spaces_repository.g.dart'; @riverpod SpacesRepository spacesRepository(Ref ref) { return SpacesRepository(client: ref.watch(matrixClientProvider)); } class SpacesRepository { SpacesRepository({required Client client}) : _client = client; final Client _client; /// Returns all rooms that are spaces. List getSpaces() { return _client.rooms.where((r) => r.isSpace).map(_toSpaceModel).toList(); } /// Returns child rooms within [spaceId] that the client is a member of. /// /// SpaceChild gives us the roomId only. We look up the Room from the client /// to get display name and avatar. If the child room is not in the client's /// room list (not joined), it is omitted. List getRoomsInSpace(String spaceId) { final space = _client.getRoomById(spaceId); if (space == null || !space.isSpace) return []; final result = []; for (final child in space.spaceChildren) { final childRoomId = child.roomId; if (childRoomId == null) continue; final room = _client.getRoomById(childRoomId); if (room == null) continue; // not joined — skip result.add( SpaceRoomModel( id: room.id, displayName: room.getLocalizedDisplayname(), avatarUrl: room.avatar?.toString(), isDirect: room.isDirectChat, ), ); } return result; } /// Stream that emits on every sync so the UI stays current. Stream> watchSpaces() async* { yield getSpaces(); yield* _client.onSync.stream.map((_) => getSpaces()); } SpaceModel _toSpaceModel(Room room) { return SpaceModel( id: room.id, displayName: room.getLocalizedDisplayname(), avatarUrl: room.avatar?.toString(), roomCount: room.spaceChildren.length, ); } }