lazy load memberships

This commit is contained in:
Henry Hiles 2026-03-22 16:35:15 -04:00
commit 9054b6b357
No known key found for this signature in database
14 changed files with 231 additions and 197 deletions

View file

@ -0,0 +1,44 @@
import "dart:async";
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/members_controller.dart";
import "package:nexus/models/configs/author_config.dart";
import "package:nexus/models/membership.dart";
class AuthorController extends AsyncNotifier<Membership> {
final AuthorConfig config;
AuthorController(this.config);
@override
Future<Membership> build() async {
var member = ref.watch(
MembersController.provider(config.room).select(
(value) => value.firstWhereOrNull(
(membership) => membership.userId == config.message.authorId,
),
),
);
final pmp = config.message.metadata?["pmp"] == null
? null
: Membership.fromContent(
IMap(config.message.metadata?["pmp"]),
config.message.authorId,
);
return Membership(
avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl,
displayName:
pmp?.displayName ??
member?.displayName ??
config.message.authorId.substring(1).split(":").first,
userId: config.message.authorId,
);
}
static final provider = AsyncNotifierProvider.family
.autoDispose<AuthorController, Membership, AuthorConfig>(
AuthorController.new,
);
}

View file

@ -1,42 +0,0 @@
import "dart:async";
import "package:collection/collection.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/members_controller.dart";
import "package:nexus/models/configs/member_config.dart";
import "package:nexus/models/membership.dart";
class MemberController extends AsyncNotifier<Membership> {
final MemberConfig config;
MemberController(this.config);
@override
FutureOr<Membership> build() {
final member = ref.watch(
MembersController.provider(config.room).select(
(value) => value.firstWhereOrNull(
(membership) => membership.userId == config.userId,
),
),
);
if (config.room.hasFetchedMembers || member != null) {
return member ??
Membership(
avatarUrl: null,
displayName: config.userId,
userId: config.userId,
);
}
return Membership(
avatarUrl: null,
displayName: config.userId,
userId: config.userId,
);
throw UnimplementedError();
}
static final provider = AsyncNotifierProvider.family
.autoDispose<MemberController, Membership, MemberConfig>(
MemberController.new,
);
}

View file

@ -1,7 +1,10 @@
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/models/event.dart";
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<IList<Membership>> {
@ -9,24 +12,42 @@ class MembersController extends Notifier<IList<Membership>> {
MembersController(this.room);
@override
IList<Membership> build() => (room.state["m.room.member"]?.values ?? [])
.map(
(eventRowId) =>
room.events.firstWhereOrNull((event) => event.rowId == eventRowId),
)
.nonNulls
.where((member) => member.content["membership"] == "join")
.map(
(membership) => Membership(
avatarUrl: Uri.tryParse(membership.content["avatar_url"] ?? ""),
userId: membership.stateKey!,
displayName: membership.content["displayname"] ?? membership.stateKey,
),
)
.toIList();
IList<Membership> build() {
IList<Membership> membersFromState(IList<Event> members) => members.nonNulls
.where((member) => member.content["membership"] == "join")
.map(
(membership) =>
Membership.fromContent(membership.content, membership.stateKey!),
)
.toIList();
static final provider = NotifierProvider.family
.autoDispose<MembersController, IList<Membership>, Room>(
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<MembersController, IList<Membership>, Room>(
MembersController.new,
);
}

View file

@ -45,6 +45,7 @@ class MessageController extends AsyncNotifier<Message?> {
"timelineId": event.timelineRowId,
"big": event.localContent?.bigEmoji == true,
"eventType": type,
"pmp": event.content["com.beeper.per_message_profile"],
"editSource":
event.localContent?.editSource ??
newContent?["body"] ??

View file

@ -36,6 +36,7 @@ class RoomsController extends Notifier<IMap<String, Room>> {
return acc.add(
roomId,
existing?.copyWith(
hasMore: incoming.hasMore,
metadata: incoming.metadata ?? existing.metadata,
events: events!,
state: incoming.state.entries.fold(