fix the response view

This commit is contained in:
otsmr 2026-06-07 12:44:57 +02:00
parent b49b9cf452
commit f66e1f17bf
3 changed files with 180 additions and 229 deletions

View file

@ -31,34 +31,27 @@ class ChatAudioEntry extends StatelessWidget {
return Container(); // media file was purged return Container(); // media file was purged
} }
return LayoutBuilder( final showTime = info.displayTime || message.modifiedAt != null;
builder: (context, constraints) {
final textWidth = measureTextWidth(info.text);
const timeWidth = 60.0;
final isExpanded =
info.expanded ||
(textWidth + timeWidth + 20 > constraints.maxWidth);
final effectiveSpacerWidth =
constraints.minWidth - textWidth - timeWidth;
final spacerWidth = effectiveSpacerWidth > 0
? effectiveSpacerWidth
: 0.0;
return Container( return IntrinsicWidth(
constraints: BoxConstraints( child: Container(
maxWidth: MediaQuery.of(context).size.width * 0.8, constraints: BoxConstraints(
minWidth: 250, maxWidth: MediaQuery.of(context).size.width * 0.8,
), minWidth: 280,
padding: info.padding, ),
decoration: BoxDecoration( padding: info.padding,
color: info.color, decoration: BoxDecoration(
borderRadius: borderRadius, color: info.color,
), borderRadius: borderRadius,
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
if (info.displayUserName != '') mainAxisSize: MainAxisSize.min,
Text( children: [
if (info.displayUserName != '')
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
info.displayUserName, info.displayUserName,
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: const TextStyle( style: const TextStyle(
@ -66,42 +59,37 @@ class ChatAudioEntry extends StatelessWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (isExpanded && info.text != '')
Expanded(
child: BetterText(
text: info.text,
textColor: info.textColor,
),
)
else if (info.text != '') ...[
BetterText(text: info.text, textColor: info.textColor),
SizedBox(width: spacerWidth),
] else ...[
if (mediaService.mediaFile.downloadState ==
DownloadState.ready ||
mediaService.mediaFile.downloadState == null)
mediaService.tempPath.existsSync()
? InChatAudioPlayer(
path: mediaService.tempPath.path,
message: message,
)
: Container()
else
MessageSendStateIcon([message], [mediaService.mediaFile]),
SizedBox(width: spacerWidth),
],
if (info.displayTime || message.modifiedAt != null)
FriendlyMessageTime(message: message),
],
), ),
], Row(
), mainAxisSize: MainAxisSize.min,
); crossAxisAlignment: CrossAxisAlignment.end,
}, children: [
if (info.text != '')
Expanded(
child: BetterText(
text: info.text,
textColor: info.textColor,
),
)
else
Expanded(
child: mediaService.mediaFile.downloadState ==
DownloadState.ready ||
mediaService.mediaFile.downloadState == null
? (mediaService.tempPath.existsSync()
? InChatAudioPlayer(
path: mediaService.tempPath.path,
message: message,
)
: Container())
: MessageSendStateIcon([message], [mediaService.mediaFile]),
),
if (showTime) FriendlyMessageTime(message: message),
],
),
],
),
),
); );
} }
} }

View file

@ -34,34 +34,27 @@ class ChatTextEntry extends StatelessWidget {
); );
} }
return LayoutBuilder( final showTime = info.displayTime || message.modifiedAt != null;
builder: (context, constraints) {
final textWidth = measureTextWidth(info.text);
const timeWidth = 60.0;
final isExpanded =
info.expanded ||
(textWidth + timeWidth + 20 > constraints.maxWidth);
final effectiveSpacerWidth =
constraints.minWidth - textWidth - timeWidth;
final spacerWidth = effectiveSpacerWidth > 0
? effectiveSpacerWidth
: 0.0;
return Container( return IntrinsicWidth(
constraints: BoxConstraints( child: Container(
maxWidth: MediaQuery.of(context).size.width * 0.8, constraints: BoxConstraints(
minWidth: info.minWidth, maxWidth: MediaQuery.of(context).size.width * 0.8,
), minWidth: info.minWidth,
padding: info.padding, ),
decoration: BoxDecoration( padding: info.padding,
color: info.color, decoration: BoxDecoration(
borderRadius: borderRadius, color: info.color,
), borderRadius: borderRadius,
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
if (info.displayUserName != '') mainAxisSize: MainAxisSize.min,
Text( children: [
if (info.displayUserName != '')
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
info.displayUserName, info.displayUserName,
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: const TextStyle( style: const TextStyle(
@ -69,31 +62,23 @@ class ChatTextEntry extends StatelessWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (isExpanded)
Expanded(
child: BetterText(
text: info.text,
textColor: info.textColor,
),
)
else ...[
BetterText(text: info.text, textColor: info.textColor),
SizedBox(
width: spacerWidth,
),
],
if (info.displayTime || message.modifiedAt != null)
FriendlyMessageTime(message: message),
],
), ),
], Row(
), mainAxisSize: MainAxisSize.min,
); crossAxisAlignment: CrossAxisAlignment.end,
}, children: [
Expanded(
child: BetterText(
text: info.text,
textColor: info.textColor,
),
),
if (showTime) FriendlyMessageTime(message: message),
],
),
],
),
),
); );
} }
} }

View file

@ -9,7 +9,7 @@ import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/views/chats/chat_messages.view.dart'; import 'package:twonly/src/visual/views/chats/chat_messages.view.dart';
class ResponseContainer extends StatefulWidget { class ResponseContainer extends StatelessWidget {
const ResponseContainer({ const ResponseContainer({
required this.msg, required this.msg,
required this.group, required this.group,
@ -27,86 +27,55 @@ class ResponseContainer extends StatefulWidget {
final BorderRadius borderRadius; final BorderRadius borderRadius;
final void Function(String)? scrollToMessage; final void Function(String)? scrollToMessage;
@override
State<ResponseContainer> createState() => _ResponseContainerState();
}
class _ResponseContainerState extends State<ResponseContainer> {
double? minWidth;
final GlobalKey _message = GlobalKey();
final GlobalKey _preview = GlobalKey();
@override
void didChangeDependencies() {
super.didChangeDependencies();
WidgetsBinding.instance.addPostFrameCallback((_) {
final messageBox =
_message.currentContext?.findRenderObject() as RenderBox?;
final previewBox =
_preview.currentContext?.findRenderObject() as RenderBox?;
if (messageBox == null || previewBox == null) {
return;
}
setState(() {
if (messageBox.size.width > previewBox.size.width) {
minWidth = messageBox.size.width;
} else {
minWidth = previewBox.size.width;
}
});
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.msg.quotesMessageId == null) { if (msg.quotesMessageId == null) {
if (widget.child == null) { if (child == null) {
return Container(); return Container();
} }
return widget.child!; return child!;
} }
return GestureDetector( return GestureDetector(
onTap: widget.scrollToMessage == null onTap: scrollToMessage == null
? null ? null
: () => widget.scrollToMessage!(widget.msg.quotesMessageId!), : () => scrollToMessage!(msg.quotesMessageId!),
child: Container( child: Container(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.8, maxWidth: MediaQuery.of(context).size.width * 0.8,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: getMessageColor(widget.msg.senderId != null), color: getMessageColor(msg.senderId != null),
borderRadius: widget.borderRadius, borderRadius: borderRadius,
), ),
child: Column( child: IntrinsicWidth(
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ mainAxisSize: MainAxisSize.min,
Padding( crossAxisAlignment: CrossAxisAlignment.stretch,
key: _preview, children: [
padding: const EdgeInsets.only(top: 4, right: 4, left: 4), Padding(
child: Container( padding: const EdgeInsets.only(top: 4, right: 4, left: 4),
width: minWidth, child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: context.color.surface.withAlpha(150), color: context.color.surface.withAlpha(150),
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topRight: Radius.circular(8), topRight: Radius.circular(8),
topLeft: Radius.circular(8), topLeft: Radius.circular(8),
bottomLeft: Radius.circular(4), bottomLeft: Radius.circular(4),
bottomRight: Radius.circular(4), bottomRight: Radius.circular(4),
),
),
child: ResponsePreview(
group: group,
messageId: msg.quotesMessageId,
showBorder: false,
showLeftBorder: false,
colorUsername: false,
), ),
), ),
child: ResponsePreview(
group: widget.group,
messageId: widget.msg.quotesMessageId,
showBorder: false,
),
), ),
), if (child != null) child!,
SizedBox( ],
key: _message, ),
width: minWidth,
child: widget.child,
),
],
), ),
), ),
); );
@ -119,6 +88,8 @@ class ResponsePreview extends StatefulWidget {
required this.showBorder, required this.showBorder,
this.message, this.message,
this.messageId, this.messageId,
this.showLeftBorder = true,
this.colorUsername = false,
super.key, super.key,
}); });
@ -126,6 +97,8 @@ class ResponsePreview extends StatefulWidget {
final String? messageId; final String? messageId;
final Group group; final Group group;
final bool showBorder; final bool showBorder;
final bool showLeftBorder;
final bool colorUsername;
@override @override
State<ResponsePreview> createState() => _ResponsePreviewState(); State<ResponsePreview> createState() => _ResponsePreviewState();
@ -212,80 +185,85 @@ class _ResponsePreviewState extends State<ResponsePreview> {
if (_message!.senderId == null) { if (_message!.senderId == null) {
_username = context.lang.you; _username = context.lang.you;
// _username = _message!.senderId.toString();
} }
color = getMessageColor(_message!.senderId != null); color = getMessageColor(_message!.senderId != null);
}
if (!_message!.mediaStored) { final hasImage =
return Container( _message != null &&
padding: widget.showBorder _message!.mediaStored &&
? const EdgeInsets.only(left: 10, right: 10) _mediaService != null &&
: const EdgeInsets.symmetric(horizontal: 5), _mediaService!.mediaFile.type != MediaType.audio;
decoration: (widget.showBorder)
? BoxDecoration( Widget? imageWidget;
border: Border( if (hasImage) {
left: BorderSide( final isVideo = _mediaService!.mediaFile.type == MediaType.video;
color: color, final pathToCheck = isVideo
width: 2, ? _mediaService!.thumbnailPath
), : _mediaService!.storedPath;
), if (pathToCheck.existsSync() && pathToCheck.lengthSync() > 0) {
) imageWidget = Container(
: null, height: 40,
child: Column( width: 40,
crossAxisAlignment: CrossAxisAlignment.start, margin: const EdgeInsets.only(left: 8),
children: [ child: ClipRRect(
Text( borderRadius: BorderRadius.circular(4),
_username, child: Image.file(
style: const TextStyle(fontWeight: FontWeight.bold), pathToCheck,
), fit: BoxFit.cover,
if (subtitle != null) Text(subtitle), ),
],
), ),
); );
} }
} }
return Container( return Container(
padding: const EdgeInsets.only(left: 10), padding: EdgeInsets.only(
width: 200, left: widget.showLeftBorder ? 8 : 4,
decoration: BoxDecoration( right: 6,
border: Border( top: 4,
left: BorderSide( bottom: 4,
color: color,
width: 2,
),
),
), ),
constraints: BoxConstraints(
minWidth: 60,
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
decoration: widget.showLeftBorder
? BoxDecoration(
border: Border(
left: BorderSide(
color: color,
width: 2.5,
),
),
)
: null,
child: Row( child: Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
_username, _username,
style: const TextStyle(fontWeight: FontWeight.bold), style: TextStyle(
fontWeight: FontWeight.bold,
color: widget.colorUsername ? color : null,
),
), ),
if (subtitle != null) Text(subtitle), if (subtitle != null)
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
], ],
), ),
), ),
if (_mediaService != null && if (imageWidget != null) imageWidget,
_mediaService!.mediaFile.type != MediaType.audio)
() {
final isVideo = _mediaService!.mediaFile.type == MediaType.video;
final pathToCheck = isVideo
? _mediaService!.thumbnailPath
: _mediaService!.storedPath;
if (pathToCheck.existsSync() && pathToCheck.lengthSync() > 0) {
return SizedBox(
height: widget.showBorder ? 100 : 210,
child: Image.file(pathToCheck),
);
}
return const SizedBox.shrink();
}(),
], ],
), ),
); );