From 6e02cce84f899143457e9ba78e382e1234c02ecb Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 5 Jun 2026 21:23:06 -0400 Subject: [PATCH] WIP subspace support --- lib/controllers/spaces_controller.dart | 2 + lib/models/space.dart | 2 + lib/models/subspace.dart | 10 +++++ lib/widgets/join_dialog.dart | 12 ++++-- lib/widgets/sidebar.dart | 53 +++++++++++++++----------- 5 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 lib/models/subspace.dart diff --git a/lib/controllers/spaces_controller.dart b/lib/controllers/spaces_controller.dart index 548ff20..5c57d3a 100644 --- a/lib/controllers/spaces_controller.dart +++ b/lib/controllers/spaces_controller.dart @@ -103,12 +103,14 @@ class SpacesController extends Notifier> { title: "Home", icon: Icons.home, children: homeRooms, + subSpaces: .new(), ), .new( id: "dms", title: "Direct Messages", icon: Icons.people, children: dmRooms, + subSpaces: .new(), ), ...topLevelSpacesList, ] diff --git a/lib/models/space.dart b/lib/models/space.dart index 631759a..73fbbc6 100644 --- a/lib/models/space.dart +++ b/lib/models/space.dart @@ -2,6 +2,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/widgets.dart"; import "package:freezed_annotation/freezed_annotation.dart"; import "package:nexus/models/room.dart"; +import "package:nexus/models/subspace.dart"; part "space.freezed.dart"; @freezed @@ -12,5 +13,6 @@ abstract class Space with _$Space { IconData? icon, Room? room, required IList children, + required IList subSpaces, }) = _Space; } diff --git a/lib/models/subspace.dart b/lib/models/subspace.dart new file mode 100644 index 0000000..1a1879c --- /dev/null +++ b/lib/models/subspace.dart @@ -0,0 +1,10 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/room.dart"; +part "subspace.freezed.dart"; + +@freezed +abstract class Subspace with _$Subspace { + const factory Subspace({required Room room, required IList children}) = + _Subspace; +} diff --git a/lib/widgets/join_dialog.dart b/lib/widgets/join_dialog.dart index d265595..d420dea 100644 --- a/lib/widgets/join_dialog.dart +++ b/lib/widgets/join_dialog.dart @@ -84,9 +84,15 @@ class JoinDialog extends HookWidget { space?.id ?? spaces .firstWhere( - (space) => space.children.any( - (child) => child.metadata?.id == id, - ), + (space) => + space.children.any( + (child) => + child.metadata?.id == id, + ) || + space.subSpaces.any( + (child) => + child.room.metadata?.id == id, + ), ) .id, ); diff --git a/lib/widgets/sidebar.dart b/lib/widgets/sidebar.dart index 6fb4780..02cac3d 100644 --- a/lib/widgets/sidebar.dart +++ b/lib/widgets/sidebar.dart @@ -1,9 +1,11 @@ import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:navigation_rail_m3e/navigation_rail_m3e.dart"; import "package:nexus/controllers/key_controller.dart"; import "package:nexus/controllers/spaces_controller.dart"; +import "package:nexus/models/room.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/join_dialog.dart"; import "package:nexus/widgets/room_menu.dart"; @@ -43,6 +45,27 @@ class Sidebar extends HookConsumerWidget { ? null : indexOfSelectedRoom; + List roomsToDestinations(IList rooms) => + rooms + .map( + (room) => NavigationRailM3EDestination( + label: room.metadata?.name ?? "Unnamed Room", + badgeCount: switch (room.metadata?.unreadNotifications) { + 0 || null => room.metadata?.unreadMessages == 0 ? null : 0, + int unread => unread, + }, + icon: AvatarOrHash( + room.metadata?.avatar, + room.metadata?.name ?? "Unnamed Room", + fallback: selectedSpaceId == "dms" + ? null + : Icon(Icons.numbers), + // space.client.headers, + ), + ), + ) + .toList(); + return Drawer( width: 340, shape: Border(), @@ -189,29 +212,15 @@ class Sidebar extends HookConsumerWidget { selectedIndex: selectedRoomIndex ?? 0, sections: [ .new( - destinations: selectedSpace.children - .map( - (room) => NavigationRailM3EDestination( - label: room.metadata?.name ?? "Unnamed Room", - badgeCount: switch (room - .metadata - ?.unreadNotifications) { - 0 || null => - room.metadata?.unreadMessages == 0 ? null : 0, - int unread => unread, - }, - icon: AvatarOrHash( - room.metadata?.avatar, - room.metadata?.name ?? "Unnamed Room", - fallback: selectedSpaceId == "dms" - ? null - : Icon(Icons.numbers), - // space.client.headers, - ), - ), - ) - .toList(), + destinations: roomsToDestinations(selectedSpace.children), ), + for (final subSpace in selectedSpace.subSpaces) + .new( + header: Text( + subSpace.room.metadata?.name ?? "Unnamed Room", + ), + destinations: roomsToDestinations(subSpace.children), + ), ], onDestinationSelected: (value) { selectedRoomIdNotifier.set(