💄 Optimize attachment list
This commit is contained in:
parent
a3c8dafff9
commit
f614da7918
@ -281,11 +281,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
||||
Expanded(
|
||||
child: InfiniteList(
|
||||
reverse: true,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 12,
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
hasReachedMax: _messageController.isAllLoaded,
|
||||
itemCount: _messageController.messages.length,
|
||||
isLoading: _messageController.isLoading,
|
||||
@ -311,23 +307,20 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxWidth: 480),
|
||||
child: ChatMessage(
|
||||
data: message,
|
||||
isMerged: canMerge,
|
||||
hasMerged: canMergePrevious,
|
||||
isPending: _messageController.unconfirmedMessages.contains(message.uuid),
|
||||
onReply: (value) {
|
||||
_inputGlobalKey.currentState?.setReply(value);
|
||||
},
|
||||
onEdit: (value) {
|
||||
_inputGlobalKey.currentState?.setEdit(value);
|
||||
},
|
||||
onDelete: (value) {
|
||||
_inputGlobalKey.currentState?.deleteMessage(value);
|
||||
},
|
||||
),
|
||||
child: ChatMessage(
|
||||
data: message,
|
||||
isMerged: canMerge,
|
||||
hasMerged: canMergePrevious,
|
||||
isPending: _messageController.unconfirmedMessages.contains(message.uuid),
|
||||
onReply: (value) {
|
||||
_inputGlobalKey.currentState?.setReply(value);
|
||||
},
|
||||
onEdit: (value) {
|
||||
_inputGlobalKey.currentState?.setEdit(value);
|
||||
},
|
||||
onDelete: (value) {
|
||||
_inputGlobalKey.currentState?.deleteMessage(value);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -153,9 +153,14 @@ class _HomeDashUpdateWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeDashSpecialDayWidget extends StatelessWidget {
|
||||
class _HomeDashSpecialDayWidget extends StatefulWidget {
|
||||
const _HomeDashSpecialDayWidget();
|
||||
|
||||
@override
|
||||
State<_HomeDashSpecialDayWidget> createState() => _HomeDashSpecialDayWidgetState();
|
||||
}
|
||||
|
||||
class _HomeDashSpecialDayWidgetState extends State<_HomeDashSpecialDayWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ua = context.watch<UserProvider>();
|
||||
@ -165,21 +170,20 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
||||
|
||||
if (days.isNotEmpty) {
|
||||
return Column(
|
||||
spacing: 8,
|
||||
children: days.map((ele) {
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: Text(kSpecialDaysSymbol[ele] ?? '🎉').fontSize(24),
|
||||
title: Text('celebrate$ele').tr(args: [ua.user?.nick ?? 'user']),
|
||||
subtitle: Text(
|
||||
DateFormat('y/M/d').format(DateTime.now().copyWith(
|
||||
month: kSpecialDays[ele]?.$1,
|
||||
day: kSpecialDays[ele]?.$2,
|
||||
)),
|
||||
),
|
||||
),
|
||||
).padding(bottom: 8);
|
||||
}).toList());
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: Text(kSpecialDaysSymbol[ele] ?? '🎉').fontSize(24),
|
||||
title: Text('celebrate$ele').tr(args: [ua.user?.nick ?? 'user']),
|
||||
subtitle: Text(
|
||||
DateFormat('y/M/d').format(DateTime.now().copyWith(
|
||||
month: kSpecialDays[ele]?.$1,
|
||||
day: kSpecialDays[ele]?.$2,
|
||||
)),
|
||||
),
|
||||
),
|
||||
).padding(bottom: 8);
|
||||
}).toList());
|
||||
}
|
||||
|
||||
final nextOne = dayz.getNextSpecialDay();
|
||||
@ -204,6 +208,9 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
||||
separatorType: SeparatorType.symbol,
|
||||
decoration: BoxDecoration(),
|
||||
padding: EdgeInsets.zero,
|
||||
onDone: () {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
const Gap(12),
|
||||
Expanded(
|
||||
|
@ -106,76 +106,38 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
}
|
||||
|
||||
if (widget.gridded) {
|
||||
return Padding(
|
||||
padding: widget.padding ?? EdgeInsets.zero,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
border: Border(
|
||||
top: borderSide,
|
||||
bottom: borderSide,
|
||||
),
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
child: StaggeredGrid.count(
|
||||
crossAxisCount: math.min(widget.data.length, 2),
|
||||
crossAxisSpacing: 4,
|
||||
mainAxisSpacing: 4,
|
||||
children: widget.data
|
||||
.mapIndexed(
|
||||
(idx, ele) => GestureDetector(
|
||||
child: Container(
|
||||
constraints: constraints,
|
||||
child: AttachmentItem(
|
||||
data: ele,
|
||||
heroTag: heroTags[idx],
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.data[idx]!.mediaType != SnMediaType.image) return;
|
||||
context.pushTransparentRoute(
|
||||
AttachmentZoomView(
|
||||
data: widget.data.where((ele) => ele != null).cast(),
|
||||
initialIndex: idx,
|
||||
heroTags: heroTags,
|
||||
),
|
||||
backgroundColor: Colors.black.withOpacity(0.7),
|
||||
rootNavigator: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
return Container(
|
||||
margin: widget.padding ?? EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
border: Border(
|
||||
top: borderSide,
|
||||
bottom: borderSide,
|
||||
),
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return AspectRatio(
|
||||
aspectRatio: (widget.data.firstOrNull?.data['ratio'] ?? 1).toDouble(),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
||||
child: ScrollConfiguration(
|
||||
behavior: _AttachmentListScrollBehavior(),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.data.length,
|
||||
itemBuilder: (context, idx) {
|
||||
return Container(
|
||||
constraints: constraints,
|
||||
child: AspectRatio(
|
||||
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
||||
child: GestureDetector(
|
||||
child: ClipRRect(
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
child: StaggeredGrid.count(
|
||||
crossAxisCount: math.min(widget.data.length, 2),
|
||||
crossAxisSpacing: 4,
|
||||
mainAxisSpacing: 4,
|
||||
children: widget.data
|
||||
.mapIndexed(
|
||||
(idx, ele) => GestureDetector(
|
||||
child: Container(
|
||||
constraints: constraints,
|
||||
child: AttachmentItem(
|
||||
data: ele,
|
||||
heroTag: heroTags[idx],
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
||||
if (widget.data[idx]!.mediaType != SnMediaType.image) return;
|
||||
context.pushTransparentRoute(
|
||||
AttachmentZoomView(
|
||||
data:
|
||||
widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
||||
data: widget.data.where((ele) => ele != null).cast(),
|
||||
initialIndex: idx,
|
||||
heroTags: heroTags,
|
||||
),
|
||||
@ -183,44 +145,76 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
rootNavigator: true,
|
||||
);
|
||||
},
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
border: Border(
|
||||
top: borderSide,
|
||||
bottom: borderSide,
|
||||
),
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
||||
child: ScrollConfiguration(
|
||||
behavior: _AttachmentListScrollBehavior(),
|
||||
child: ListView.separated(
|
||||
padding: widget.padding,
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.data.length,
|
||||
itemBuilder: (context, idx) {
|
||||
return Container(
|
||||
constraints: constraints.copyWith(maxWidth: widget.maxWidth),
|
||||
child: AspectRatio(
|
||||
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
||||
context.pushTransparentRoute(
|
||||
AttachmentZoomView(
|
||||
data: widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
||||
initialIndex: idx,
|
||||
heroTags: heroTags,
|
||||
),
|
||||
backgroundColor: Colors.black.withOpacity(0.7),
|
||||
rootNavigator: true,
|
||||
);
|
||||
},
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
border: Border(
|
||||
top: borderSide,
|
||||
bottom: borderSide,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
child: AttachmentItem(
|
||||
data: widget.data[idx],
|
||||
heroTag: heroTags[idx],
|
||||
),
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: AttachmentList.kDefaultRadius,
|
||||
child: AttachmentItem(
|
||||
data: widget.data[idx],
|
||||
heroTag: heroTags[idx],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
child: Chip(
|
||||
label: Text('${idx + 1}/${widget.data.length}'),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
child: Chip(
|
||||
label: Text('${idx + 1}/${widget.data.length}'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Gap(8),
|
||||
padding: widget.padding,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Gap(8),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -231,7 +231,7 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
|
||||
children: [
|
||||
IgnorePointer(
|
||||
child: AccountImage(
|
||||
content: account!.avatar,
|
||||
content: account?.avatar,
|
||||
radius: 19,
|
||||
),
|
||||
),
|
||||
@ -246,7 +246,7 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
Text(
|
||||
account.nick,
|
||||
account?.nick ?? 'unknown'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
|
@ -24,6 +24,7 @@ class ChatMessage extends StatelessWidget {
|
||||
final Function(SnChatMessage)? onReply;
|
||||
final Function(SnChatMessage)? onEdit;
|
||||
final Function(SnChatMessage)? onDelete;
|
||||
final EdgeInsets padding;
|
||||
|
||||
const ChatMessage({
|
||||
super.key,
|
||||
@ -35,6 +36,7 @@ class ChatMessage extends StatelessWidget {
|
||||
this.onReply,
|
||||
this.onEdit,
|
||||
this.onDelete,
|
||||
this.padding = const EdgeInsets.only(left: 12, right: 12),
|
||||
});
|
||||
|
||||
@override
|
||||
@ -87,83 +89,89 @@ class ChatMessage extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isMerged && !isCompact)
|
||||
AccountImage(
|
||||
content: user?.avatar,
|
||||
)
|
||||
else if (isMerged)
|
||||
const Gap(40),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isMerged)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (isCompact)
|
||||
AccountImage(
|
||||
content: user?.avatar,
|
||||
radius: 12,
|
||||
).padding(right: 8),
|
||||
Text(
|
||||
(data.sender.nick?.isNotEmpty ?? false) ? data.sender.nick! : user?.nick ?? 'unknown',
|
||||
).bold(),
|
||||
const Gap(8),
|
||||
Text(
|
||||
dateFormatter.format(data.createdAt.toLocal()),
|
||||
).fontSize(13),
|
||||
],
|
||||
),
|
||||
if (isCompact) const Gap(4),
|
||||
if (data.preload?.quoteEvent != null)
|
||||
StyledWidget(Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
Padding(
|
||||
padding: isCompact ? EdgeInsets.zero : padding,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isMerged && !isCompact)
|
||||
AccountImage(
|
||||
content: user?.avatar,
|
||||
)
|
||||
else if (isMerged)
|
||||
const Gap(40),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxWidth: 480),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isMerged)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (isCompact)
|
||||
AccountImage(
|
||||
content: user?.avatar,
|
||||
radius: 12,
|
||||
).padding(right: 8),
|
||||
Text(
|
||||
(data.sender.nick?.isNotEmpty ?? false) ? data.sender.nick! : user?.nick ?? 'unknown',
|
||||
).bold(),
|
||||
const Gap(8),
|
||||
Text(
|
||||
dateFormatter.format(data.createdAt.toLocal()),
|
||||
).fontSize(13),
|
||||
],
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 4,
|
||||
right: 4,
|
||||
top: 8,
|
||||
bottom: 6,
|
||||
),
|
||||
child: ChatMessage(
|
||||
data: data.preload!.quoteEvent!,
|
||||
isCompact: true,
|
||||
onReply: onReply,
|
||||
onEdit: onEdit,
|
||||
onDelete: onDelete,
|
||||
),
|
||||
)).padding(bottom: 4, top: 4),
|
||||
switch (data.type) {
|
||||
'messages.new' => _ChatMessageText(data: data),
|
||||
_ => _ChatMessageSystemNotify(data: data),
|
||||
},
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
).opacity(isPending ? 0.5 : 1),
|
||||
if (isCompact) const Gap(8),
|
||||
if (data.preload?.quoteEvent != null)
|
||||
StyledWidget(Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 4,
|
||||
right: 4,
|
||||
top: 8,
|
||||
bottom: 6,
|
||||
),
|
||||
child: ChatMessage(
|
||||
data: data.preload!.quoteEvent!,
|
||||
isCompact: true,
|
||||
onReply: onReply,
|
||||
onEdit: onEdit,
|
||||
onDelete: onDelete,
|
||||
),
|
||||
)).padding(bottom: 4, top: 4),
|
||||
switch (data.type) {
|
||||
'messages.new' => _ChatMessageText(data: data),
|
||||
_ => _ChatMessageSystemNotify(data: data),
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
).opacity(isPending ? 0.5 : 1),
|
||||
),
|
||||
if (data.body['text'] != null && data.type == 'messages.new' && (data.body['text']?.isNotEmpty ?? false))
|
||||
LinkPreviewWidget(text: data.body['text']!),
|
||||
if (data.preload?.attachments?.isNotEmpty ?? false)
|
||||
AttachmentList(
|
||||
data: data.preload!.attachments!,
|
||||
bordered: true,
|
||||
// gridded: true,
|
||||
maxHeight: 560,
|
||||
maxWidth: 480,
|
||||
minWidth: 480,
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
padding: padding.copyWith(top: 8),
|
||||
),
|
||||
if (!hasMerged && !isCompact) const Gap(12) else if (!isCompact) const Gap(6),
|
||||
if (!hasMerged && !isCompact) const Gap(12) else if (!isCompact) const Gap(8),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user