From 372224a62c835a8f91b90ee7c7e851ef523de903 Mon Sep 17 00:00:00 2001 From: Janez T Date: Tue, 17 Mar 2026 10:07:02 +0100 Subject: [PATCH] feat: Share contact as meshcore:// URL via clipboard --- lib/providers/connection_provider.dart | 19 ++++++++++++++- lib/widgets/contacts/contact_tile.dart | 32 ++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/lib/providers/connection_provider.dart b/lib/providers/connection_provider.dart index 9a58c22..2a1c7bf 100644 --- a/lib/providers/connection_provider.dart +++ b/lib/providers/connection_provider.dart @@ -2201,7 +2201,24 @@ class ConnectionProvider with ChangeNotifier { } } - /// Request fresh device info (triggers SelfInfo response) + /// Export a contact as a meshcore:// share URL. + /// Pass null to export self. + Future exportContactUrl(Uint8List? publicKey) async { + if (!_activeService.isConnected) return null; + try { + final frame = await _activeService.exportContact(publicKey); + final hex = frame + .map((b) => b.toRadixString(16).padLeft(2, '0')) + .join(); + return 'meshcore://$hex'; + } catch (e) { + debugPrint('⚠️ [Provider] exportContact failed: $e'); + _error = 'Failed to export contact: $e'; + notifyListeners(); + return null; + } + } + /// Read custom variables from device (GPS mode, sensor settings, etc.) Future> getCustomVars() async { if (!_activeService.isConnected) { diff --git a/lib/widgets/contacts/contact_tile.dart b/lib/widgets/contacts/contact_tile.dart index 2d124b2..159297f 100644 --- a/lib/widgets/contacts/contact_tile.dart +++ b/lib/widgets/contacts/contact_tile.dart @@ -1,6 +1,5 @@ -import 'dart:typed_data'; - import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; @@ -371,6 +370,35 @@ class ContactTile extends StatelessWidget { } }, ), + if (!contact.isChannel) + ListTile( + leading: const Icon(Icons.share_outlined), + title: const Text('Share Contact'), + onTap: () async { + Navigator.pop(sheetContext); + final connectionProvider = + context.read(); + final url = await connectionProvider.exportContactUrl( + contact.publicKey, + ); + if (url != null && context.mounted) { + await Clipboard.setData(ClipboardData(text: url)); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Contact link copied to clipboard'), + ), + ); + } + } else if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Failed to export contact'), + ), + ); + } + }, + ), ListTile( leading: const Icon(Icons.message_outlined), title: Text(l10n.messages),