mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Allow overriding contact name
This commit is contained in:
@@ -318,104 +318,115 @@ class ContactTile extends StatelessWidget {
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (sheetContext) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.message_outlined),
|
||||
title: Text(l10n.messages),
|
||||
enabled: canMessage,
|
||||
onTap: !canMessage
|
||||
? null
|
||||
: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await _openMessagesForContact(context, contact);
|
||||
},
|
||||
),
|
||||
if (contact.displayLocation != null)
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.map_outlined),
|
||||
title: Text(l10n.viewOnMap),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showContactOnMap(context, contact);
|
||||
},
|
||||
),
|
||||
if (contact.type == ContactType.room && !contact.isPublicChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.login),
|
||||
title: Text(
|
||||
context
|
||||
.read<ConnectionProvider>()
|
||||
.getRoomLoginState(contact.publicKeyPrefix)
|
||||
?.isLoggedIn ==
|
||||
true
|
||||
? AppLocalizations.of(context)!.reLoginToRoom
|
||||
: AppLocalizations.of(context)!.loginToRoom,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showRoomLoginDialog(context, contact);
|
||||
},
|
||||
),
|
||||
if (canAddToSensors)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
isInSensors ? Icons.sensors : Icons.sensors_outlined,
|
||||
),
|
||||
title: Text(
|
||||
isInSensors
|
||||
? l10n.contactInSensors
|
||||
: l10n.contactAddToSensors,
|
||||
),
|
||||
enabled: !isInSensors,
|
||||
onTap: isInSensors
|
||||
leading: const Icon(Icons.message_outlined),
|
||||
title: Text(l10n.messages),
|
||||
enabled: canMessage,
|
||||
onTap: !canMessage
|
||||
? null
|
||||
: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await _addContactToSensors(context, contact);
|
||||
await _openMessagesForContact(context, contact);
|
||||
},
|
||||
),
|
||||
if (canSetPath)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.alt_route),
|
||||
title: Text(l10n.contactSetPath),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showSetRouteDialog(context, contact);
|
||||
},
|
||||
),
|
||||
if (!contact.isChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.route),
|
||||
title: const Text('Trace'),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showTraceSheet(context, contact);
|
||||
},
|
||||
),
|
||||
if (!contact.isPublicChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete, color: Colors.red),
|
||||
title: Text(
|
||||
contact.isChannel ? l10n.deleteChannel : l10n.deleteContact,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
if (contact.displayLocation != null)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.map_outlined),
|
||||
title: Text(l10n.viewOnMap),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showContactOnMap(context, contact);
|
||||
},
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
if (!context.mounted) return;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (contact.type == ContactType.room && !contact.isPublicChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.login),
|
||||
title: Text(
|
||||
context
|
||||
.read<ConnectionProvider>()
|
||||
.getRoomLoginState(contact.publicKeyPrefix)
|
||||
?.isLoggedIn ==
|
||||
true
|
||||
? AppLocalizations.of(context)!.reLoginToRoom
|
||||
: AppLocalizations.of(context)!.loginToRoom,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showRoomLoginDialog(context, contact);
|
||||
},
|
||||
),
|
||||
if (canAddToSensors)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
isInSensors ? Icons.sensors : Icons.sensors_outlined,
|
||||
),
|
||||
title: Text(
|
||||
isInSensors
|
||||
? l10n.contactInSensors
|
||||
: l10n.contactAddToSensors,
|
||||
),
|
||||
enabled: !isInSensors,
|
||||
onTap: isInSensors
|
||||
? null
|
||||
: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await _addContactToSensors(context, contact);
|
||||
},
|
||||
),
|
||||
if (canSetPath)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.alt_route),
|
||||
title: Text(l10n.contactSetPath),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showSetRouteDialog(context, contact);
|
||||
},
|
||||
),
|
||||
if (!contact.isChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.route),
|
||||
title: const Text('Trace'),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showTraceSheet(context, contact);
|
||||
},
|
||||
),
|
||||
if (!contact.isPublicChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.edit_outlined),
|
||||
title: const Text('Edit name'),
|
||||
onTap: () {
|
||||
Navigator.pop(sheetContext);
|
||||
_showNameOverrideDialog(context, contact);
|
||||
},
|
||||
),
|
||||
if (!contact.isPublicChannel)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete, color: Colors.red),
|
||||
title: Text(
|
||||
contact.isChannel ? l10n.deleteChannel : l10n.deleteContact,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
if (!context.mounted) return;
|
||||
if (contact.isChannel) {
|
||||
_showDeleteChannelDialog(context, contact);
|
||||
} else {
|
||||
_showDeleteConfirmation(context, contact);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!context.mounted) return;
|
||||
if (contact.isChannel) {
|
||||
_showDeleteChannelDialog(context, contact);
|
||||
} else {
|
||||
_showDeleteConfirmation(context, contact);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -555,6 +566,61 @@ class ContactTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showNameOverrideDialog(
|
||||
BuildContext context,
|
||||
Contact contact,
|
||||
) async {
|
||||
final controller = TextEditingController(text: contact.nameOverride ?? '');
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
final result = await showDialog<String?>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: const Text('Edit name'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
textInputAction: TextInputAction.done,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Custom name',
|
||||
hintText: contact.advName,
|
||||
helperText: 'Leave blank to use the advertised name.',
|
||||
),
|
||||
onSubmitted: (value) {
|
||||
Navigator.of(dialogContext).pop(value);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(controller.text),
|
||||
child: Text(l10n.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
controller.dispose();
|
||||
|
||||
if (result == null || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.read<ContactsProvider>().setContactNameOverride(
|
||||
contact.publicKeyHex,
|
||||
result,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showSetRouteDialog(
|
||||
BuildContext context,
|
||||
Contact contact,
|
||||
|
||||
@@ -12,6 +12,7 @@ class RecipientSelectorSheet extends StatefulWidget {
|
||||
final Map<String, int> unreadCountsByPublicKey;
|
||||
final String? currentDestinationType;
|
||||
final String? currentRecipientPublicKey;
|
||||
final bool showAllOption;
|
||||
final Function(String type, Contact? recipient) onSelect;
|
||||
|
||||
const RecipientSelectorSheet({
|
||||
@@ -23,6 +24,7 @@ class RecipientSelectorSheet extends StatefulWidget {
|
||||
required this.unreadCountsByPublicKey,
|
||||
this.currentDestinationType,
|
||||
this.currentRecipientPublicKey,
|
||||
this.showAllOption = true,
|
||||
required this.onSelect,
|
||||
});
|
||||
|
||||
@@ -61,6 +63,11 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
|
||||
final filteredContacts = _filterContacts(widget.contacts);
|
||||
final filteredRooms = _filterContacts(widget.rooms);
|
||||
final filteredChannels = _filterContacts(widget.channels);
|
||||
final showChannelsSection = widget.channels.isNotEmpty;
|
||||
final showContactsSection = widget.contacts.isNotEmpty;
|
||||
final showRoomsSection = widget.rooms.isNotEmpty;
|
||||
final showAnyRecipients =
|
||||
showChannelsSection || showContactsSection || showRoomsSection;
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
@@ -142,21 +149,23 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
_buildOptionTile(
|
||||
context: context,
|
||||
icon: Icons.all_inbox,
|
||||
title: l10n.showAll,
|
||||
subtitle: 'All messages',
|
||||
unreadCount: widget.unreadCount,
|
||||
isSelected: _isSelected('all', null),
|
||||
onTap: () {
|
||||
widget.onSelect('all', null);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
if (widget.showAllOption) ...[
|
||||
_buildOptionTile(
|
||||
context: context,
|
||||
icon: Icons.all_inbox,
|
||||
title: l10n.showAll,
|
||||
subtitle: 'All messages',
|
||||
unreadCount: widget.unreadCount,
|
||||
isSelected: _isSelected('all', null),
|
||||
onTap: () {
|
||||
widget.onSelect('all', null);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
if (showAnyRecipients) const Divider(),
|
||||
],
|
||||
// Channels section
|
||||
if (widget.channels.isNotEmpty) ...[
|
||||
if (showChannelsSection) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
@@ -204,10 +213,12 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
|
||||
}),
|
||||
],
|
||||
|
||||
const Divider(),
|
||||
if (showChannelsSection &&
|
||||
(showContactsSection || showRoomsSection))
|
||||
const Divider(),
|
||||
|
||||
// Contacts section
|
||||
if (widget.contacts.isNotEmpty) ...[
|
||||
if (showContactsSection) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
@@ -253,10 +264,10 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
|
||||
}),
|
||||
],
|
||||
|
||||
const Divider(),
|
||||
if (showContactsSection && showRoomsSection) const Divider(),
|
||||
|
||||
// Rooms section
|
||||
if (widget.rooms.isNotEmpty) ...[
|
||||
if (showRoomsSection) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
@@ -302,9 +313,7 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
|
||||
],
|
||||
|
||||
// Empty state
|
||||
if (widget.contacts.isEmpty &&
|
||||
widget.rooms.isEmpty &&
|
||||
widget.channels.isEmpty) ...[
|
||||
if (!showAnyRecipients) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
|
||||
Reference in New Issue
Block a user