diff --git a/lib/controllers/author_controller.dart b/lib/controllers/author_controller.dart index fac80e5..c7e4e05 100644 --- a/lib/controllers/author_controller.dart +++ b/lib/controllers/author_controller.dart @@ -12,8 +12,8 @@ class AuthorController extends AsyncNotifier { @override Future build() async { - var member = ref.watch( - MembersController.provider(config.room).select( + var member = await ref.watch( + MembersController.provider(config.room).selectAsync( (value) => value.firstWhereOrNull( (membership) => membership.userId == config.message.authorId, ), diff --git a/lib/controllers/members_controller.dart b/lib/controllers/members_controller.dart index 90d9b76..8cc164e 100644 --- a/lib/controllers/members_controller.dart +++ b/lib/controllers/members_controller.dart @@ -7,47 +7,35 @@ import "package:nexus/models/membership.dart"; import "package:nexus/models/requests/get_room_state_request.dart"; import "package:nexus/models/room.dart"; -class MembersController extends Notifier> { +class MembersController extends AsyncNotifier> { final Room room; MembersController(this.room); @override - IList build() { - IList membersFromState(IList members) => members.nonNulls + Future> build() async { + if (room.metadata == null) return const IList.empty(); + + final state = await ref + .watch(ClientController.provider.notifier) + .getRoomState( + GetRoomStateRequest( + roomId: room.metadata!.id, + fetchMembers: room.metadata!.hasMemberList == false, + includeMembers: true, + ), + ); + + return state.nonNulls .where((member) => member.content["membership"] == "join") .map( (membership) => Membership.fromContent(membership.content, membership.stateKey!), ) .toIList(); - - if (room.metadata != null) { - ref - .watch(ClientController.provider.notifier) - .getRoomState( - GetRoomStateRequest( - roomId: room.metadata!.id, - fetchMembers: room.metadata!.hasMemberList == false, - includeMembers: true, - ), - ) - .then((value) => state = membersFromState(value)); - } - - return membersFromState( - (room.state["m.room.members"]?.values ?? []) - .map( - (eventRowId) => room.events.firstWhereOrNull( - (event) => event.rowId == eventRowId, - ), - ) - .nonNulls - .toIList(), - ); } static final provider = - NotifierProvider.family, Room>( + AsyncNotifierProvider.family, Room>( MembersController.new, ); } diff --git a/lib/widgets/chat_page/composer/mention_overlay.dart b/lib/widgets/chat_page/composer/mention_overlay.dart index 75473f0..d95253d 100644 --- a/lib/widgets/chat_page/composer/mention_overlay.dart +++ b/lib/widgets/chat_page/composer/mention_overlay.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/models/room.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/loading.dart"; @@ -31,45 +32,47 @@ class MentionOverlay extends ConsumerWidget { color: Theme.of(context).colorScheme.surfaceContainerHigh, padding: EdgeInsets.all(8), child: switch (triggerCharacter) { - "@" => Consumer( - builder: (_, ref, _) { - final members = ref.watch(MembersController.provider(room)); - return ListView( - children: - (query.isEmpty - ? members - : members.where( - (member) => - member.userId.toLowerCase().contains( - query.toLowerCase(), - ) == - true || - member.displayName.toLowerCase().contains( - query.toLowerCase(), - ) == - true, - )) - .map( - (member) => ListTile( - leading: AvatarOrHash( - member.avatarUrl, - member.displayName, - ), - title: Text(member.displayName), - subtitle: Text(member.userId), - onTap: () => addTag( - id: "[@${member.displayName}](https://matrix.to/#/${member.userId})", - name: member.userId - .substring(1) - .split(":") - .first, - ), - ), - ) - .toList(), - ); - }, - ), + "@" => + ref + .watch(MembersController.provider(room)) + .betterWhen( + data: (members) => ListView( + children: + (query.isEmpty + ? members + : members.where( + (member) => + member.userId.toLowerCase().contains( + query.toLowerCase(), + ) == + true || + member.displayName + .toLowerCase() + .contains( + query.toLowerCase(), + ) == + true, + )) + .map( + (member) => ListTile( + leading: AvatarOrHash( + member.avatarUrl, + member.displayName, + ), + title: Text(member.displayName), + subtitle: Text(member.userId), + onTap: () => addTag( + id: "[@${member.displayName}](https://matrix.to/#/${member.userId})", + name: member.userId + .substring(1) + .split(":") + .first, + ), + ), + ) + .toList(), + ), + ), "#" => ListView( children: (query.isEmpty diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart index 08785c6..8cdbbb9 100644 --- a/lib/widgets/chat_page/member_list.dart +++ b/lib/widgets/chat_page/member_list.dart @@ -1,6 +1,7 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/models/room.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; @@ -10,15 +11,17 @@ class MemberList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final members = ref.watch(MembersController.provider(room)); + final membersProvider = ref.watch(MembersController.provider(room)); return Drawer( shape: Border(), - child: ListView( + child: Column( children: [ AppBar( scrolledUnderElevation: 0, leading: Icon(Icons.people), - title: Text("Members (${members.length})"), + title: Text( + "Members ${membersProvider.when(data: (members) => "${members.length}", error: (_, _) => "", loading: () => "")}", + ), actionsPadding: EdgeInsets.only(right: 4), actions: [ if (Scaffold.of(context).hasEndDrawer) @@ -29,16 +32,33 @@ class MemberList extends ConsumerWidget { ), ], ), - ...members.map( - (member) => ListTile( - onTap: () => showDialog( - context: context, - builder: (context) => - Dialog(child: Text("TODO: Open member popover")), + membersProvider.betterWhen( + data: (members) => Expanded( + child: ListView( + children: members + .map( + (member) => ListTile( + onTap: () => showDialog( + context: context, + builder: (context) => + Dialog(child: Text("TODO: Open member popover")), + ), + leading: AvatarOrHash( + member.avatarUrl, + member.displayName, + ), + title: Text( + member.displayName, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + member.userId, + overflow: TextOverflow.ellipsis, + ), + ), + ) + .toList(), ), - leading: AvatarOrHash(member.avatarUrl, member.displayName), - title: Text(member.displayName, overflow: TextOverflow.ellipsis), - subtitle: Text(member.userId, overflow: TextOverflow.ellipsis), ), ), ],