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
+39 -47
View File
@@ -95,12 +95,14 @@ class _TcpScreenState extends State<TcpScreen> {
final isConnecting =
connector.state == MeshCoreConnectionState.connecting &&
connector.activeTransport == MeshCoreTransportType.tcp;
final isButtonDisabled =
isConnecting ||
connector.state == MeshCoreConnectionState.scanning;
// A running BLE scan must not block TCP connect: connectTcp() stops
// any active scan before connecting, so the only reason to disable
// the button is a TCP connect already in flight.
final isButtonDisabled = isConnecting;
return Column(
children: [
_buildStatusBar(context, connector),
_buildTransportLinks(context),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
@@ -154,40 +156,32 @@ class _TcpScreenState extends State<TcpScreen> {
},
),
),
bottomNavigationBar: SafeArea(
top: false,
minimum: const EdgeInsets.fromLTRB(16, 8, 16, 16),
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (PlatformInfo.supportsUsbSerial)
FloatingActionButton.extended(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const UsbScreen()),
);
},
heroTag: 'tcp_usb_action',
extendedPadding: const EdgeInsets.symmetric(horizontal: 12),
icon: const Icon(Icons.usb),
label: Text(context.l10n.connectionChoiceUsbLabel),
),
if (PlatformInfo.supportsUsbSerial) const SizedBox(width: 12),
FloatingActionButton.extended(
onPressed: () {
Navigator.of(context).maybePop();
},
heroTag: 'tcp_ble_action',
extendedPadding: const EdgeInsets.symmetric(horizontal: 12),
icon: const Icon(Icons.bluetooth),
label: Text(context.l10n.connectionChoiceBluetoothLabel),
),
],
);
}
Widget _buildTransportLinks(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Wrap(
spacing: 12,
runSpacing: 8,
children: [
if (PlatformInfo.supportsUsbSerial)
OutlinedButton.icon(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const UsbScreen()),
);
},
icon: const Icon(Icons.usb),
label: Text(context.l10n.connectionChoiceUsbLabel),
),
OutlinedButton.icon(
onPressed: () => Navigator.of(context).maybePop(),
icon: const Icon(Icons.bluetooth),
label: Text(context.l10n.connectionChoiceBluetoothLabel),
),
),
],
),
);
}
@@ -214,7 +208,7 @@ class _TcpScreenState extends State<TcpScreen> {
statusColor = Colors.orange;
} else {
statusText = l10n.tcpStatus_notConnected;
statusColor = Colors.grey;
statusColor = Theme.of(context).colorScheme.onSurfaceVariant;
}
return Container(
@@ -226,15 +220,13 @@ class _TcpScreenState extends State<TcpScreen> {
Icon(Icons.circle, size: 12, color: statusColor),
const SizedBox(width: 8),
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text(
statusText,
style: TextStyle(
color: statusColor,
fontWeight: FontWeight.w500,
),
child: Text(
statusText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: statusColor,
fontWeight: FontWeight.w500,
),
),
),
@@ -274,7 +266,7 @@ class _TcpScreenState extends State<TcpScreen> {
showDismissibleSnackBar(
context,
content: Text(message),
backgroundColor: Colors.red,
backgroundColor: Theme.of(context).colorScheme.error,
);
}