feat: add contact UI helpers and path editor for routing management

- Implemented contactTypeIcon and contactTypeColor functions for better UI representation of contact types.
- Created colorForName and firstCharacterOrEmoji functions to enhance contact display.
- Developed PathEditorSheet widget for managing contact paths with a user-friendly interface.
- Introduced RoutingSheet for managing contact routing modes and displaying path history.
- Added a script for generating proof of concept (PoC) payloads for clipboard contact import validation.
This commit is contained in:
zjs81
2026-06-11 00:07:12 -07:00
parent 743ef7f124
commit cba1e5950c
86 changed files with 8149 additions and 6379 deletions
+85 -55
View File
@@ -106,7 +106,7 @@ class ChannelMessagePathScreen extends StatelessWidget {
if (!hasHopDetails)
Text(
l10n.channelPath_noHopDetails,
style: const TextStyle(color: Colors.grey),
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
)
else
..._buildHopTiles(context, hops),
@@ -131,22 +131,25 @@ class ChannelMessagePathScreen extends StatelessWidget {
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
_buildDetailRow(l10n.channelPath_senderLabel, message.senderName),
_buildDetailRow(context, l10n.channelPath_senderLabel, message.senderName),
_buildDetailRow(
context,
l10n.channelPath_timeLabel,
_formatTime(message.timestamp, l10n),
),
if (message.repeatCount > 0)
_buildDetailRow(
context,
l10n.channelPath_repeatsLabel,
message.repeatCount.toString(),
),
_buildDetailRow(
context,
l10n.channelPath_pathLabelTitle,
_formatPathLabel(message.pathLength, l10n),
),
if (observedLabel != null)
_buildDetailRow(l10n.channelPath_observedLabel, observedLabel),
_buildDetailRow(context, l10n.channelPath_observedLabel, observedLabel),
],
),
),
@@ -250,7 +253,7 @@ class ChannelMessagePathScreen extends StatelessWidget {
return l10n.channelPath_observedSomeOf(observedCount, pathLength);
}
Widget _buildDetailRow(String label, String value) {
Widget _buildDetailRow(BuildContext context, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
@@ -258,7 +261,7 @@ class ChannelMessagePathScreen extends StatelessWidget {
children: [
SizedBox(
width: 70,
child: Text(label, style: TextStyle(color: Colors.grey[600])),
child: Text(label, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)),
),
Expanded(child: Text(value)),
],
@@ -412,17 +415,17 @@ class _ChannelMessagePathMapScreenState
children: [
IconButton(
icon: const Icon(Icons.add),
tooltip: 'Zoom in',
tooltip: context.l10n.map_zoomIn,
onPressed: () => _zoomMapBy(1),
),
IconButton(
icon: const Icon(Icons.remove),
tooltip: 'Zoom out',
tooltip: context.l10n.map_zoomOut,
onPressed: () => _zoomMapBy(-1),
),
IconButton(
icon: const Icon(Icons.my_location),
tooltip: 'Center map',
tooltip: context.l10n.map_centerMap,
onPressed: () => _resetMapView(
initialCenter: initialCenter,
initialZoom: initialZoom,
@@ -593,7 +596,7 @@ class _ChannelMessagePathMapScreenState
if (points.isEmpty)
Center(
child: Card(
color: Colors.white.withValues(alpha: 0.9),
color: Theme.of(context).colorScheme.surface.withValues(alpha: 0.9),
child: Padding(
padding: EdgeInsets.all(12),
child: Text(
@@ -664,7 +667,7 @@ class _ChannelMessagePathMapScreenState
label,
_formatPathPrefixes(selectedPath.pathBytes),
),
style: TextStyle(color: Colors.grey[700], fontSize: 12),
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 12),
),
],
),
@@ -685,28 +688,32 @@ class _ChannelMessagePathMapScreenState
markers.add(
Marker(
point: point,
width: 35,
height: 35,
child: Container(
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 4,
offset: const Offset(0, 2),
width: 48,
height: 48,
child: Center(
child: Container(
width: 35,
height: 35,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
alignment: Alignment.center,
child: Text(
hop.index.toString(),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
],
),
alignment: Alignment.center,
child: Text(
hop.index.toString(),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
@@ -729,28 +736,32 @@ class _ChannelMessagePathMapScreenState
markers.add(
Marker(
point: selfPoint,
width: 35,
height: 35,
child: Container(
decoration: BoxDecoration(
color: Colors.teal,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 4,
offset: const Offset(0, 2),
width: 48,
height: 48,
child: Center(
child: Container(
width: 35,
height: 35,
decoration: BoxDecoration(
color: Colors.teal,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
alignment: Alignment.center,
child: Text(
context.l10n.pathTrace_you,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
],
),
alignment: Alignment.center,
child: Text(
context.l10n.pathTrace_you,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
@@ -804,6 +815,12 @@ class _ChannelMessagePathMapScreenState
);
}
Widget _colorDot(Color color) => Container(
width: 10,
height: 10,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
);
Widget _buildLegendCard(
BuildContext context,
List<_PathHop> hops,
@@ -826,9 +843,22 @@ class _ChannelMessagePathMapScreenState
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Text(
'${l10n.channelPath_repeaterHops} ${formatDistance(_pathDistance, isImperial: isImperial)}',
style: const TextStyle(fontWeight: FontWeight.w600),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${l10n.channelPath_repeaterHops} ${formatDistance(_pathDistance, isImperial: isImperial)}',
style: const TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 6),
Row(
children: [
_colorDot(Colors.green),
const SizedBox(width: 4),
Text(l10n.pathTrace_legendGpsConfirmed, style: const TextStyle(fontSize: 11)),
],
),
],
),
),
const Divider(height: 1),