mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-31 15:52:25 +10:00
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:
@@ -111,24 +111,26 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.logout, color: Colors.red),
|
||||
Icon(
|
||||
Icons.logout,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(context.l10n.common_disconnect),
|
||||
],
|
||||
),
|
||||
onTap: () => _disconnect(context),
|
||||
),
|
||||
if (_communities.isNotEmpty)
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.groups),
|
||||
const SizedBox(width: 8),
|
||||
Text(context.l10n.community_manageCommunities),
|
||||
],
|
||||
),
|
||||
onTap: () => _showManageCommunitiesDialog(context),
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.groups),
|
||||
const SizedBox(width: 8),
|
||||
Text(context.l10n.community_manageCommunities),
|
||||
],
|
||||
),
|
||||
onTap: () => _showManageCommunitiesDialog(context),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -241,32 +243,22 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
),
|
||||
Expanded(
|
||||
child: filteredChannels.isEmpty
|
||||
? ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height - 300,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.search_off,
|
||||
size: 64,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
? LayoutBuilder(
|
||||
builder: (context, constraints) => ListView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: EmptyState(
|
||||
icon: Icons.search_off,
|
||||
title:
|
||||
context.l10n.channels_noChannelsFound,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
: (viewState.channelsSortOption ==
|
||||
ChannelSortOption.manual &&
|
||||
@@ -357,6 +349,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
int? dragIndex,
|
||||
}) {
|
||||
final unreadCount = connector.getUnreadCountForChannel(channel);
|
||||
final isMuted = context.watch<AppSettingsService>().isChannelMuted(
|
||||
channel.name,
|
||||
);
|
||||
|
||||
// Determine icon and colors based on channel type
|
||||
IconData icon;
|
||||
@@ -449,37 +444,45 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isMuted) ...[
|
||||
Icon(
|
||||
Icons.notifications_off,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (unreadCount > 0) ...[
|
||||
UnreadBadge(count: unreadCount),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (showDragHandle && dragIndex != null)
|
||||
ReorderableDelayedDragStartListener(
|
||||
ReorderableDragStartListener(
|
||||
index: dragIndex,
|
||||
child: Icon(
|
||||
Icons.drag_handle,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Icon(
|
||||
Icons.drag_handle,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () async {
|
||||
onTap: () {
|
||||
final unread = connector.getUnreadCountForChannelIndex(
|
||||
channel.index,
|
||||
);
|
||||
connector.markChannelRead(channel.index);
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
if (context.mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChannelChatScreen(
|
||||
channel: channel,
|
||||
initialUnreadCount: unread,
|
||||
),
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChannelChatScreen(
|
||||
channel: channel,
|
||||
initialUnreadCount: unread,
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
},
|
||||
onLongPress: () => _showChannelActions(
|
||||
context,
|
||||
@@ -540,10 +543,15 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete_outline, color: Colors.red),
|
||||
leading: Icon(
|
||||
Icons.delete_outline,
|
||||
color: Theme.of(sheetContext).colorScheme.error,
|
||||
),
|
||||
title: Text(
|
||||
context.l10n.channels_deleteChannel,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
style: TextStyle(
|
||||
color: Theme.of(sheetContext).colorScheme.error,
|
||||
),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
@@ -725,23 +733,39 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
? (isSelected
|
||||
? Theme.of(dialogContext).colorScheme.primaryContainer
|
||||
: null)
|
||||
: Colors.grey.withValues(alpha: 0.2),
|
||||
: Theme.of(
|
||||
dialogContext,
|
||||
).colorScheme.onSurface.withValues(alpha: 0.12),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: enabled
|
||||
? (isSelected
|
||||
? Theme.of(dialogContext).colorScheme.primary
|
||||
: null)
|
||||
: Colors.grey,
|
||||
: Theme.of(
|
||||
dialogContext,
|
||||
).colorScheme.onSurface.withValues(alpha: 0.38),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(color: enabled ? null : Colors.grey),
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? null
|
||||
: Theme.of(
|
||||
dialogContext,
|
||||
).colorScheme.onSurface.withValues(alpha: 0.38),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
subtitle,
|
||||
style: TextStyle(color: enabled ? null : Colors.grey),
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? null
|
||||
: Theme.of(
|
||||
dialogContext,
|
||||
).colorScheme.onSurface.withValues(alpha: 0.38),
|
||||
),
|
||||
),
|
||||
trailing: enabled ? const Icon(Icons.chevron_right) : null,
|
||||
selected: isSelected,
|
||||
@@ -929,7 +953,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
Channel.publicChannelPsk,
|
||||
);
|
||||
Navigator.pop(dialogContext);
|
||||
connector.setChannel(nextIndex, 'Public', psk);
|
||||
connector.setChannel(nextIndex, context.l10n.channels_public, psk);
|
||||
if (context.mounted) {
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
@@ -1043,7 +1067,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
dialogContext.l10n.community_hashtagPrivacyHint,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
color: Theme.of(
|
||||
dialogContext,
|
||||
).colorScheme.onSurfaceVariant,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
@@ -1214,6 +1240,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
final name = nameController.text.trim();
|
||||
final publicLabel = context.l10n.channels_public;
|
||||
if (name.isEmpty) {
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
@@ -1238,7 +1265,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
final psk = community
|
||||
.deriveCommunityPublicPsk();
|
||||
final channelName =
|
||||
'${community.name} Public';
|
||||
'${community.name} $publicLabel';
|
||||
connector.setChannel(
|
||||
nextIndex,
|
||||
channelName,
|
||||
@@ -1594,7 +1621,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
},
|
||||
child: Text(
|
||||
dialogContext.l10n.common_delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1604,7 +1631,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
|
||||
void _addPublicChannel(BuildContext context, MeshCoreConnector connector) {
|
||||
final psk = Channel.parsePskHex(Channel.publicChannelPsk);
|
||||
connector.setChannel(0, 'Public', psk);
|
||||
connector.setChannel(0, context.l10n.channels_public, psk);
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(context.l10n.channels_publicChannelAdded),
|
||||
@@ -1653,14 +1680,19 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
Icon(
|
||||
Icons.groups_outlined,
|
||||
size: 64,
|
||||
color: Colors.grey[400],
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant
|
||||
.withValues(alpha: 0.6),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.l10n.community_noCommunities,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[600],
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -1668,7 +1700,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
context.l10n.community_scanOrCreate,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[500],
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant
|
||||
.withValues(alpha: 0.8),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -1692,10 +1727,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
),
|
||||
title: Text(community.name),
|
||||
subtitle: Text(
|
||||
'ID: ${community.shortCommunityId}...',
|
||||
context.l10n.channels_communityShortId(community.shortCommunityId),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
trailing: PopupMenuButton<String>(
|
||||
@@ -1722,14 +1759,20 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
value: 'leave',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icon(
|
||||
Icons.exit_to_app,
|
||||
color: Colors.red,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.error,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
context.l10n.community_delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.error,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -1830,7 +1873,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
},
|
||||
child: Text(
|
||||
dialogContext.l10n.community_delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user