Remove flutter chat #26

Manually merged
Henry-Hiles merged 108 commits from remove-flutter-chat into main 2026-05-22 15:26:28 -04:00
8 changed files with 132 additions and 94 deletions
Showing only changes of commit 49c09b3c35 - Show all commits

easy widgets ported to use new event format

Henry Hiles 2026-05-16 16:22:49 -04:00
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -5,13 +5,14 @@ import "package:fluttertagger/fluttertagger.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/power_level_controller.dart";
import "package:nexus/models/configs/power_level_config.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/relation_type.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/emoji_picker_button.dart";
class ChatBox extends HookConsumerWidget {
final Message? relatedMessage;
final Event? relatedEvent;
final RelationType relationType;
final VoidCallback onDismiss;
final FocusNode? node;
@ -22,7 +23,7 @@ class ChatBox extends HookConsumerWidget {
})
onSend;
const ChatBox({
required this.relatedMessage,
required this.relatedEvent,
required this.relationType,
required this.onDismiss,
required this.onSend,
@ -38,10 +39,8 @@ class ChatBox extends HookConsumerWidget {
final shouldMention = useState(true);
final query = useState("");
if (relationType == RelationType.edit &&
relatedMessage is TextMessage &&
controller.value.text.isEmpty) {
controller.value.text = relatedMessage?.metadata?["editSource"] ?? "";
if (relationType == RelationType.edit && controller.value.text.isEmpty) {
controller.value.text = relatedEvent?.localContent?.editSource ?? "";
}
void send() {
@ -72,7 +71,7 @@ class ChatBox extends HookConsumerWidget {
child: Column(
children: [
RelationPreview(
relatedMessage,
relatedEvent,
shouldMention: shouldMention.value,
toggleShouldMention: () =>
shouldMention.value = !shouldMention.value,

View file

@ -4,6 +4,7 @@ import "package:nexus/controllers/members_by_type_controller.dart";
import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/controllers/via_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/models/content/membership.dart";
import "package:nexus/models/membership_status.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/loading.dart";
@ -43,33 +44,48 @@ class MentionOverlay extends ConsumerWidget {
? members
: members.where(
(member) =>
member.userId.toLowerCase().contains(
member.stateKey
?.toLowerCase()
.contains(
query.toLowerCase(),
) ==
true ||
member.displayName
switch (member.content) {
MembershipContent(
:final displayName,
) =>
displayName
.toLowerCase()
.contains(
query.toLowerCase(),
) ==
true,
_ => false,
},
))
.map(
(member) => ListTile(
(member) => switch (member.content) {
MembershipContent(
:final displayName,
:final avatarUrl,
) =>
ListTile(
leading: AvatarOrHash(
member.avatarUrl,
member.displayName,
avatarUrl,
displayName,
),
title: Text(member.displayName),
subtitle: Text(member.userId),
title: Text(displayName),
subtitle: Text(member.stateKey!),
onTap: () => addTag(
id: "[@${member.displayName}](matrix:u/${member.userId.substring(1)})",
name: member.userId
id: "[@$displayName](matrix:u/${member.stateKey!.substring(1)})",
name: member.stateKey!
.substring(1)
.split(":")
.first,
),
),
_ => SizedBox.shrink(),
},
)
.toList(),
),

View file

@ -1,18 +1,19 @@
import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/relation_type.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 {
final Message? relatedMessage;
final Event? relatedEvent;
final RelationType relationType;
final VoidCallback onDismiss;
final bool shouldMention;
final VoidCallback toggleShouldMention;
const RelationPreview(
this.relatedMessage, {
this.relatedEvent, {
required this.relationType,
required this.onDismiss,
required this.shouldMention,
@ -22,7 +23,7 @@ class RelationPreview extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
if (relatedMessage == null) return SizedBox.shrink();
if (relatedEvent == null) return SizedBox.shrink();
final theme = Theme.of(context);
return Container(
@ -37,7 +38,7 @@ class RelationPreview extends ConsumerWidget {
style: TextStyle(fontWeight: FontWeight.bold),
),
MessageAvatar(relatedMessage!),
MessageAvatar(relatedEvent!),
Expanded(
child: Row(
@ -45,16 +46,20 @@ class RelationPreview extends ConsumerWidget {
children: [
Flexible(
child: MessageDisplayname(
relatedMessage!,
relatedEvent!,
style: theme.textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: Text(
relatedMessage?.metadata?["body"] ??
relatedMessage?.metadata?["eventType"] ??
child: Text(switch (relatedEvent?.content) {
_ => ""
}
relatedEvent?.metadata?["body"] ??
relatedEvent?.metadata?["eventType"] ??
"",
maxLines: 1,
overflow: TextOverflow.ellipsis,

View file

@ -10,18 +10,21 @@ class MentionChip extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final membership = content.mention!.startsWith("@") == true
final mention = content.mention;
final membership = mention?.startsWith("@") == true
? ref
.watch(UserController.provider(content.mention!))
.watch(UserController.provider(mention!))
.whenOrNull(data: (data) => data)
: null;
return InkWell(
return mention == null
? SizedBox.shrink()
: InkWell(
onTapUp: (details) {
content.mention;
if (membership != null) {
context.showUserPopover(
membership,
mention,
globalPosition: details.globalPosition,
);
}
@ -30,7 +33,7 @@ class MentionChip extends ConsumerWidget {
child: Chip(
label: Text(
(membership == null ? null : "@${membership.displayName}") ??
content.mention!,
mention,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onPrimary,

View file

@ -3,22 +3,29 @@ 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/helpers/extensions/show_user_popover.dart";
import "package:nexus/models/content/membership.dart";
import "package:nexus/models/event.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
class MessageAvatar extends ConsumerWidget {
final Message message;
final Event event;
final double height;
const MessageAvatar(this.message, {this.height = 16, super.key});
const MessageAvatar(this.event, {this.height = 16, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) => ref
.watch(AuthorController.provider(message))
.watch(AuthorController.provider(event))
.betterWhen(
data: (membership) => InkWell(
onTapUp: (details) => context.showUserPopover(
membership,
onTapUp: (details) {
if (event.content is MembershipContent) {
context.showUserPopover(
event.content as MembershipContent,
event.stateKey!,
globalPosition: details.globalPosition,
),
);
}
},
child: AvatarOrHash(
membership.avatarUrl,
membership.displayName,
@ -26,6 +33,6 @@ class MessageAvatar extends ConsumerWidget {
),
),
loading: () =>
AvatarOrHash(null, message.sender.substring(1), height: height),
AvatarOrHash(null, event.stateKey!.substring(1), height: height),
);
}

View file

@ -3,13 +3,14 @@ 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/helpers/extensions/show_user_popover.dart";
import "package:nexus/models/event.dart";
class MessageDisplayname extends ConsumerWidget {
final Message message;
final Event event;
final TextStyle? style;
final bool clickable;
const MessageDisplayname(
this.message, {
this.event, {
this.clickable = true,
this.style,
super.key,
@ -17,17 +18,18 @@ class MessageDisplayname extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) => ref
.watch(AuthorController.provider(message))
.watch(AuthorController.provider(event))
.betterWhen(
data: (membership) => InkWell(
onTapUp: clickable
? (details) => context.showUserPopover(
membership,
event.stateKey!,
globalPosition: details.globalPosition,
)
: null,
child: Text(
"${membership.displayName}${message.metadata?["pmp"] == null ? "" : " (via ${message.sender})"}",
"${membership.displayName}${event.pmp == null ? "" : " (via ${event.stateKey})"}",
style: style,
overflow: TextOverflow.ellipsis,
),

View file

@ -4,6 +4,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/members_by_type_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/show_user_popover.dart";
import "package:nexus/models/content/membership.dart";
import "package:nexus/models/membership_status.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
@ -62,26 +63,31 @@ class MemberList extends HookConsumerWidget {
child: ListView(
children: members
.map(
(member) => InkWell(
(member) => switch (member.content) {
MembershipContent(
:final avatarUrl,
:final displayName,
) =>
InkWell(
onTapUp: (details) => context.showUserPopover(
member,
member.content as MembershipContent,
member.stateKey!,
globalPosition: details.globalPosition,
),
child: ListTile(
leading: AvatarOrHash(
member.avatarUrl,
member.displayName,
),
leading: AvatarOrHash(avatarUrl, displayName),
title: Text(
member.displayName,
displayName,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
member.userId,
member.stateKey!,
overflow: TextOverflow.ellipsis,
),
),
),
_ => SizedBox.shrink(),
},
)
.toList(),
),

View file

@ -371,7 +371,7 @@ class RoomChat extends HookConsumerWidget {
)
.onError(showError),
relationType: relationType.value,
relatedMessage: relatedMessage.value,
relatedEvent: relatedMessage.value,
onDismiss: () => relatedMessage.value = null,
),