chat fixes

This commit is contained in:
Ben Allfree
2026-02-23 04:09:27 -08:00
parent 7465e81996
commit 173fdf7168
5 changed files with 187 additions and 90 deletions
+59 -45
View File
@@ -4,6 +4,7 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
@@ -23,6 +24,7 @@ import '../widgets/emoji_picker.dart';
import '../widgets/gif_message.dart';
import '../widgets/jump_to_bottom_button.dart';
import '../widgets/gif_picker.dart';
import '../widgets/message_status_icon.dart';
import 'channel_message_path_screen.dart';
import 'map_screen.dart';
@@ -337,7 +339,23 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
const SizedBox(height: 8),
],
if (poi != null)
_buildPoiMessage(context, poi, isOutgoing)
_buildPoiMessage(
context,
poi,
isOutgoing,
trailing: (!enableTracing && isOutgoing)
? Padding(
padding: const EdgeInsets.only(bottom: 2),
child: MessageStatusIcon(
isAcked: message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty,
isFailed: message.status ==
ChannelMessageStatus.failed,
),
)
: null,
)
else if (gifId != null)
Stack(
children: [
@@ -358,33 +376,31 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
),
if (!enableTracing && isOutgoing)
Positioned(
top: 4,
right: 4,
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3),
shape: BoxShape.circle,
color: isOutgoing
? Theme.of(
context,
).colorScheme.primaryContainer
: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topRight: Radius.circular(8),
),
),
child: Icon(
(message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty)
? Icons.check_circle
: message.status ==
ChannelMessageStatus.failed
? Icons.cancel
: Icons.cloud,
size: 14,
color:
(message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty)
? Colors.green
: message.status ==
ChannelMessageStatus.failed
? Colors.red
: Colors.white70,
child: MessageStatusIcon(
isAcked:
message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty,
isFailed:
message.status ==
ChannelMessageStatus.failed,
),
),
),
@@ -419,25 +435,14 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
const SizedBox(width: 4),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Icon(
(message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty)
? Icons.check_circle
: message.status ==
ChannelMessageStatus.failed
? Icons.cancel
: Icons.cloud,
size: 14,
color:
(message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty)
? Colors.green
: message.status ==
ChannelMessageStatus.failed
? Colors.red
: Colors.grey,
child: MessageStatusIcon(
isAcked:
message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty,
isFailed:
message.status ==
ChannelMessageStatus.failed,
),
),
],
@@ -727,7 +732,12 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
return _PoiInfo(lat: lat, lon: lon, label: label);
}
Widget _buildPoiMessage(BuildContext context, _PoiInfo poi, bool isOutgoing) {
Widget _buildPoiMessage(
BuildContext context,
_PoiInfo poi,
bool isOutgoing, {
Widget? trailing,
}) {
final colorScheme = Theme.of(context).colorScheme;
final textColor = isOutgoing
? colorScheme.onPrimaryContainer
@@ -773,6 +783,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
],
),
),
if (trailing != null) ...[
const SizedBox(width: 4),
trailing,
],
],
);
}
+49 -45
View File
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:meshcore_open/screens/path_trace_map.dart';
import 'package:provider/provider.dart';
@@ -13,6 +14,7 @@ import 'package:latlong2/latlong.dart';
import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart';
import '../helpers/reaction_helper.dart';
import '../widgets/message_status_icon.dart';
import '../helpers/chat_scroll_controller.dart';
import '../helpers/link_handler.dart';
import '../helpers/utf8_length_limiter.dart';
@@ -1252,7 +1254,24 @@ class _MessageBubble extends StatelessWidget {
if (gifId == null) const SizedBox(height: 4),
],
if (poi != null)
_buildPoiMessage(context, poi, textColor, metaColor)
_buildPoiMessage(
context,
poi,
textColor,
metaColor,
trailing: (!enableTracing && isOutgoing)
? Padding(
padding: const EdgeInsets.only(bottom: 2),
child: MessageStatusIcon(
isAcked: message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty,
isFailed: message.status ==
MessageStatus.failed,
),
)
: null,
)
else if (gifId != null)
Stack(
children: [
@@ -1269,35 +1288,25 @@ class _MessageBubble extends StatelessWidget {
),
if (!enableTracing && isOutgoing)
Positioned(
top: 4,
right: 4,
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: Colors.black.withValues(
alpha: 0.3,
color: bubbleColor,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topRight: Radius.circular(12),
),
shape: BoxShape.circle,
),
child: Icon(
(message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty)
? Icons.check_circle
: message.status ==
MessageStatus.failed
? Icons.cancel
: Icons.cloud,
size: 14,
color:
(message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty)
? Colors.green
: message.status ==
MessageStatus.failed
? Colors.red
: Colors.white70,
child: MessageStatusIcon(
isAcked:
message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty,
isFailed:
message.status ==
MessageStatus.failed,
),
),
),
@@ -1331,23 +1340,13 @@ class _MessageBubble extends StatelessWidget {
const SizedBox(width: 4),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Icon(
(message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty)
? Icons.check_circle
: message.status == MessageStatus.failed
? Icons.cancel
: Icons.cloud,
size: 14,
color:
(message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty)
? Colors.green
: message.status == MessageStatus.failed
? Colors.red
: Colors.grey,
child: MessageStatusIcon(
isAcked:
message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty,
isFailed:
message.status == MessageStatus.failed,
),
),
],
@@ -1464,8 +1463,9 @@ class _MessageBubble extends StatelessWidget {
BuildContext context,
_PoiInfo poi,
Color textColor,
Color metaColor,
) {
Color metaColor, {
Widget? trailing,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@@ -1502,6 +1502,10 @@ class _MessageBubble extends StatelessWidget {
],
),
),
if (trailing != null) ...[
const SizedBox(width: 4),
trailing,
],
],
);
}
+36
View File
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class MessageStatusIcon extends StatelessWidget {
final bool isAcked;
final bool isFailed;
final double size;
const MessageStatusIcon({
super.key,
required this.isAcked,
this.isFailed = false,
this.size = 14,
});
@override
Widget build(BuildContext context) {
if (isFailed) {
return Icon(Icons.cancel, size: size, color: Colors.red);
}
final Color color;
if (isAcked) {
color = Colors.green;
} else {
color = Colors.grey;
}
return SvgPicture.asset(
'assets/icons/done_all.svg',
width: size,
height: size,
colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
);
}
}