add inviting from room menu

This commit is contained in:
Henry Hiles 2026-10-06 19:23:00 -04:00
commit 7be1dfe0bc
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
5 changed files with 85 additions and 69 deletions

2
gomuks

@ -1 +1 @@
Subproject commit 15ac73be09155a7c5319a144f8af0e75a409fc51 Subproject commit 38e5033c3a91db0cbea6cbd9f0feef9ff6441038

View file

@ -10,7 +10,7 @@ class const SetMembershipRequest({
required final String userId, required final String userId,
required final String roomId, required final String roomId,
final String? reason, final String? reason,
@JsonKey(name: "action") required final MembershipAction action, required final MembershipAction action,
@JsonKey(name: "msc4293_redact_events") final bool redact = false, @JsonKey(name: "msc4293_redact_events") final bool redact = false,
}) with _$SetMembershipRequest { }) with _$SetMembershipRequest {
Map<String, Object?> toJson() => _$SetMembershipRequestToJson(this); Map<String, Object?> toJson() => _$SetMembershipRequestToJson(this);

View file

@ -3,7 +3,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/rooms.dart"; import "package:nexus/controllers/rooms.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/room_menu.dart"; import "package:nexus/widgets/room_menu_button.dart";
import "package:nexus/widgets/room_summary_dialog.dart"; import "package:nexus/widgets/room_summary_dialog.dart";
final class const RoomAppbar({ final class const RoomAppbar({
@ -72,21 +72,23 @@ final class const RoomAppbar({
), ),
], ],
), ),
actions: room == null actions: .new(
? .new() room == null
: .new([ ? []
IconButton( : [
onPressed: onOpenPinnedMessagesList?.call, IconButton(
icon: Icon(Icons.push_pin), onPressed: onOpenPinnedMessagesList?.call,
tooltip: "Open pinned messages", icon: Icon(Icons.push_pin),
), tooltip: "Open pinned messages",
IconButton( ),
onPressed: () => onOpenMemberList?.call(context), IconButton(
tooltip: "Open member list", onPressed: () => onOpenMemberList?.call(context),
icon: Icon(Icons.people), tooltip: "Open member list",
), icon: Icon(Icons.people),
RoomMenu(room), ),
]), RoomMenuButton(room),
],
),
); );
} }
} }

View file

@ -10,7 +10,7 @@ import "package:nexus/widgets/divider_widget.dart";
import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/widgets/error_dialog.dart";
import "package:nexus/widgets/pages/settings.dart"; import "package:nexus/widgets/pages/settings.dart";
import "package:nexus/widgets/join_dialog.dart"; import "package:nexus/widgets/join_dialog.dart";
import "package:nexus/widgets/room_menu.dart"; import "package:nexus/widgets/room_menu_button.dart";
import "package:nexus/controllers/key.dart"; import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/spaces.dart"; import "package:nexus/controllers/spaces.dart";
import "package:nexus/models/room.dart"; import "package:nexus/models/room.dart";
@ -232,7 +232,10 @@ class const RoomsPane({super.key}) extends ConsumerWidget {
title: Text(selectedSpace.title, overflow: .ellipsis), title: Text(selectedSpace.title, overflow: .ellipsis),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
actions: [ actions: [
RoomMenu(selectedSpace.room, children: selectedSpace.allChildRooms), RoomMenuButton(
selectedSpace.room,
children: selectedSpace.allChildRooms,
),
], ],
), ),
body: MaterialUiCompatibilityBridge( body: MaterialUiCompatibilityBridge(

View file

@ -5,8 +5,10 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/via.dart"; import "package:nexus/controllers/via.dart";
import "package:nexus/models/room.dart"; import "package:nexus/models/room.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:nexus/main.dart";
final class const RoomMenu( final class const RoomMenuButton(
final Room? room, { final Room? room, {
final IList<Room> children = const IList.empty(), final IList<Room> children = const IList.empty(),
super.key, super.key,
@ -44,6 +46,63 @@ final class const RoomMenu(
title: Text("Copy Link"), title: Text("Copy Link"),
), ),
), ),
PopupMenuItem(
onTap: () => showDialog(
context: context,
builder: (context) => HookBuilder(
builder: (context) {
final userIdController = useTextEditingController();
final text = useValueListenable(userIdController).text.trim();
final valid = RegExp(r"^@[^:\s]+:[^\s]+$").hasMatch(text);
void submit() {
if (!valid) return;
Navigator.of(context).pop();
client
.setMembership(
.new(
userId: text,
roomId: room!.metadata!.id,
action: .invite,
),
)
.onError(showError);
}
return AlertDialog(
title: Text("Invite User"),
content: TextField(
controller: userIdController,
autofocus: true,
keyboardType: .emailAddress,
onSubmitted: (_) => submit(),
decoration: .new(
labelText: "User ID",
hintText: "@user:example.com",
errorText: text.isEmpty || valid
? null
: "Enter a valid user ID",
),
),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: Text("Cancel"),
),
TextButton(
onPressed: valid ? submit : null,
child: Text("Invite"),
),
],
);
},
),
),
child: ListTile(
leading: Icon(Icons.person_add),
title: Text("Invite"),
),
),
PopupMenuItem( PopupMenuItem(
onTap: () => showDialog( onTap: () => showDialog(
context: context, context: context,
@ -81,54 +140,6 @@ final class const RoomMenu(
), ),
), ),
], ],
// PopupMenuItem(
// onTap: () => showDialog(
// context: context,
// builder: (context) => HookBuilder(
// builder: (_) {
// final reasonController = useTextEditingController();
// return AlertDialog(
// title: Text("Report"),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Report this room to your server administrators, who can take action like banning this room.",
// ),
// SizedBox(height: 12),
// FormTextInput(
// required: false,
// capitalize: true,
// controller: reasonController,
// title: "Reason for report (optional)",
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: Navigator.of(context).pop,
// child: Text("Cancel"),
// ),
// TextButton(
// onPressed: () {
// room.client.reportRoom(room.id, reasonController.text);
// Navigator.of(context).pop();
// },
// child: Text("Report"),
// ),
// ],
// );
// },
// ),
// ),
// child: ListTile(
// leading: Icon(Icons.report, color: danger),
// title: Text("Report", style: TextStyle(color: danger)),
// ),
// ),
], ],
); );
} }