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,23 +31,13 @@ class ChatAudioEntry extends StatelessWidget {
return Container(); // media file was purged
}
return LayoutBuilder(
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;
final showTime = info.displayTime || message.modifiedAt != null;
return Container(
return IntrinsicWidth(
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.8,
minWidth: 250,
minWidth: 280,
),
padding: info.padding,
decoration: BoxDecoration(
@ -56,9 +46,12 @@ class ChatAudioEntry extends StatelessWidget {
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (info.displayUserName != '')
Text(
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
info.displayUserName,
textAlign: TextAlign.left,
style: const TextStyle(
@ -66,42 +59,37 @@ class ChatAudioEntry extends StatelessWidget {
fontWeight: FontWeight.bold,
),
),
),
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (isExpanded && info.text != '')
if (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 ==
else
Expanded(
child: mediaService.mediaFile.downloadState ==
DownloadState.ready ||
mediaService.mediaFile.downloadState == null)
mediaService.tempPath.existsSync()
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),
: Container())
: MessageSendStateIcon([message], [mediaService.mediaFile]),
),
if (showTime) FriendlyMessageTime(message: message),
],
),
],
),
);
},
),
);
}
}

View file

@ -34,20 +34,10 @@ class ChatTextEntry extends StatelessWidget {
);
}
return LayoutBuilder(
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;
final showTime = info.displayTime || message.modifiedAt != null;
return Container(
return IntrinsicWidth(
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.8,
minWidth: info.minWidth,
@ -59,9 +49,12 @@ class ChatTextEntry extends StatelessWidget {
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (info.displayUserName != '')
Text(
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
info.displayUserName,
textAlign: TextAlign.left,
style: const TextStyle(
@ -69,31 +62,23 @@ class ChatTextEntry extends StatelessWidget {
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),
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/visual/views/chats/chat_messages.view.dart';
class ResponseContainer extends StatefulWidget {
class ResponseContainer extends StatelessWidget {
const ResponseContainer({
required this.msg,
required this.group,
@ -27,64 +27,34 @@ class ResponseContainer extends StatefulWidget {
final BorderRadius borderRadius;
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
Widget build(BuildContext context) {
if (widget.msg.quotesMessageId == null) {
if (widget.child == null) {
if (msg.quotesMessageId == null) {
if (child == null) {
return Container();
}
return widget.child!;
return child!;
}
return GestureDetector(
onTap: widget.scrollToMessage == null
onTap: scrollToMessage == null
? null
: () => widget.scrollToMessage!(widget.msg.quotesMessageId!),
: () => scrollToMessage!(msg.quotesMessageId!),
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.8,
),
decoration: BoxDecoration(
color: getMessageColor(widget.msg.senderId != null),
borderRadius: widget.borderRadius,
color: getMessageColor(msg.senderId != null),
borderRadius: borderRadius,
),
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
key: _preview,
padding: const EdgeInsets.only(top: 4, right: 4, left: 4),
child: Container(
width: minWidth,
decoration: BoxDecoration(
color: context.color.surface.withAlpha(150),
borderRadius: const BorderRadius.only(
@ -95,20 +65,19 @@ class _ResponseContainerState extends State<ResponseContainer> {
),
),
child: ResponsePreview(
group: widget.group,
messageId: widget.msg.quotesMessageId,
group: group,
messageId: msg.quotesMessageId,
showBorder: false,
showLeftBorder: false,
colorUsername: false,
),
),
),
SizedBox(
key: _message,
width: minWidth,
child: widget.child,
),
if (child != null) child!,
],
),
),
),
);
}
}
@ -119,6 +88,8 @@ class ResponsePreview extends StatefulWidget {
required this.showBorder,
this.message,
this.messageId,
this.showLeftBorder = true,
this.colorUsername = false,
super.key,
});
@ -126,6 +97,8 @@ class ResponsePreview extends StatefulWidget {
final String? messageId;
final Group group;
final bool showBorder;
final bool showLeftBorder;
final bool colorUsername;
@override
State<ResponsePreview> createState() => _ResponsePreviewState();
@ -212,80 +185,85 @@ class _ResponsePreviewState extends State<ResponsePreview> {
if (_message!.senderId == null) {
_username = context.lang.you;
// _username = _message!.senderId.toString();
}
color = getMessageColor(_message!.senderId != null);
if (!_message!.mediaStored) {
return Container(
padding: widget.showBorder
? const EdgeInsets.only(left: 10, right: 10)
: const EdgeInsets.symmetric(horizontal: 5),
decoration: (widget.showBorder)
? BoxDecoration(
border: Border(
left: BorderSide(
color: color,
width: 2,
),
),
)
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_username,
style: const TextStyle(fontWeight: FontWeight.bold),
),
if (subtitle != null) Text(subtitle),
],
),
);
}
}
return Container(
padding: const EdgeInsets.only(left: 10),
width: 200,
decoration: BoxDecoration(
border: Border(
left: BorderSide(
color: color,
width: 2,
),
),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_username,
style: const TextStyle(fontWeight: FontWeight.bold),
),
if (subtitle != null) Text(subtitle),
],
),
),
if (_mediaService != null &&
_mediaService!.mediaFile.type != MediaType.audio)
() {
final hasImage =
_message != null &&
_message!.mediaStored &&
_mediaService != null &&
_mediaService!.mediaFile.type != MediaType.audio;
Widget? imageWidget;
if (hasImage) {
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),
imageWidget = Container(
height: 40,
width: 40,
margin: const EdgeInsets.only(left: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.file(
pathToCheck,
fit: BoxFit.cover,
),
),
);
}
return const SizedBox.shrink();
}(),
}
return Container(
padding: EdgeInsets.only(
left: widget.showLeftBorder ? 8 : 4,
right: 6,
top: 4,
bottom: 4,
),
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(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
_username,
style: TextStyle(
fontWeight: FontWeight.bold,
color: widget.colorUsername ? color : null,
),
),
if (subtitle != null)
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
if (imageWidget != null) imageWidget,
],
),
);