WIP subspace support

This commit is contained in:
Henry Hiles 2026-06-05 21:23:06 -04:00
commit 6e02cce84f
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
5 changed files with 54 additions and 25 deletions

View file

@ -103,12 +103,14 @@ class SpacesController extends Notifier<IList<Space>> {
title: "Home", title: "Home",
icon: Icons.home, icon: Icons.home,
children: homeRooms, children: homeRooms,
subSpaces: .new(),
), ),
.new( .new(
id: "dms", id: "dms",
title: "Direct Messages", title: "Direct Messages",
icon: Icons.people, icon: Icons.people,
children: dmRooms, children: dmRooms,
subSpaces: .new(),
), ),
...topLevelSpacesList, ...topLevelSpacesList,
] ]

View file

@ -2,6 +2,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/widgets.dart"; import "package:flutter/widgets.dart";
import "package:freezed_annotation/freezed_annotation.dart"; import "package:freezed_annotation/freezed_annotation.dart";
import "package:nexus/models/room.dart"; import "package:nexus/models/room.dart";
import "package:nexus/models/subspace.dart";
part "space.freezed.dart"; part "space.freezed.dart";
@freezed @freezed
@ -12,5 +13,6 @@ abstract class Space with _$Space {
IconData? icon, IconData? icon,
Room? room, Room? room,
required IList<Room> children, required IList<Room> children,
required IList<Subspace> subSpaces,
}) = _Space; }) = _Space;
} }

10
lib/models/subspace.dart Normal file
View file

@ -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<Room> children}) =
_Subspace;
}

View file

@ -84,8 +84,14 @@ class JoinDialog extends HookWidget {
space?.id ?? space?.id ??
spaces spaces
.firstWhere( .firstWhere(
(space) => space.children.any( (space) =>
(child) => child.metadata?.id == id, space.children.any(
(child) =>
child.metadata?.id == id,
) ||
space.subSpaces.any(
(child) =>
child.room.metadata?.id == id,
), ),
) )
.id, .id,

View file

@ -1,9 +1,11 @@
import "package:collection/collection.dart"; import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:navigation_rail_m3e/navigation_rail_m3e.dart"; import "package:navigation_rail_m3e/navigation_rail_m3e.dart";
import "package:nexus/controllers/key_controller.dart"; import "package:nexus/controllers/key_controller.dart";
import "package:nexus/controllers/spaces_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/avatar_or_hash.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.dart";
@ -43,6 +45,27 @@ class Sidebar extends HookConsumerWidget {
? null ? null
: indexOfSelectedRoom; : indexOfSelectedRoom;
List<NavigationRailM3EDestination> roomsToDestinations(IList<Room> 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( return Drawer(
width: 340, width: 340,
shape: Border(), shape: Border(),
@ -189,28 +212,14 @@ class Sidebar extends HookConsumerWidget {
selectedIndex: selectedRoomIndex ?? 0, selectedIndex: selectedRoomIndex ?? 0,
sections: [ sections: [
.new( .new(
destinations: selectedSpace.children destinations: roomsToDestinations(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,
), ),
for (final subSpace in selectedSpace.subSpaces)
.new(
header: Text(
subSpace.room.metadata?.name ?? "Unnamed Room",
), ),
) destinations: roomsToDestinations(subSpace.children),
.toList(),
), ),
], ],
onDestinationSelected: (value) { onDestinationSelected: (value) {