💄 Optimize chat list expansion tiles and close #210
This commit is contained in:
@@ -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},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user