💄 Optimize chat list expansion tiles and close #210

This commit is contained in:
2025-12-27 22:18:21 +08:00
parent 5d5bda7925
commit 6e7eedc026

View File

@@ -78,119 +78,151 @@ class ChatListBodyWidget extends HookConsumerWidget {
onRefresh: () => Future.sync(() { onRefresh: () => Future.sync(() {
ref.invalidate(chatRoomJoinedProvider); ref.invalidate(chatRoomJoinedProvider);
}), }),
child: Column( child: Theme(
children: [ data: Theme.of(
// Always show pinned chats in their own section context,
if (pinnedItems.isNotEmpty) ).copyWith(dividerColor: Colors.transparent),
ExpansionTile( child: Column(
backgroundColor: Theme.of( children: [
context, // Always show pinned chats in their own section
).colorScheme.surfaceContainer.withOpacity(0.5), if (pinnedItems.isNotEmpty)
collapsedBackgroundColor: Theme.of( ExpansionTile(
context, backgroundColor: Theme.of(context)
).colorScheme.surfaceContainer.withOpacity(0.5), .colorScheme
title: Text('pinnedChatRoom'.tr()), .surfaceContainerHighest
leading: const Icon(Symbols.keep, fill: 1), .withOpacity(0.5),
tilePadding: const EdgeInsets.symmetric(horizontal: 24), collapsedBackgroundColor: Theme.of(
initiallyExpanded: true, context,
children: [ ).colorScheme.surfaceContainer.withOpacity(0.5),
for (final item in pinnedItems) title: Text('pinnedChatRoom'.tr()),
ChatRoomListTile( leading: const Icon(Symbols.keep, fill: 1),
room: item, tilePadding: const EdgeInsets.symmetric(
isDirect: item.type == 1, horizontal: 24,
onTap: () { ),
if (isWideScreen(context)) { initiallyExpanded: true,
context.replaceNamed( children: [
'chatRoom', for (final item in pinnedItems)
pathParameters: {'id': item.id}, ChatRoomListTile(
); room: item,
} else { isDirect: item.type == 1,
context.pushNamed( onTap: () {
'chatRoom', if (isWideScreen(context)) {
pathParameters: {'id': item.id}, context.replaceNamed(
); 'chatRoom',
pathParameters: {'id': item.id},
);
} else {
context.pushNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
}
},
),
],
),
Expanded(
child: Consumer(
builder: (context, ref, _) {
final summaries =
ref
.watch(chatSummaryProvider)
.whenData((data) => data)
.value ??
{};
if (settings.groupedChatList &&
selectedTab.value == 0) {
// Group by realm (include both pinned and unpinned)
final realmGroups = <String?, List<SnChatRoom>>{};
final ungrouped = <SnChatRoom>[];
for (final item in filteredItems) {
if (item.realmId != null) {
realmGroups
.putIfAbsent(item.realmId, () => [])
.add(item);
} else if (!item.isPinned) {
// Only unpinned chats without realm go to ungrouped
ungrouped.add(item);
} }
},
),
],
),
Expanded(
child: Consumer(
builder: (context, ref, _) {
final summaries =
ref
.watch(chatSummaryProvider)
.whenData((data) => data)
.value ??
{};
if (settings.groupedChatList &&
selectedTab.value == 0) {
// Group by realm (include both pinned and unpinned)
final realmGroups = <String?, List<SnChatRoom>>{};
final ungrouped = <SnChatRoom>[];
for (final item in filteredItems) {
if (item.realmId != null) {
realmGroups
.putIfAbsent(item.realmId, () => [])
.add(item);
} else if (!item.isPinned) {
// Only unpinned chats without realm go to ungrouped
ungrouped.add(item);
} }
}
final children = <Widget>[]; final children = <Widget>[];
// Add realm groups // Add realm groups
for (final entry in realmGroups.entries) { for (final entry in realmGroups.entries) {
final rooms = entry.value; final rooms = entry.value;
final realm = rooms.first.realm; final realm = rooms.first.realm;
final realmName = realm?.name ?? 'Unknown Realm'; final realmName =
realm?.name ?? 'Unknown Realm';
// Calculate total unread count for this realm // Calculate total unread count for this realm
final totalUnread = rooms.fold<int>( final totalUnread = rooms.fold<int>(
0, 0,
(sum, room) => (sum, room) =>
sum + sum +
(summaries[room.id]?.unreadCount ?? 0), (summaries[room.id]?.unreadCount ?? 0),
); );
children.add( children.add(
ExpansionTile( ExpansionTile(
backgroundColor: Theme.of(context) backgroundColor: Theme.of(context)
.colorScheme .colorScheme
.surfaceContainer .surfaceContainerHighest
.withOpacity(0.5), .withOpacity(0.5),
collapsedBackgroundColor: Theme.of(context) collapsedBackgroundColor:
.colorScheme Colors.transparent,
.surfaceContainer title: Row(
.withOpacity(0.5), children: [
title: Row( Expanded(child: Text(realmName)),
children: [ Badge(
Expanded(child: Text(realmName)), isLabelVisible: totalUnread > 0,
Badge( label: Text(totalUnread.toString()),
isLabelVisible: totalUnread > 0, backgroundColor: Theme.of(
label: Text(totalUnread.toString()), context,
backgroundColor: Theme.of( ).colorScheme.primary,
context, textColor: Theme.of(
).colorScheme.primary, context,
textColor: Theme.of( ).colorScheme.onPrimary,
context, ),
).colorScheme.onPrimary, ],
), ),
], leading: ProfilePictureWidget(
file: realm?.picture,
radius: 16,
),
tilePadding: const EdgeInsets.only(
left: 20,
right: 24,
),
children: rooms.map((room) {
return ChatRoomListTile(
room: room,
isDirect: room.type == 1,
onTap: () {
if (isWideScreen(context)) {
context.replaceNamed(
'chatRoom',
pathParameters: {'id': room.id},
);
} else {
context.pushNamed(
'chatRoom',
pathParameters: {'id': room.id},
);
}
},
);
}).toList(),
), ),
leading: ProfilePictureWidget( );
file: realm?.picture, }
radius: 16,
), // Add ungrouped chats
tilePadding: const EdgeInsets.only( if (ungrouped.isNotEmpty) {
left: 20, children.addAll(
right: 24, ungrouped.map((room) {
),
children: rooms.map((room) {
return ChatRoomListTile( return ChatRoomListTile(
room: room, room: room,
isDirect: room.type == 1, isDirect: room.type == 1,
@@ -208,80 +240,55 @@ class ChatListBodyWidget extends HookConsumerWidget {
} }
}, },
); );
}).toList(), }),
), );
); }
}
// Add ungrouped chats return ListView(
if (ungrouped.isNotEmpty) { padding: EdgeInsets.only(bottom: 96),
children.addAll( children: children,
ungrouped.map((room) { );
} else {
// Normal list view
return SuperListView.builder(
padding: EdgeInsets.only(bottom: 96),
itemCount: unpinnedItems
.where(
(item) =>
selectedTab.value == 0 ||
(selectedTab.value == 1 &&
item.type == 1) ||
(selectedTab.value == 2 &&
item.type != 1),
)
.length,
itemBuilder: (context, index) {
final item = unpinnedItems[index];
return ChatRoomListTile( return ChatRoomListTile(
room: room, room: item,
isDirect: room.type == 1, isDirect: item.type == 1,
onTap: () { onTap: () {
if (isWideScreen(context)) { if (isWideScreen(context)) {
context.replaceNamed( context.replaceNamed(
'chatRoom', 'chatRoom',
pathParameters: {'id': room.id}, pathParameters: {'id': item.id},
); );
} else { } else {
context.pushNamed( context.pushNamed(
'chatRoom', 'chatRoom',
pathParameters: {'id': room.id}, pathParameters: {'id': item.id},
); );
} }
}, },
); );
}), },
); );
} }
},
return ListView( ),
padding: EdgeInsets.only(bottom: 96),
children: children,
);
} else {
// Normal list view
return SuperListView.builder(
padding: EdgeInsets.only(bottom: 96),
itemCount: unpinnedItems
.where(
(item) =>
selectedTab.value == 0 ||
(selectedTab.value == 1 &&
item.type == 1) ||
(selectedTab.value == 2 &&
item.type != 1),
)
.length,
itemBuilder: (context, index) {
final item = unpinnedItems[index];
return ChatRoomListTile(
room: item,
isDirect: item.type == 1,
onTap: () {
if (isWideScreen(context)) {
context.replaceNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
} else {
context.pushNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
}
},
);
},
);
}
},
), ),
), ],
], ),
), ),
); );
}, },