don't pass room around, use many watches

This commit is contained in:
Henry Hiles 2026-03-29 14:14:11 -04:00
commit 60be7aaf72
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
15 changed files with 207 additions and 231 deletions

View file

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

View file

@ -2,24 +2,28 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_controller.dart";
import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/models/membership.dart"; import "package:nexus/models/membership.dart";
import "package:nexus/models/requests/get_room_state_request.dart"; import "package:nexus/models/requests/get_room_state_request.dart";
import "package:nexus/models/room.dart";
class MembersController extends AsyncNotifier<IList<Membership>> { class MembersController extends AsyncNotifier<IList<Membership>> {
final Room room;
MembersController(this.room);
@override @override
Future<IList<Membership>> build() async { Future<IList<Membership>> build() async {
if (room.metadata == null) return const IList.empty(); final data = ref.watch(
SelectedRoomController.provider.select(
(value) => value?.metadata == null
? null
: (value!.metadata!.id, value.metadata!.hasMemberList),
),
);
if (data == null) return const IList.empty();
final state = await ref final state = await ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)
.getRoomState( .getRoomState(
GetRoomStateRequest( GetRoomStateRequest(
roomId: room.metadata!.id, roomId: data.$1,
fetchMembers: room.metadata!.hasMemberList == false, fetchMembers: data.$2 == false,
includeMembers: true, includeMembers: true,
), ),
); );
@ -42,7 +46,7 @@ class MembersController extends AsyncNotifier<IList<Membership>> {
} }
static final provider = static final provider =
AsyncNotifierProvider.family<MembersController, IList<Membership>, Room>( AsyncNotifierProvider<MembersController, IList<Membership>>(
MembersController.new, MembersController.new,
); );
} }

View file

@ -223,13 +223,13 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
} }
Future<void> send( Future<void> send(
String message, { String text, {
bool shouldMention = true, bool shouldMention = true,
required Iterable<Tag> tags, required IList<Tag> tags,
required RelationType relationType, required RelationType relationType,
Message? relation, Message? relation,
}) async { }) async {
var taggedMessage = message; var taggedMessage = text;
for (final tag in tags) { for (final tag in tags) {
final escaped = RegExp.escape(tag.id); final escaped = RegExp.escape(tag.id);

View file

@ -1,14 +0,0 @@
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:freezed_annotation/freezed_annotation.dart";
import "package:nexus/models/room.dart";
part "author_config.freezed.dart";
part "author_config.g.dart";
@freezed
abstract class AuthorConfig with _$AuthorConfig {
const factory AuthorConfig({required Message message, required Room room}) =
_AuthorConfig;
factory AuthorConfig.fromJson(Map<String, Object?> json) =>
_$AuthorConfigFromJson(json);
}

View file

@ -1,12 +1,11 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter/services.dart"; import "package:flutter/services.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:fluttertagger/fluttertagger.dart"; import "package:fluttertagger/fluttertagger.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/models/relation_type.dart"; import "package:nexus/models/relation_type.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/chat_page/composer/mention_overlay.dart"; import "package:nexus/widgets/chat_page/composer/mention_overlay.dart";
import "package:nexus/widgets/chat_page/composer/relation_preview.dart"; import "package:nexus/widgets/chat_page/composer/relation_preview.dart";
@ -14,12 +13,17 @@ class ChatBox extends HookConsumerWidget {
final Message? relatedMessage; final Message? relatedMessage;
final RelationType relationType; final RelationType relationType;
final VoidCallback onDismiss; final VoidCallback onDismiss;
final Room room; final Future<void> Function(
String text, {
required bool shouldMention,
required IList<Tag> tags,
})
onSend;
const ChatBox({ const ChatBox({
required this.relatedMessage, required this.relatedMessage,
required this.relationType, required this.relationType,
required this.onDismiss, required this.onDismiss,
required this.room, required this.onSend,
super.key, super.key,
}); });
@ -38,16 +42,12 @@ class ChatBox extends HookConsumerWidget {
} }
void send() { void send() {
if (controller.value.text.trim().isEmpty || room.metadata == null) return; onSend(
ref
.watch(RoomChatController.provider(room.metadata!.id).notifier)
.send(
controller.value.formattedText, controller.value.formattedText,
shouldMention: shouldMention.value, shouldMention: shouldMention.value,
relation: relatedMessage, tags: controller.value.tags.toIList(),
relationType: relationType,
tags: controller.value.tags,
); );
onDismiss(); onDismiss();
controller.value.text = ""; controller.value.text = "";
} }
@ -81,7 +81,6 @@ class ChatBox extends HookConsumerWidget {
children: [ children: [
RelationPreview( RelationPreview(
relatedMessage, relatedMessage,
room: room,
shouldMention: shouldMention.value, shouldMention: shouldMention.value,
toggleShouldMention: () => toggleShouldMention: () =>
shouldMention.value = !shouldMention.value, shouldMention.value = !shouldMention.value,
@ -123,7 +122,6 @@ class ChatBox extends HookConsumerWidget {
child: FlutterTagger( child: FlutterTagger(
triggerStrategy: TriggerStrategy.eager, triggerStrategy: TriggerStrategy.eager,
overlay: MentionOverlay( overlay: MentionOverlay(
room,
query: query.value, query: query.value,
triggerCharacter: triggerCharacter.value, triggerCharacter: triggerCharacter.value,
addTag: ({required id, required name}) { addTag: ({required id, required name}) {

View file

@ -3,17 +3,14 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/members_controller.dart"; import "package:nexus/controllers/members_controller.dart";
import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/helpers/extensions/better_when.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/avatar_or_hash.dart";
import "package:nexus/widgets/loading.dart"; import "package:nexus/widgets/loading.dart";
class MentionOverlay extends ConsumerWidget { class MentionOverlay extends ConsumerWidget {
final String? triggerCharacter; final String? triggerCharacter;
final String query; final String query;
final Room room;
final void Function({required String id, required String name}) addTag; final void Function({required String id, required String name}) addTag;
const MentionOverlay( const MentionOverlay({
this.room, {
required this.query, required this.query,
required this.addTag, required this.addTag,
required this.triggerCharacter, required this.triggerCharacter,
@ -34,7 +31,7 @@ class MentionOverlay extends ConsumerWidget {
child: switch (triggerCharacter) { child: switch (triggerCharacter) {
"@" => "@" =>
ref ref
.watch(MembersController.provider(room)) .watch(MembersController.provider)
.betterWhen( .betterWhen(
data: (members) => ListView( data: (members) => ListView(
children: children:

View file

@ -2,7 +2,6 @@ import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/models/relation_type.dart"; import "package:nexus/models/relation_type.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
@ -12,11 +11,9 @@ class RelationPreview extends ConsumerWidget {
final VoidCallback onDismiss; final VoidCallback onDismiss;
final bool shouldMention; final bool shouldMention;
final VoidCallback toggleShouldMention; final VoidCallback toggleShouldMention;
final Room room;
const RelationPreview( const RelationPreview(
this.relatedMessage, { this.relatedMessage, {
required this.room,
required this.relationType, required this.relationType,
required this.onDismiss, required this.onDismiss,
required this.shouldMention, required this.shouldMention,
@ -41,10 +38,9 @@ class RelationPreview extends ConsumerWidget {
"Editing message:", "Editing message:",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
MessageAvatar(relatedMessage!, room), MessageAvatar(relatedMessage!),
MessageDisplayname( MessageDisplayname(
relatedMessage!, relatedMessage!,
room,
style: theme.textTheme.labelMedium?.copyWith( style: theme.textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),

View file

@ -3,21 +3,16 @@ import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/author_controller.dart"; import "package:nexus/controllers/author_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/models/configs/author_config.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/avatar_or_hash.dart";
class MessageAvatar extends ConsumerWidget { class MessageAvatar extends ConsumerWidget {
final Message message; final Message message;
final Room room;
final double height; final double height;
const MessageAvatar(this.message, this.room, {this.height = 16, super.key}); const MessageAvatar(this.message, {this.height = 16, super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) => ref Widget build(BuildContext context, WidgetRef ref) => ref
.watch( .watch(AuthorController.provider(message))
AuthorController.provider(AuthorConfig(room: room, message: message)),
)
.betterWhen( .betterWhen(
data: (membership) => AvatarOrHash( data: (membership) => AvatarOrHash(
membership.avatarUrl, membership.avatarUrl,

View file

@ -3,20 +3,15 @@ import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/author_controller.dart"; import "package:nexus/controllers/author_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/models/configs/author_config.dart";
import "package:nexus/models/room.dart";
class MessageDisplayname extends ConsumerWidget { class MessageDisplayname extends ConsumerWidget {
final Message message; final Message message;
final Room room;
final TextStyle? style; final TextStyle? style;
const MessageDisplayname(this.message, this.room, {this.style, super.key}); const MessageDisplayname(this.message, {this.style, super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) => ref Widget build(BuildContext context, WidgetRef ref) => ref
.watch( .watch(AuthorController.provider(message))
AuthorController.provider(AuthorConfig(room: room, message: message)),
)
.betterWhen( .betterWhen(
data: (membership) => Text( data: (membership) => Text(
"${membership.displayName} ${message.metadata?["pmp"] == null ? "" : "(via ${message.authorId})"}", "${membership.displayName} ${message.metadata?["pmp"] == null ? "" : "(via ${message.authorId})"}",

View file

@ -2,16 +2,14 @@ import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/members_controller.dart"; import "package:nexus/controllers/members_controller.dart";
import "package:nexus/helpers/extensions/better_when.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/avatar_or_hash.dart";
class MemberList extends ConsumerWidget { class MemberList extends ConsumerWidget {
final Room room; const MemberList({super.key});
const MemberList(this.room, {super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final membersProvider = ref.watch(MembersController.provider(room)); final membersProvider = ref.watch(MembersController.provider);
return Drawer( return Drawer(
shape: Border(), shape: Border(),
child: Column( child: Column(

View file

@ -3,10 +3,10 @@ import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/event_controller.dart"; import "package:nexus/controllers/event_controller.dart";
import "package:nexus/controllers/message_controller.dart"; import "package:nexus/controllers/message_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/models/configs/message_config.dart"; import "package:nexus/models/configs/message_config.dart";
import "package:nexus/models/requests/get_event_request.dart"; import "package:nexus/models/requests/get_event_request.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/chat_page/html/quoted.dart"; import "package:nexus/widgets/chat_page/html/quoted.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
@ -16,12 +16,10 @@ typedef OnTapReply = void Function(Message message)?;
class ReplyWidget extends ConsumerWidget { class ReplyWidget extends ConsumerWidget {
final Message message; final Message message;
final bool alwaysShow; final bool alwaysShow;
final Room room;
final MessageGroupStatus? groupStatus; final MessageGroupStatus? groupStatus;
final OnTapReply onTapReply; final OnTapReply onTapReply;
const ReplyWidget( const ReplyWidget(
this.message, { this.message, {
required this.room,
required this.groupStatus, required this.groupStatus,
this.onTapReply, this.onTapReply,
this.alwaysShow = false, this.alwaysShow = false,
@ -29,8 +27,9 @@ class ReplyWidget extends ConsumerWidget {
}); });
@override @override
Widget build(BuildContext context, WidgetRef ref) => Widget build(BuildContext context, WidgetRef ref) {
message.replyToMessageId == null final room = ref.watch(SelectedRoomController.provider);
return message.replyToMessageId == null || room == null
? SizedBox.shrink() ? SizedBox.shrink()
: Padding( : Padding(
padding: EdgeInsets.only(bottom: 12), padding: EdgeInsets.only(bottom: 12),
@ -67,11 +66,10 @@ class ReplyWidget extends ConsumerWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
spacing: 8, spacing: 8,
children: [ children: [
MessageAvatar(replyMessage, room), MessageAvatar(replyMessage),
Flexible( Flexible(
child: MessageDisplayname( child: MessageDisplayname(
replyMessage, replyMessage,
room,
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.labelMedium .labelMedium
@ -99,3 +97,4 @@ class ReplyWidget extends ConsumerWidget {
), ),
); );
} }
}

View file

@ -1,18 +1,17 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:nexus/models/room.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/chat_page/expandable_image.dart"; import "package:nexus/widgets/chat_page/expandable_image.dart";
import "package:nexus/widgets/chat_page/room_menu.dart"; import "package:nexus/widgets/chat_page/room_menu.dart";
class RoomAppbar extends StatelessWidget implements PreferredSizeWidget { class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
final bool isDesktop; final bool isDesktop;
final Room room;
final void Function(BuildContext context) onOpenMemberList; final void Function(BuildContext context) onOpenMemberList;
final void Function(BuildContext context) onOpenDrawer; final void Function(BuildContext context) onOpenDrawer;
const RoomAppbar( const RoomAppbar({
this.room, {
required this.isDesktop, required this.isDesktop,
required this.onOpenMemberList, required this.onOpenMemberList,
required this.onOpenDrawer, required this.onOpenDrawer,
@ -23,7 +22,9 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
Size get preferredSize => AppBar().preferredSize; Size get preferredSize => AppBar().preferredSize;
@override @override
Widget build(BuildContext context) => Appbar( Widget build(BuildContext context, WidgetRef ref) {
final room = ref.watch(SelectedRoomController.provider)!;
return Appbar(
leading: isDesktop leading: isDesktop
? ExpandableImage( ? ExpandableImage(
room.metadata?.avatar?.toString(), room.metadata?.avatar?.toString(),
@ -70,3 +71,4 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
].toIList(), ].toIList(),
); );
} }
}

View file

@ -35,16 +35,18 @@ class RoomChat extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final client = ref.watch(ClientController.provider.notifier); final client = ref.watch(ClientController.provider.notifier);
final replyToMessage = useState<Message?>(null); final relatedMessage = useState<Message?>(null);
final memberListOpened = useState<bool>(showMembersByDefault); final memberListOpened = useState<bool>(showMembersByDefault);
final relationType = useState(RelationType.reply); final relationType = useState(RelationType.reply);
final room = ref.watch(SelectedRoomController.provider);
final userId = ref.watch(ClientStateController.provider)?.userId; final userId = ref.watch(ClientStateController.provider)?.userId;
final roomId = ref.watch(
SelectedRoomController.provider.select((value) => value?.metadata?.id),
);
final theme = Theme.of(context); final theme = Theme.of(context);
final danger = theme.colorScheme.error; final danger = theme.colorScheme.error;
if (room == null || userId == null || room.metadata?.id == null) { if (roomId == null || userId == null) {
return Center( return Center(
child: Text( child: Text(
"Nothing to see here...", "Nothing to see here...",
@ -53,7 +55,7 @@ class RoomChat extends HookConsumerWidget {
); );
} }
final controllerProvider = RoomChatController.provider(room.metadata!.id); final controllerProvider = RoomChatController.provider(roomId);
final notifier = ref.watch(controllerProvider.notifier); final notifier = ref.watch(controllerProvider.notifier);
List<PopupMenuEntry> getMessageOptions(Message message) { List<PopupMenuEntry> getMessageOptions(Message message) {
@ -61,7 +63,7 @@ class RoomChat extends HookConsumerWidget {
return [ return [
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
replyToMessage.value = message; relatedMessage.value = message;
relationType.value = RelationType.reply; relationType.value = RelationType.reply;
}, },
child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")), child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")),
@ -69,7 +71,7 @@ class RoomChat extends HookConsumerWidget {
if (message is TextMessage && isSentByMe) if (message is TextMessage && isSentByMe)
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
replyToMessage.value = message; relatedMessage.value = message;
relationType.value = RelationType.edit; relationType.value = RelationType.edit;
}, },
child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")),
@ -153,10 +155,9 @@ class RoomChat extends HookConsumerWidget {
), ),
TextButton( TextButton(
onPressed: () { onPressed: () {
if (room.metadata == null) return;
client.reportEvent( client.reportEvent(
ReportRequest( ReportRequest(
roomId: room.metadata!.id, roomId: roomId,
eventId: message.id, eventId: message.id,
reason: reasonController.text.isEmpty reason: reasonController.text.isEmpty
? null ? null
@ -189,7 +190,6 @@ class RoomChat extends HookConsumerWidget {
return Scaffold( return Scaffold(
appBar: RoomAppbar( appBar: RoomAppbar(
room,
isDesktop: isDesktop, isDesktop: isDesktop,
onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), onOpenDrawer: (_) => Scaffold.of(context).openDrawer(),
onOpenMemberList: (thisContext) { onOpenMemberList: (thisContext) {
@ -237,18 +237,41 @@ class RoomChat extends HookConsumerWidget {
chatAnimatedListBuilder: (_, itemBuilder) => chatAnimatedListBuilder: (_, itemBuilder) =>
ChatAnimatedList( ChatAnimatedList(
itemBuilder: itemBuilder, itemBuilder: itemBuilder,
onEndReached: room.hasMore onEndReached:
ref.watch(
SelectedRoomController.provider.select(
(room) => room?.hasMore == true,
),
)
? notifier.loadOlder ? notifier.loadOlder
: null, : null,
onStartReached: () => client.markRead(room), onStartReached: () async {
final room = ref.watch(
SelectedRoomController.provider,
);
return room == null
? null
: await client.markRead(room);
},
bottomPadding: 72, bottomPadding: 72,
), ),
composerBuilder: (_) => ChatBox( composerBuilder: (_) => ChatBox(
onSend:
(
text, {
required shouldMention,
required tags,
}) => notifier.send(
text,
tags: tags,
relationType: relationType.value, relationType: relationType.value,
relatedMessage: replyToMessage.value, shouldMention: shouldMention,
onDismiss: () => replyToMessage.value = null, relation: relatedMessage.value,
room: room, ),
relationType: relationType.value,
relatedMessage: relatedMessage.value,
onDismiss: () => relatedMessage.value = null,
), ),
textMessageBuilder: textMessageBuilder:
@ -259,7 +282,6 @@ class RoomChat extends HookConsumerWidget {
required bool isSentByMe, required bool isSentByMe,
MessageGroupStatus? groupStatus, MessageGroupStatus? groupStatus,
}) => TextMessageWrapper( }) => TextMessageWrapper(
room: room,
message, message,
content: message.text, content: message.text,
groupStatus: groupStatus, groupStatus: groupStatus,
@ -277,7 +299,6 @@ class RoomChat extends HookConsumerWidget {
MessageGroupStatus? groupStatus, MessageGroupStatus? groupStatus,
}) => TextMessageWrapper( }) => TextMessageWrapper(
message, message,
room: room,
content: message.text, content: message.text,
groupStatus: groupStatus, groupStatus: groupStatus,
onTapReply: notifier.scrollToMessage, onTapReply: notifier.scrollToMessage,
@ -309,7 +330,6 @@ class RoomChat extends HookConsumerWidget {
), ),
child: FlyerChatFileMessage( child: FlyerChatFileMessage(
topWidget: ReplyWidget( topWidget: ReplyWidget(
room: room,
message, message,
onTapReply: notifier.scrollToMessage, onTapReply: notifier.scrollToMessage,
groupStatus: groupStatus, groupStatus: groupStatus,
@ -319,7 +339,6 @@ class RoomChat extends HookConsumerWidget {
), ),
), ),
groupStatus, groupStatus,
room,
), ),
systemMessageBuilder: systemMessageBuilder:
@ -358,11 +377,11 @@ class RoomChat extends HookConsumerWidget {
), ),
if (memberListOpened.value == true && showMembersByDefault) if (memberListOpened.value == true && showMembersByDefault)
MemberList(room), MemberList(),
], ],
), ),
endDrawer: showMembersByDefault ? null : MemberList(room), endDrawer: showMembersByDefault ? null : MemberList(),
); );
} }
} }

View file

@ -1,21 +1,13 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
class MessageWrapper extends StatelessWidget { class MessageWrapper extends StatelessWidget {
final Message message; final Message message;
final Widget child; final Widget child;
final Room room;
final MessageGroupStatus? groupStatus; final MessageGroupStatus? groupStatus;
const MessageWrapper( const MessageWrapper(this.message, this.child, this.groupStatus, {super.key});
this.message,
this.child,
this.groupStatus,
this.room, {
super.key,
});
@override @override
Widget build(BuildContext context) => ClipRRect( Widget build(BuildContext context) => ClipRRect(
@ -33,7 +25,7 @@ class MessageWrapper extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
groupStatus?.isFirst != false groupStatus?.isFirst != false
? MessageAvatar(message, room, height: 40) ? MessageAvatar(message, height: 40)
: SizedBox(width: 40), : SizedBox(width: 40),
Expanded( Expanded(
child: Column( child: Column(
@ -43,7 +35,6 @@ class MessageWrapper extends StatelessWidget {
if (groupStatus?.isFirst != false) if (groupStatus?.isFirst != false)
MessageDisplayname( MessageDisplayname(
message, message,
room,
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),

View file

@ -1,7 +1,6 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_link_previewer/flutter_link_previewer.dart"; import "package:flutter_link_previewer/flutter_link_previewer.dart";
import "package:nexus/models/room.dart";
import "package:nexus/widgets/chat_page/html/html.dart"; import "package:nexus/widgets/chat_page/html/html.dart";
import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart"; import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart";
import "package:nexus/widgets/chat_page/reply_widget.dart"; import "package:nexus/widgets/chat_page/reply_widget.dart";
@ -9,7 +8,6 @@ import "package:nexus/widgets/chat_page/reply_widget.dart";
class TextMessageWrapper extends StatelessWidget { class TextMessageWrapper extends StatelessWidget {
final Message message; final Message message;
final String? content; final String? content;
final Room room;
final MessageGroupStatus? groupStatus; final MessageGroupStatus? groupStatus;
final Future<void> Function(Message oldMessage, Message newMessage) final Future<void> Function(Message oldMessage, Message newMessage)
updateMessage; updateMessage;
@ -21,7 +19,6 @@ class TextMessageWrapper extends StatelessWidget {
this.message, { this.message, {
this.content, this.content,
this.onTapReply, this.onTapReply,
required this.room,
required this.updateMessage, required this.updateMessage,
required this.groupStatus, required this.groupStatus,
required this.isSentByMe, required this.isSentByMe,
@ -51,7 +48,6 @@ class TextMessageWrapper extends StatelessWidget {
children: [ children: [
ReplyWidget( ReplyWidget(
message, message,
room: room,
groupStatus: groupStatus, groupStatus: groupStatus,
onTapReply: onTapReply, onTapReply: onTapReply,
), ),
@ -109,7 +105,6 @@ class TextMessageWrapper extends StatelessWidget {
), ),
), ),
groupStatus, groupStatus,
room,
); );
} }
} }