Leave room support, persist last room, fixes

This commit is contained in:
Henry Hiles 2025-12-03 16:16:30 -05:00
commit 7dfd47a404
No known key found for this signature in database
17 changed files with 312 additions and 136 deletions

View file

@ -1,4 +1,3 @@
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_chat_ui/flutter_chat_ui.dart";
@ -9,7 +8,7 @@ import "package:flyer_chat_image_message/flyer_chat_image_message.dart";
import "package:flyer_chat_system_message/flyer_chat_system_message.dart";
import "package:flyer_chat_text_message/flyer_chat_text_message.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/current_room_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_headers.dart";
@ -38,7 +37,7 @@ class RoomChat extends HookConsumerWidget {
final theme = Theme.of(context);
return ref
.watch(CurrentRoomController.provider)
.watch(SelectedRoomController.provider)
.betterWhen(
data: (room) {
if (room == null) {
@ -291,13 +290,12 @@ class RoomChat extends HookConsumerWidget {
index, {
required bool isSentByMe,
MessageGroupStatus? groupStatus,
}) => kDebugMode
? Text(
"${message.authorId} sent ${message.metadata?["eventType"]}",
style: theme.textTheme.labelSmall
?.copyWith(color: Colors.grey),
)
: SizedBox.shrink(),
}) => Text(
"${message.authorId} sent ${message.metadata?["eventType"]}",
style: theme.textTheme.labelSmall?.copyWith(
color: Colors.grey,
),
),
),
onMessageSend: (message) {
notifier.send(

View file

@ -20,8 +20,32 @@ class RoomMenu extends StatelessWidget {
child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")),
),
PopupMenuItem(
onTap: () =>
showDialog(context: context, builder: (context) => AlertDialog()),
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("Leave Room"),
content: Text(
"Are you sure you want to leave \"${room.getLocalizedDisplayname()}\"?",
),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: Text("Cancel"),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
final snackbar = ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Leaving room...")));
await room.leave();
snackbar.close();
},
child: Text("Leave"),
),
],
),
),
child: ListTile(
leading: Icon(Icons.logout, color: danger),
title: Text("Leave", style: TextStyle(color: danger)),

View file

@ -1,8 +1,7 @@
import "package:collection/collection.dart";
import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/current_room_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/controllers/key_controller.dart";
import "package:nexus/controllers/selected_space_controller.dart";
import "package:nexus/controllers/spaces_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
@ -16,11 +15,15 @@ class Sidebar extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedSpaceProvider = SelectedSpaceController.provider;
final selectedSpaceProvider = KeyController.provider(
KeyController.spaceKey,
);
final selectedSpace = ref.watch(selectedSpaceProvider);
final selectedSpaceNotifier = ref.watch(selectedSpaceProvider.notifier);
final selectedRoomController = SelectedRoomController.provider;
final selectedRoomController = KeyController.provider(
KeyController.roomKey,
);
final selectedRoom = ref.watch(selectedRoomController);
final selectedRoomNotifier = ref.watch(selectedRoomController.notifier);
@ -36,73 +39,87 @@ class Sidebar extends HookConsumerWidget {
debugPrintStack(label: error.toString(), stackTrace: stack);
throw error;
},
data: (spaces) => NavigationRail(
scrollable: true,
onDestinationSelected: (value) {
selectedRoomNotifier.set(0);
selectedSpaceNotifier.set(value);
ref
.watch(CurrentRoomController.provider.notifier)
.set(spaces[value].children[0]);
},
destinations: spaces
.map(
(space) => NavigationRailDestination(
icon: AvatarOrHash(
space.avatar,
fallback: space.icon,
space.title,
headers: space.client.headers,
hasBadge:
space.children.firstWhereOrNull(
(room) => room.roomData.hasNewMessages,
) !=
null,
data: (spaces) {
final indexOfSelected = spaces.indexWhere(
(space) => space.id == selectedSpace,
);
final selectedIndex = indexOfSelected == -1
? null
: indexOfSelected;
return NavigationRail(
scrollable: true,
onDestinationSelected: (value) {
selectedSpaceNotifier.set(spaces[value].roomData?.id);
selectedRoomNotifier.set(
spaces[value].children.firstOrNull?.roomData.id,
);
},
destinations: spaces
.map(
(space) => NavigationRailDestination(
icon: AvatarOrHash(
space.avatar,
fallback: space.icon,
space.title,
headers: space.client.headers,
hasBadge:
space.children.firstWhereOrNull(
(room) => room.roomData.hasNewMessages,
) !=
null,
),
label: Text(space.title),
padding: EdgeInsets.only(top: 4),
),
label: Text(space.title),
padding: EdgeInsets.only(top: 4),
),
)
.toList(),
selectedIndex: selectedSpace,
trailingAtBottom: true,
trailing: Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
spacing: 8,
children: [
IconButton(
onPressed: () => Navigator.of(context).push(
// TODO: join or create room/space
MaterialPageRoute(builder: (_) => SettingsPage()),
)
.toList(),
selectedIndex: selectedIndex,
trailingAtBottom: true,
trailing: Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
spacing: 8,
children: [
IconButton(
onPressed: () => Navigator.of(context).push(
// TODO: join or create room/space
MaterialPageRoute(builder: (_) => SettingsPage()),
),
icon: Icon(Icons.add),
),
icon: Icon(Icons.add),
),
IconButton(
onPressed: () => Navigator.of(context).push(
// TODO: explore public rooms/spaces
MaterialPageRoute(builder: (_) => SettingsPage()),
IconButton(
onPressed: () => Navigator.of(context).push(
// TODO: explore public rooms/spaces
MaterialPageRoute(builder: (_) => SettingsPage()),
),
icon: Icon(Icons.explore),
),
icon: Icon(Icons.explore),
),
IconButton(
onPressed: () => Navigator.of(context).push(
// TODO: explore public rooms/spaces
MaterialPageRoute(builder: (_) => SettingsPage()),
IconButton(
onPressed: () => Navigator.of(context).push(
// TODO: explore public rooms/spaces
MaterialPageRoute(builder: (_) => SettingsPage()),
),
icon: Icon(Icons.settings),
),
icon: Icon(Icons.settings),
),
],
],
),
),
),
),
);
},
),
Expanded(
child: ref
.watch(SpacesController.provider)
.watch(SelectedSpaceController.provider)
.betterWhen(
data: (spaces) {
final space = spaces[selectedSpace];
data: (space) {
final indexOfSelected = space.children.indexWhere(
(room) => room.roomData.id == selectedRoom,
);
final selectedIndex = indexOfSelected == -1
? null
: indexOfSelected;
return Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
@ -125,9 +142,7 @@ class Sidebar extends HookConsumerWidget {
scrollable: true,
backgroundColor: Colors.transparent,
extended: true,
selectedIndex: space.children.isEmpty
? null
: selectedRoom,
selectedIndex: selectedIndex,
destinations: space.children
.map(
(room) => NavigationRailDestination(
@ -144,12 +159,8 @@ class Sidebar extends HookConsumerWidget {
),
)
.toList(),
onDestinationSelected: (value) {
selectedRoomNotifier.set(value);
ref
.watch(CurrentRoomController.provider.notifier)
.set(space.children[value]);
},
onDestinationSelected: (value) => selectedRoomNotifier
.set(space.children[value].roomData.id),
),
);
},