nexus/lib/widgets/chat_page/sidebar.dart
2026-01-30 18:45:55 +01:00

231 lines
9.2 KiB
Dart

import "package:flutter/material.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/client_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/join_room_with_snackbars.dart";
import "package:nexus/pages/settings_page.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/chat_page/room_menu.dart";
import "package:nexus/widgets/form_text_input.dart";
class Sidebar extends HookConsumerWidget {
const Sidebar({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedSpaceProvider = KeyController.provider(
KeyController.spaceKey,
);
final selectedSpaceId = ref.watch(selectedSpaceProvider);
final selectedSpaceIdNotifier = ref.watch(selectedSpaceProvider.notifier);
final selectedRoomController = KeyController.provider(
KeyController.roomKey,
);
final selectedRoomId = ref.watch(selectedRoomController);
final selectedRoomIdNotifier = ref.watch(selectedRoomController.notifier);
final spaces = ref.watch(SpacesController.provider);
final indexOfSelected = spaces.indexWhere(
(space) => space.id == selectedSpaceId,
);
final selectedIndex = indexOfSelected == -1 ? 0 : indexOfSelected;
final selectedSpace = ref.watch(SelectedSpaceController.provider);
final indexOfSelectedRoom = selectedSpace.children.indexWhere(
(room) => room.metadata?.id == selectedRoomId,
);
final selectedRoomIndex = indexOfSelectedRoom == -1
? selectedSpace.children.isEmpty
? null
: 0
: indexOfSelectedRoom;
return Drawer(
shape: Border(),
child: Row(
children: [
NavigationRail(
scrollable: true,
onDestinationSelected: (value) {
selectedSpaceIdNotifier.set(spaces[value].id);
selectedRoomIdNotifier.set(
spaces[value].children.firstOrNull?.metadata?.id,
);
},
destinations: spaces
.map(
(space) => NavigationRailDestination(
icon: AvatarOrHash(
space.room?.metadata?.avatar,
fallback: space.icon == null ? null : Icon(space.icon),
space.title,
hasBadge: space.children.any(
(room) => room.metadata?.unreadMessages != 0,
),
badgeNumber: space.children.fold(
0,
(previousValue, room) =>
previousValue +
(room.metadata?.unreadNotifications ?? 0),
),
),
label: Text(space.title),
padding: EdgeInsets.only(top: 4),
),
)
.toList(),
selectedIndex: selectedIndex,
trailingAtBottom: true,
trailing: Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
spacing: 8,
children: [
PopupMenuButton(
itemBuilder: (_) => [
PopupMenuItem(
onTap: () => showDialog(
context: context,
builder: (alertContext) => HookBuilder(
builder: (_) {
final roomAlias = useTextEditingController();
return AlertDialog(
title: Text("Join a Room"),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Enter the room alias, ID, or a Matrix.to link.",
),
SizedBox(height: 12),
FormTextInput(
required: false,
capitalize: true,
controller: roomAlias,
title: "#room:server",
),
],
),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: Text("Cancel"),
),
TextButton(
onPressed: () async {
Navigator.of(alertContext).pop();
final client = ref.watch(
ClientController.provider.notifier,
);
if (context.mounted) {
client.joinRoomWithSnackBars(
context,
roomAlias.text,
ref,
);
}
},
child: Text("Join"),
),
],
);
},
),
),
child: ListTile(
title: Text("Join an existing room (or space)"),
leading: Icon(Icons.numbers),
),
),
PopupMenuItem(
onTap: () {},
child: ListTile(
title: Text("Create a new room"),
leading: Icon(Icons.add),
),
),
],
icon: Icon(Icons.add),
),
IconButton(
onPressed: () => showDialog(
context: context,
builder: (context) => AlertDialog(title: Text("To-do")),
),
icon: Icon(Icons.explore),
),
IconButton(
onPressed: () => Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => SettingsPage())),
icon: Icon(Icons.settings),
),
],
),
),
),
Expanded(
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
leading: AvatarOrHash(
selectedSpace.room?.metadata?.avatar,
fallback: selectedSpace.icon == null
? null
: Icon(selectedSpace.icon),
selectedSpace.title,
),
title: Text(
selectedSpace.title,
overflow: TextOverflow.ellipsis,
),
backgroundColor: Colors.transparent,
actions: [
if (selectedSpace.room != null)
RoomMenu(
selectedSpace.room!,
children: selectedSpace.children,
),
],
),
body: NavigationRail(
scrollable: true,
backgroundColor: Colors.transparent,
extended: true,
selectedIndex: selectedRoomIndex,
destinations: selectedSpace.children
.map(
(room) => NavigationRailDestination(
label: Text(room.metadata?.name ?? "Unnamed Room"),
icon: AvatarOrHash(
room.metadata?.avatar,
hasBadge: room.metadata?.unreadMessages != 0,
badgeNumber: room.metadata?.unreadNotifications ?? 0,
room.metadata?.name ?? "Unnamed Room",
fallback: selectedSpaceId == "dms"
? null
: Icon(Icons.numbers),
// space.client.headers,
),
),
)
.toList(),
onDestinationSelected: (value) => selectedRoomIdNotifier.set(
selectedSpace.children[value].metadata?.id,
),
),
),
),
],
),
);
}
}