chore: Follow up spaces UX

This commit is contained in:
Christian Kußowski 2025-10-19 13:00:47 +02:00
commit 765bff9e32
No known key found for this signature in database
GPG key ID: E067ECD60F1A0652
5 changed files with 25 additions and 89 deletions

View file

@ -3383,5 +3383,14 @@
"moveToDifferentSpace": "Move to different space", "moveToDifferentSpace": "Move to different space",
"moveUp": "Move up", "moveUp": "Move up",
"moveDown": "Move down", "moveDown": "Move down",
"removeFromSpaceDescription": "The chat will be removed from the space but still appear in your chat list." "removeFromSpaceDescription": "The chat will be removed from the space but still appear in your chat list.",
"countChats": "{chats} chats",
"@countChats": {
"type": "String",
"placeholders": {
"chats": {
"type": "int"
}
}
}
} }

View file

@ -36,10 +36,7 @@ class ChatListViewBody extends StatelessWidget {
spaceId: activeSpace, spaceId: activeSpace,
onBack: controller.clearActiveSpace, onBack: controller.clearActiveSpace,
onChatTab: (room) => controller.onChatTap(room), onChatTab: (room) => controller.onChatTap(room),
onChatContext: (room, context) =>
controller.chatContextAction(room, context),
activeChat: controller.activeChat, activeChat: controller.activeChat,
toParentSpace: controller.setActiveSpace,
); );
} }
final spaces = client.rooms.where((r) => r.isSpace); final spaces = client.rooms.where((r) => r.isSpace);

View file

@ -242,10 +242,8 @@ class ChatListItem extends StatelessWidget {
Expanded( Expanded(
child: room.isSpace && room.membership == Membership.join child: room.isSpace && room.membership == Membership.join
? Text( ? Text(
L10n.of(context).countChatsAndCountParticipants( L10n.of(context)
room.spaceChildren.length, .countChats(room.spaceChildren.length),
(room.summary.mJoinedMemberCount ?? 1),
),
style: TextStyle(color: theme.colorScheme.outline), style: TextStyle(color: theme.colorScheme.outline),
) )
: typingText.isNotEmpty : typingText.isNotEmpty

View file

@ -38,9 +38,7 @@ enum SpaceActions {
class SpaceView extends StatefulWidget { class SpaceView extends StatefulWidget {
final String spaceId; final String spaceId;
final void Function() onBack; final void Function() onBack;
final void Function(String spaceId) toParentSpace;
final void Function(Room room) onChatTab; final void Function(Room room) onChatTab;
final void Function(Room room, BuildContext context) onChatContext;
final String? activeChat; final String? activeChat;
const SpaceView({ const SpaceView({
@ -48,8 +46,6 @@ class SpaceView extends StatefulWidget {
required this.onBack, required this.onBack,
required this.onChatTab, required this.onChatTab,
required this.activeChat, required this.activeChat,
required this.toParentSpace,
required this.onChatContext,
super.key, super.key,
}); });
@ -525,25 +521,6 @@ class _SpaceViewState extends State<SpaceView> {
.where((s) => s.hasRoomUpdate) .where((s) => s.hasRoomUpdate)
.rateLimit(const Duration(seconds: 1)), .rateLimit(const Duration(seconds: 1)),
builder: (context, snapshot) { builder: (context, snapshot) {
final childrenIds = room.spaceChildren
.map((c) => c.roomId)
.whereType<String>()
.toSet();
final joinedRooms = Map.fromEntries(
room.client.rooms
.where((room) => childrenIds.remove(room.id))
.map((room) => MapEntry(room.id, room)),
);
final joinedParents = room.spaceParents
.map((parent) {
final roomId = parent.roomId;
if (roomId == null) return null;
return room.client.getRoomById(roomId);
})
.whereType<Room>()
.toList();
final filter = _filterController.text.trim().toLowerCase(); final filter = _filterController.text.trim().toLowerCase();
return CustomScrollView( return CustomScrollView(
slivers: [ slivers: [
@ -575,47 +552,6 @@ class _SpaceViewState extends State<SpaceView> {
), ),
), ),
), ),
SliverList.builder(
itemCount: joinedParents.length,
itemBuilder: (context, i) {
final displayname =
joinedParents[i].getLocalizedDisplayname();
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 1,
),
child: Material(
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
clipBehavior: Clip.hardEdge,
child: ListTile(
minVerticalPadding: 0,
leading: Icon(
Icons.adaptive.arrow_back_outlined,
size: 16,
),
title: Row(
children: [
Avatar(
mxContent: joinedParents[i].avatar,
name: displayname,
size: Avatar.defaultSize / 2,
borderRadius: BorderRadius.circular(
AppConfig.borderRadius / 4,
),
),
const SizedBox(width: 8),
Expanded(child: Text(displayname)),
],
),
onTap: () =>
widget.toParentSpace(joinedParents[i].id),
),
),
);
},
),
SliverList.builder( SliverList.builder(
itemCount: _discoveredChildren.length + 1, itemCount: _discoveredChildren.length + 1,
itemBuilder: (context, i) { itemBuilder: (context, i) {
@ -643,7 +579,10 @@ class _SpaceViewState extends State<SpaceView> {
if (!displayname.toLowerCase().contains(filter)) { if (!displayname.toLowerCase().contains(filter)) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
final joinedRoom = joinedRooms[item.roomId]; var joinedRoom = room.client.getRoomById(item.roomId);
if (joinedRoom?.membership == Membership.leave) {
joinedRoom = null;
}
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, horizontal: 8,
@ -664,7 +603,7 @@ class _SpaceViewState extends State<SpaceView> {
contentPadding: contentPadding:
const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 8),
onTap: joinedRoom != null onTap: joinedRoom != null
? () => widget.onChatTab(joinedRoom) ? () => widget.onChatTab(joinedRoom!)
: () => _joinChildRoom(item), : () => _joinChildRoom(item),
onLongPress: isAdmin onLongPress: isAdmin
? () => _showSpaceChildEditMenu( ? () => _showSpaceChildEditMenu(

View file

@ -43,15 +43,8 @@ class SpacesNavigationRail extends StatelessWidget {
.where((s) => s.hasRoomUpdate) .where((s) => s.hasRoomUpdate)
.rateLimit(const Duration(seconds: 1)), .rateLimit(const Duration(seconds: 1)),
builder: (context, _) { builder: (context, _) {
final allSpaces = client.rooms.where((room) => room.isSpace); final allSpaces =
final rootSpaces = allSpaces client.rooms.where((room) => room.isSpace).toList();
.where(
(space) => !allSpaces.any(
(parentSpace) => parentSpace.spaceChildren
.any((child) => child.roomId == space.id),
),
)
.toList();
return SizedBox( return SizedBox(
width: FluffyThemes.isColumnMode(context) width: FluffyThemes.isColumnMode(context)
@ -62,7 +55,7 @@ class SpacesNavigationRail extends StatelessWidget {
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
itemCount: rootSpaces.length + 2, itemCount: allSpaces.length + 2,
itemBuilder: (context, i) { itemBuilder: (context, i) {
if (i == 0) { if (i == 0) {
return NaviRailItem( return NaviRailItem(
@ -81,7 +74,7 @@ class SpacesNavigationRail extends StatelessWidget {
); );
} }
i--; i--;
if (i == rootSpaces.length) { if (i == allSpaces.length) {
return NaviRailItem( return NaviRailItem(
isSelected: false, isSelected: false,
onTap: () => context.go('/rooms/newspace'), onTap: () => context.go('/rooms/newspace'),
@ -92,9 +85,9 @@ class SpacesNavigationRail extends StatelessWidget {
toolTip: L10n.of(context).createNewSpace, toolTip: L10n.of(context).createNewSpace,
); );
} }
final space = rootSpaces[i]; final space = allSpaces[i];
final displayname = final displayname =
rootSpaces[i].getLocalizedDisplayname( allSpaces[i].getLocalizedDisplayname(
MatrixLocals(L10n.of(context)), MatrixLocals(L10n.of(context)),
); );
final spaceChildrenIds = final spaceChildrenIds =
@ -102,11 +95,11 @@ class SpacesNavigationRail extends StatelessWidget {
return NaviRailItem( return NaviRailItem(
toolTip: displayname, toolTip: displayname,
isSelected: activeSpaceId == space.id, isSelected: activeSpaceId == space.id,
onTap: () => onGoToSpaceId(rootSpaces[i].id), onTap: () => onGoToSpaceId(allSpaces[i].id),
unreadBadgeFilter: (room) => unreadBadgeFilter: (room) =>
spaceChildrenIds.contains(room.id), spaceChildrenIds.contains(room.id),
icon: Avatar( icon: Avatar(
mxContent: rootSpaces[i].avatar, mxContent: allSpaces[i].avatar,
name: displayname, name: displayname,
border: BorderSide( border: BorderSide(
width: 1, width: 1,