Compare commits

..

No commits in common. "9054b6b357f775b2ecbd99212aa121b360716927" and "edbc647a06ccdc96733f52bd717ebf1662e4a29f" have entirely different histories.

23 changed files with 253 additions and 356 deletions

View file

@ -54,7 +54,6 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [x] HTML/Markdown - [x] HTML/Markdown
- [x] Replies - [x] Replies
- [x] Choose ping on/off - [x] Choose ping on/off
- [ ] Per message profiles
- [ ] Attachments - [ ] Attachments
- [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391) - [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391)
- [x] Mentions - [x] Mentions
@ -65,7 +64,6 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [ ] GIFs using Gomuks' GIF proxies - [ ] GIFs using Gomuks' GIF proxies
- [x] Recieving - [x] Recieving
- [x] Plain text - [x] Plain text
- [x] Per message profiles
- [x] HTML - [x] HTML
- [x] Replies - [x] Replies
- [x] Viewing - [x] Viewing

View file

@ -1,44 +0,0 @@
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,53 +1,25 @@
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_riverpod/flutter_riverpod.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/event.dart";
import "package:nexus/models/membership.dart";
import "package:nexus/models/requests/get_room_state_request.dart";
import "package:nexus/models/room.dart"; import "package:nexus/models/room.dart";
class MembersController extends Notifier<IList<Membership>> { class MembersController extends Notifier<IList<Event>> {
final Room room; final Room room;
MembersController(this.room); MembersController(this.room);
@override @override
IList<Membership> build() { IList<Event> build() => (room.state["m.room.member"]?.values ?? [])
IList<Membership> membersFromState(IList<Event> members) => members.nonNulls
.where((member) => member.content["membership"] == "join")
.map( .map(
(membership) => (eventRowId) =>
Membership.fromContent(membership.content, membership.stateKey!), room.events.firstWhereOrNull((event) => event.rowId == eventRowId),
)
.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 .nonNulls
.toIList(), .where((member) => member.content["membership"] == "join")
); .toIList();
}
static final provider = static final provider = NotifierProvider.family
NotifierProvider.family<MembersController, IList<Membership>, Room>( .autoDispose<MembersController, IList<Event>, Room>(
MembersController.new, MembersController.new,
); );
} }

View file

@ -2,8 +2,9 @@ import "package:collection/collection.dart";
import "package:flutter_chat_core/flutter_chat_core.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/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart";
import "package:nexus/models/configs/message_config.dart"; import "package:nexus/models/message_config.dart";
class MessageController extends AsyncNotifier<Message?> { class MessageController extends AsyncNotifier<Message?> {
final MessageConfig config; final MessageConfig config;
@ -26,6 +27,12 @@ class MessageController extends AsyncNotifier<Message?> {
if (!ref.mounted) return null; if (!ref.mounted) return null;
final members = ref.read(MembersController.provider(config.room));
final author = members.firstWhereOrNull(
(member) => member.stateKey == event.authorId,
);
if (!ref.mounted) return null;
final content = (event.decrypted ?? event.content); final content = (event.decrypted ?? event.content);
final type = (config.event.decryptedType ?? config.event.type); final type = (config.event.decryptedType ?? config.event.type);
final newContent = content["m.new_content"] as Map?; final newContent = content["m.new_content"] as Map?;
@ -45,11 +52,14 @@ class MessageController extends AsyncNotifier<Message?> {
"timelineId": event.timelineRowId, "timelineId": event.timelineRowId,
"big": event.localContent?.bigEmoji == true, "big": event.localContent?.bigEmoji == true,
"eventType": type, "eventType": type,
"pmp": event.content["com.beeper.per_message_profile"], "avatarUrl": author?.content["avatar_url"],
"editSource": "editSource":
event.localContent?.editSource ?? event.localContent?.editSource ??
newContent?["body"] ?? newContent?["body"] ??
content["body"], content["body"],
"displayName": author?.content["displayname"]?.isNotEmpty == true
? author?.content["displayname"]
: event.authorId.substring(1).split(":")[0],
"txnId": config.event.transactionId, "txnId": config.event.transactionId,
}; };

View file

@ -2,8 +2,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_chat_core/flutter_chat_core.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/message_controller.dart"; import "package:nexus/controllers/message_controller.dart";
import "package:nexus/models/configs/message_config.dart"; import "package:nexus/models/message_config.dart";
import "package:nexus/models/configs/messages_config.dart"; import "package:nexus/models/messages_config.dart";
class MessagesController extends AsyncNotifier<IList<Message>> { class MessagesController extends AsyncNotifier<IList<Message>> {
final MessagesConfig config; final MessagesConfig config;

View file

@ -1,4 +1,5 @@
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_chat_core/flutter_chat_core.dart";
@ -10,8 +11,8 @@ import "package:nexus/controllers/message_controller.dart";
import "package:nexus/controllers/messages_controller.dart"; import "package:nexus/controllers/messages_controller.dart";
import "package:nexus/controllers/new_events_controller.dart"; import "package:nexus/controllers/new_events_controller.dart";
import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/models/configs/messages_config.dart"; import "package:nexus/models/message_config.dart";
import "package:nexus/models/configs/message_config.dart"; import "package:nexus/models/messages_config.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/requests/paginate_request.dart"; import "package:nexus/models/requests/paginate_request.dart";
import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/requests/redact_event_request.dart";
@ -30,7 +31,11 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
if (room == null) return InMemoryChatController(); if (room == null) return InMemoryChatController();
final state = await client.getRoomState( final state = await client.getRoomState(
GetRoomStateRequest(roomId: roomId), GetRoomStateRequest(
roomId: roomId,
fetchMembers: room.metadata?.hasMemberList == false,
includeMembers: true,
),
); );
ref ref

View file

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

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,22 +0,0 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:freezed_annotation/freezed_annotation.dart";
part "membership.freezed.dart";
@freezed
abstract class Membership with _$Membership {
const Membership._();
const factory Membership({
required Uri? avatarUrl,
required String displayName,
required String userId,
}) = _Membership;
factory Membership.fromContent(
IMap<String, dynamic> content,
String userId,
) => Membership(
avatarUrl: Uri.tryParse(content["avatar_url"] ?? ""),
userId: userId,
displayName: content["displayname"] ?? userId.substring(1).split(":").first,
);
}

View file

@ -6,7 +6,7 @@ part "get_room_state_request.g.dart";
abstract class GetRoomStateRequest with _$GetRoomStateRequest { abstract class GetRoomStateRequest with _$GetRoomStateRequest {
const factory GetRoomStateRequest({ const factory GetRoomStateRequest({
required String roomId, required String roomId,
@Default(false) bool fetchMembers, required bool fetchMembers,
@Default(false) bool includeMembers, @Default(false) bool includeMembers,
}) = _GetRoomStateRequest; }) = _GetRoomStateRequest;

View file

@ -86,11 +86,10 @@ class ChatBox extends HookConsumerWidget {
child: Column( child: Column(
children: [ children: [
RelationPreview( RelationPreview(
relatedMessage,
room: room,
shouldMention: shouldMention.value, shouldMention: shouldMention.value,
toggleShouldMention: () => toggleShouldMention: () =>
shouldMention.value = !shouldMention.value, shouldMention.value = !shouldMention.value,
relatedMessage: relatedMessage,
relationType: relationType, relationType: relationType,
onDismiss: onDismiss, onDismiss: onDismiss,
), ),

View file

@ -22,10 +22,6 @@ class Html extends ConsumerWidget {
html, html,
textStyle: textStyle, textStyle: textStyle,
customWidgetBuilder: (element) { customWidgetBuilder: (element) {
if (element.attributes.keys.contains("data-mx-profile-fallback")) {
return SizedBox.shrink();
}
if (element.attributes.keys.contains("data-mx-spoiler")) { if (element.attributes.keys.contains("data-mx-spoiler")) {
return InlineCustomWidget(child: SpoilerText(text: element.text)); return InlineCustomWidget(child: SpoilerText(text: element.text));
} }

View file

@ -1,30 +0,0 @@
import "package:flutter/widgets.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/author_controller.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";
class MessageAvatar extends ConsumerWidget {
final Message message;
final Room room;
final double height;
const MessageAvatar(this.message, this.room, {this.height = 16, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) => ref
.watch(
AuthorController.provider(AuthorConfig(room: room, message: message)),
)
.betterWhen(
data: (membership) => AvatarOrHash(
membership.avatarUrl,
membership.displayName,
height: height,
),
loading: () =>
AvatarOrHash(null, message.authorId.substring(1), height: height),
);
}

View file

@ -1,28 +0,0 @@
import "package:flutter/widgets.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/author_controller.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 {
final Message message;
final Room room;
final TextStyle? style;
const MessageDisplayname(this.message, this.room, {this.style, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) => ref
.watch(
AuthorController.provider(AuthorConfig(room: room, message: message)),
)
.betterWhen(
data: (membership) => Text(
"${membership.displayName} ${message.metadata?["pmp"] == null ? "" : "(via ${message.authorId})"}",
style: style,
overflow: TextOverflow.ellipsis,
),
loading: SizedBox.shrink,
);
}

View file

@ -36,9 +36,18 @@ class MemberList extends ConsumerWidget {
builder: (context) => builder: (context) =>
Dialog(child: Text("TODO: Open member popover")), Dialog(child: Text("TODO: Open member popover")),
), ),
leading: AvatarOrHash(member.avatarUrl, member.displayName), leading: AvatarOrHash(
title: Text(member.displayName, overflow: TextOverflow.ellipsis), Uri.tryParse(member.content["avatar_url"] ?? ""),
subtitle: Text(member.userId, overflow: TextOverflow.ellipsis), member.content["displayname"].toString(),
),
title: Text(
member.content["displayname"].toString(),
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
member.stateKey ?? "Unknown User",
overflow: TextOverflow.ellipsis,
),
), ),
), ),
], ],

View file

@ -40,29 +40,39 @@ class MentionOverlay extends ConsumerWidget {
? members ? members
: members.where( : members.where(
(member) => (member) =>
member.userId.toLowerCase().contains( member.stateKey?.toLowerCase().contains(
query.toLowerCase(), query.toLowerCase(),
) == ) ==
true || true ||
member.displayName.toLowerCase().contains( (member.content["displayname"] as String?)
query.toLowerCase(), ?.toLowerCase()
) == .contains(query.toLowerCase()) ==
true, true,
)) ))
.map( .map(
(member) => ListTile( (member) => ListTile(
leading: AvatarOrHash( leading: AvatarOrHash(
member.avatarUrl, Uri.tryParse(
member.displayName, member.content["avatar_url"] ?? "",
), ),
title: Text(member.displayName), member.content["displayname"] ?? "",
subtitle: Text(member.userId), ),
title: Text(
member.content["displayname"] as String? ??
member.stateKey ??
"Unknown User",
),
subtitle: member.stateKey != null
? Text(member.stateKey!)
: null,
onTap: () => addTag( onTap: () => addTag(
id: "[@${member.displayName}](https://matrix.to/#/${member.userId})", id: "[@${member.content["displayname"]}](https://matrix.to/#/${member.stateKey})",
name: member.userId name:
.substring(1) member.stateKey
?.substring(1)
.split(":") .split(":")
.first, .first ??
"Unknown User",
), ),
), ),
) )

View file

@ -1,21 +1,12 @@
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/avatar_or_hash.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.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 +24,11 @@ class MessageWrapper extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
groupStatus?.isFirst != false groupStatus?.isFirst != false
? MessageAvatar(message, room, height: 40) ? AvatarOrHash(
Uri.parse(message.metadata?["avatarUrl"] ?? ""),
height: 40,
message.metadata?["displayName"] ?? "",
)
: SizedBox(width: 40), : SizedBox(width: 40),
Expanded( Expanded(
child: Column( child: Column(
@ -41,9 +36,9 @@ class MessageWrapper extends StatelessWidget {
spacing: 4, spacing: 4,
children: [ children: [
if (groupStatus?.isFirst != false) if (groupStatus?.isFirst != false)
MessageDisplayname( Text(
message, message.metadata?["displayName"] ?? message.authorId,
room, overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),

View file

@ -2,9 +2,7 @@ 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/avatar_or_hash.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
class RelationPreview extends ConsumerWidget { class RelationPreview extends ConsumerWidget {
final Message? relatedMessage; final Message? relatedMessage;
@ -12,11 +10,8 @@ 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({
required this.relatedMessage,
const RelationPreview(
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 +36,14 @@ class RelationPreview extends ConsumerWidget {
"Editing message:", "Editing message:",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
MessageAvatar(relatedMessage!, room), AvatarOrHash(
MessageDisplayname( Uri.tryParse(relatedMessage?.metadata?["avatarUrl"] ?? ""),
relatedMessage!, relatedMessage?.metadata?["displayName"]?.toString() ?? "",
room, height: 16,
),
Text(
relatedMessage!.metadata?["displayName"] ??
relatedMessage!.authorId,
style: theme.textTheme.labelMedium?.copyWith( style: theme.textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),

View file

@ -1,15 +1,15 @@
import "dart:math";
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_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/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/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/models/room.dart";
import "package:nexus/widgets/avatar_or_hash.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_displayname.dart";
typedef OnTapReply = void Function(Message message)?; typedef OnTapReply = void Function(Message message)?;
@ -61,28 +61,73 @@ class ReplyWidget extends ConsumerWidget {
return SizedBox.shrink(); return SizedBox.shrink();
} }
final smallerText =
message is TextMessage &&
replyMessage.metadata?["body"] != null
? replyMessage.metadata!["body"].substring(
0,
min(
max(
max(
(message as TextMessage)
.text
.length -
(replyMessage
.metadata?["displayName"]
as String)
.length -
5,
message
.metadata?["displayName"]
.length,
),
5,
),
replyMessage.metadata!["body"].length,
),
)
: null;
final replyText =
(smallerText == null ||
smallerText.length ==
replyMessage
.metadata!["body"]
.length)
? replyMessage.metadata!["body"]
: "$smallerText...";
return InkWell( return InkWell(
onTap: () => onTapReply?.call(replyMessage), onTap: () => onTapReply?.call(replyMessage),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
spacing: 8, spacing: 8,
children: [ children: [
MessageAvatar(replyMessage, room), AvatarOrHash(
Uri.tryParse(
replyMessage.metadata?["avatarUrl"] ??
"",
),
replyMessage.metadata?["displayName"] ??
"",
height: 16,
),
Flexible( Flexible(
child: MessageDisplayname( child: Text(
replyMessage, replyMessage
room, .metadata?["displayName"] ??
replyMessage.authorId,
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.labelMedium .labelMedium
?.copyWith( ?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
overflow: TextOverflow.ellipsis,
), ),
), ),
Flexible( Flexible(
child: Text( child: Text(
replyMessage.metadata!["body"], replyText,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of( style: Theme.of(
context, context,

View file

@ -320,7 +320,6 @@ class RoomChat extends HookConsumerWidget {
), ),
), ),
groupStatus, groupStatus,
room,
), ),
systemMessageBuilder: systemMessageBuilder:

View file

@ -109,7 +109,6 @@ class TextMessageWrapper extends StatelessWidget {
), ),
), ),
groupStatus, groupStatus,
room,
); );
} }
} }