invite-related fixups

This commit is contained in:
Henry Hiles 2026-10-15 20:01:24 -04:00
commit ba258c2fcd
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
6 changed files with 66 additions and 32 deletions

View file

@ -59,8 +59,13 @@ class InvitesController extends Notifier<IList<RoomSummary>> {
summary.copyWith( summary.copyWith(
worldReadable: historyVisibility == .worldReadable, worldReadable: historyVisibility == .worldReadable,
), ),
MembershipContent(:final isDirect) when event.stateKey == userId => MembershipContent(:final isDirect, :final reason)
summary.copyWith(invitedBy: event.sender, isDirect: isDirect), when event.stateKey == userId =>
summary.copyWith(
invitedBy: event.sender,
inviteReason: reason,
isDirect: isDirect,
),
_ => summary, _ => summary,
}, },
); );

View file

@ -31,6 +31,9 @@ class const RoomSummary({
/// Who invited the current user, for invites. /// Who invited the current user, for invites.
@JsonKey(includeFromJson: false) final String? invitedBy, @JsonKey(includeFromJson: false) final String? invitedBy,
/// The reason given for the invite, if any.
@JsonKey(includeFromJson: false) final String? inviteReason,
/// The inviter's membership, for their display name and avatar. /// The inviter's membership, for their display name and avatar.
@JsonKey(includeFromJson: false) final MembershipContent? inviterProfile, @JsonKey(includeFromJson: false) final MembershipContent? inviterProfile,

View file

@ -124,7 +124,6 @@ class const InviteCard(final RoomSummary invite, {super.key})
[ [
if (inviterName != null) if (inviterName != null)
"${invite.isDirect ? "Direct message from" : "Invited by"} $inviterName", "${invite.isDirect ? "Direct message from" : "Invited by"} $inviterName",
?invite.topic,
].join("\n"), ].join("\n"),
maxLines: 2, maxLines: 2,
overflow: .ellipsis, overflow: .ellipsis,

View file

@ -10,7 +10,9 @@ final class const LinkifiedText(
super.key, super.key,
}) extends ConsumerWidget { }) extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) => Linkify( Widget build(BuildContext context, WidgetRef ref) =>
MaterialUiCompatibilityBridge(
child: Linkify(
text: text, text: text,
maxLines: maxLines, maxLines: maxLines,
style: style, style: style,
@ -19,5 +21,6 @@ final class const LinkifiedText(
ref.read(LaunchHelper.provider).launchUrl(.parse(link.url)), ref.read(LaunchHelper.provider).launchUrl(.parse(link.url)),
linkStyle: .new(color: Theme.of(context).colorScheme.primary), linkStyle: .new(color: Theme.of(context).colorScheme.primary),
overflow: maxLines == null ? null : .ellipsis, overflow: maxLines == null ? null : .ellipsis,
),
); );
} }

View file

@ -55,18 +55,21 @@ final class const RoomMenuButton(
builder: (context) => HookBuilder( builder: (context) => HookBuilder(
builder: (context) { builder: (context) {
final userIdController = useTextEditingController(); final userIdController = useTextEditingController();
final reasonController = useTextEditingController();
final text = useValueListenable(userIdController).text.trim(); final text = useValueListenable(userIdController).text.trim();
final valid = RegExp(r"^@[^:\s]+:[^\s]+$").hasMatch(text); final valid = RegExp(r"^@[^:\s]+:[^\s]+$").hasMatch(text);
void submit() { void submit() {
if (!valid) return; if (!valid) return;
Navigator.of(context).pop(); Navigator.of(context).pop();
final reason = reasonController.text.trim();
client client
.setMembership( .setMembership(
.new( .new(
userId: text, userId: text,
roomId: room!.metadata!.id, roomId: room!.metadata!.id,
action: .invite, action: .invite,
reason: reason.isEmpty ? null : reason,
), ),
) )
.onError(showError); .onError(showError);
@ -74,7 +77,11 @@ final class const RoomMenuButton(
return AlertDialog( return AlertDialog(
title: Text("Invite User"), title: Text("Invite User"),
content: TextField( content: Column(
mainAxisSize: .min,
spacing: 12,
children: [
TextField(
controller: userIdController, controller: userIdController,
autofocus: true, autofocus: true,
keyboardType: .emailAddress, keyboardType: .emailAddress,
@ -87,6 +94,16 @@ final class const RoomMenuButton(
: "Enter a valid user ID", : "Enter a valid user ID",
), ),
), ),
TextField(
controller: reasonController,
textCapitalization: .sentences,
onSubmitted: (_) => submit(),
decoration: const .new(
labelText: "Invite reason (optional)",
),
),
],
),
actions: [ actions: [
TextButton( TextButton(
onPressed: Navigator.of(context).pop, onPressed: Navigator.of(context).pop,

View file

@ -61,6 +61,13 @@ class const RoomSummaryDialog(
), ),
subtitle: SelectableText(invitedBy), subtitle: SelectableText(invitedBy),
), ),
if (summary.inviteReason case final reason?)
ListTile(
titleAlignment: ListTileTitleAlignment.titleHeight,
leading: const Icon(Icons.format_quote),
title: const Text("Invite reason"),
subtitle: SelectableText(reason),
),
// Invite state never includes the member count // Invite state never includes the member count
if (summary.joinedMembers != null || !isInvite) if (summary.joinedMembers != null || !isInvite)
ListTile( ListTile(
@ -70,23 +77,23 @@ class const RoomSummaryDialog(
leading: Icon(Icons.people), leading: Icon(Icons.people),
), ),
ListTile( ListTile(
title: SelectableText(summary.canonicalAlias ?? summary.roomId), title: Text(
summary.canonicalAlias == null ? "Room ID" : "Canonical Alias",
),
subtitle: SelectableText(summary.canonicalAlias ?? summary.roomId),
leading: Icon(Icons.numbers), leading: Icon(Icons.numbers),
), ),
if (summary.topic != null) if (summary.topic != null)
ListTile( ListTile(
titleAlignment: ListTileTitleAlignment.titleHeight, title: Text("Topic"),
title: LinkifiedText( subtitle: LinkifiedText(summary.topic!),
summary.topic!,
style: Theme.of(context).textTheme.bodyLarge,
),
leading: Icon(Icons.info), leading: Icon(Icons.info),
), ),
if (summary.encryption != null) if (summary.encryption != null)
ListTile(title: Text("Encrypted"), leading: Icon(Icons.lock)), ListTile(title: Text("Encrypted"), leading: Icon(Icons.lock)),
if (summary.worldReadable == true) if (summary.worldReadable == true)
ListTile( ListTile(
title: Text("Anyone can read the history"), title: Text("Anyone can read room history"),
leading: Icon(Icons.public), leading: Icon(Icons.public),
), ),
], ],