format files

This commit is contained in:
zjs81
2026-06-13 00:39:13 -07:00
parent 7da4e68384
commit becfbedc99
26 changed files with 1040 additions and 741 deletions
+139 -168
View File
@@ -30,87 +30,103 @@ class AppSettingsScreen extends StatelessWidget {
),
body: SafeArea(
top: false,
child: Consumer3<AppSettingsService, MeshCoreConnector,
TranslationService>(
builder: (
context,
settingsService,
connector,
translationService,
child,
) {
return ListView(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 24),
children: [
// APPEARANCE
SectionHeader(context.l10n.appSettings_appearance),
MeshCard(
padding: EdgeInsets.zero,
child: _buildAppearanceContent(context, settingsService),
),
// NOTIFICATIONS
SectionHeader(context.l10n.appSettings_notifications),
MeshCard(
padding: EdgeInsets.zero,
child: _buildNotificationsContent(context, settingsService),
),
// MESSAGING
SectionHeader(context.l10n.appSettings_messaging),
MeshCard(
padding: EdgeInsets.zero,
child: _buildMessagingContent(context, settingsService),
),
// BATTERY
SectionHeader(context.l10n.appSettings_battery),
MeshCard(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: _buildBatteryContent(
child:
Consumer3<
AppSettingsService,
MeshCoreConnector,
TranslationService
>(
builder:
(
context,
settingsService,
connector,
),
),
translationService,
child,
) {
return ListView(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 24),
children: [
// APPEARANCE
SectionHeader(context.l10n.appSettings_appearance),
MeshCard(
padding: EdgeInsets.zero,
child: _buildAppearanceContent(
context,
settingsService,
),
),
// MAP
SectionHeader(context.l10n.appSettings_mapDisplay),
MeshCard(
padding: EdgeInsets.zero,
child: _buildMapContent(context, settingsService),
),
// NOTIFICATIONS
SectionHeader(context.l10n.appSettings_notifications),
MeshCard(
padding: EdgeInsets.zero,
child: _buildNotificationsContent(
context,
settingsService,
),
),
// TRANSLATION (non-web only)
if (!kIsWeb) ...[
SectionHeader(context.l10n.translation_title),
MeshCard(
padding: EdgeInsets.zero,
child: _buildTranslationContent(
context,
settingsService,
translationService,
),
),
],
// MESSAGING
SectionHeader(context.l10n.appSettings_messaging),
MeshCard(
padding: EdgeInsets.zero,
child: _buildMessagingContent(
context,
settingsService,
),
),
// CYR2LAT
SectionHeader(context.l10n.channels_cyr2latSettingsHeading),
MeshCard(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: _buildCyr2LatContent(context, settingsService),
),
// BATTERY
SectionHeader(context.l10n.appSettings_battery),
MeshCard(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: _buildBatteryContent(
context,
settingsService,
connector,
),
),
// DEBUG
SectionHeader(context.l10n.appSettings_debugCard),
MeshCard(
padding: EdgeInsets.zero,
child: _buildDebugContent(context, settingsService),
),
],
);
},
),
// MAP
SectionHeader(context.l10n.appSettings_mapDisplay),
MeshCard(
padding: EdgeInsets.zero,
child: _buildMapContent(context, settingsService),
),
// TRANSLATION (non-web only)
if (!kIsWeb) ...[
SectionHeader(context.l10n.translation_title),
MeshCard(
padding: EdgeInsets.zero,
child: _buildTranslationContent(
context,
settingsService,
translationService,
),
),
],
// CYR2LAT
SectionHeader(
context.l10n.channels_cyr2latSettingsHeading,
),
MeshCard(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: _buildCyr2LatContent(context, settingsService),
),
// DEBUG
SectionHeader(context.l10n.appSettings_debugCard),
MeshCard(
padding: EdgeInsets.zero,
child: _buildDebugContent(context, settingsService),
),
],
);
},
),
),
);
}
@@ -317,8 +333,7 @@ class AppSettingsScreen extends StatelessWidget {
),
value: settingsService.settings.notifyOnNewChannelMessage,
onChanged: notifEnabled
? (value) =>
settingsService.setNotifyOnNewChannelMessage(value)
? (value) => settingsService.setNotifyOnNewChannelMessage(value)
: null,
),
const Divider(height: 1, indent: 16),
@@ -367,9 +382,7 @@ class AppSettingsScreen extends StatelessWidget {
),
secondary: const Icon(Icons.refresh_outlined, size: 20),
title: Text(context.l10n.appSettings_clearPathOnMaxRetry),
subtitle: Text(
context.l10n.appSettings_clearPathOnMaxRetrySubtitle,
),
subtitle: Text(context.l10n.appSettings_clearPathOnMaxRetrySubtitle),
value: settingsService.settings.clearPathOnMaxRetry,
onChanged: (value) {
settingsService.setClearPathOnMaxRetry(value);
@@ -472,9 +485,7 @@ class AppSettingsScreen extends StatelessWidget {
min: 0.5,
max: 5.0,
divisions: 9,
label: settingsService
.settings
.initialRouteWeight
label: settingsService.settings.initialRouteWeight
.toStringAsFixed(1),
onChanged: (value) =>
settingsService.setInitialRouteWeight(value),
@@ -485,9 +496,7 @@ class AppSettingsScreen extends StatelessWidget {
const Divider(height: 1),
ListTile(
title: Text(
context
.l10n
.appSettings_routeWeightSuccessIncrement,
context.l10n.appSettings_routeWeightSuccessIncrement,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -517,9 +526,7 @@ class AppSettingsScreen extends StatelessWidget {
const Divider(height: 1),
ListTile(
title: Text(
context
.l10n
.appSettings_routeWeightFailureDecrement,
context.l10n.appSettings_routeWeightFailureDecrement,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -548,9 +555,7 @@ class AppSettingsScreen extends StatelessWidget {
),
const Divider(height: 1),
ListTile(
title: Text(
context.l10n.appSettings_maxMessageRetries,
),
title: Text(context.l10n.appSettings_maxMessageRetries),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -560,16 +565,12 @@ class AppSettingsScreen extends StatelessWidget {
.appSettings_maxMessageRetriesSubtitle,
),
Slider(
value: settingsService
.settings
.maxMessageRetries
value: settingsService.settings.maxMessageRetries
.toDouble(),
min: 2,
max: 10,
divisions: 8,
label: settingsService
.settings
.maxMessageRetries
label: settingsService.settings.maxMessageRetries
.toString(),
onChanged: (value) => settingsService
.setMaxMessageRetries(value.toInt()),
@@ -590,9 +591,7 @@ class AppSettingsScreen extends StatelessWidget {
),
secondary: const Icon(Icons.location_searching, size: 20),
title: Text(context.l10n.appSettings_enableMessageTracing),
subtitle: Text(
context.l10n.appSettings_enableMessageTracingSubtitle,
),
subtitle: Text(context.l10n.appSettings_enableMessageTracingSubtitle),
value: settingsService.settings.enableMessageTracing,
onChanged: (value) {
settingsService.setEnableMessageTracing(value);
@@ -765,9 +764,7 @@ class AppSettingsScreen extends StatelessWidget {
settingsService.settings.mapTimeFilterHours == 0
? context.l10n.appSettings_timeFilterShowAll
: context.l10n.appSettings_timeFilterShowLast(
settingsService
.settings
.mapTimeFilterHours
settingsService.settings.mapTimeFilterHours
.toInt(),
),
style: textTheme.bodySmall?.copyWith(
@@ -925,15 +922,17 @@ class AppSettingsScreen extends StatelessWidget {
title: Text(
context.l10n.translation_autoIncomingTitle,
style: TextStyle(
color:
translationEnabled ? null : Theme.of(context).disabledColor,
color: translationEnabled
? null
: Theme.of(context).disabledColor,
),
),
subtitle: Text(
context.l10n.translation_autoIncomingSubtitle,
style: TextStyle(
color:
translationEnabled ? null : Theme.of(context).disabledColor,
color: translationEnabled
? null
: Theme.of(context).disabledColor,
),
),
value: settings.autoTranslateIncomingMessages,
@@ -955,15 +954,17 @@ class AppSettingsScreen extends StatelessWidget {
title: Text(
context.l10n.translation_composerTitle,
style: TextStyle(
color:
translationEnabled ? null : Theme.of(context).disabledColor,
color: translationEnabled
? null
: Theme.of(context).disabledColor,
),
),
subtitle: Text(
context.l10n.translation_composerSubtitle,
style: TextStyle(
color:
translationEnabled ? null : Theme.of(context).disabledColor,
color: translationEnabled
? null
: Theme.of(context).disabledColor,
),
),
value: settings.composerTranslationEnabled,
@@ -973,17 +974,12 @@ class AppSettingsScreen extends StatelessWidget {
),
const Divider(height: 1, indent: 16),
InkWell(
onTap: () =>
_showTranslationLanguageDialog(context, settingsService),
onTap: () => _showTranslationLanguageDialog(context, settingsService),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Icon(
Icons.language,
size: 20,
color: scheme.onSurfaceVariant,
),
Icon(Icons.language, size: 20, color: scheme.onSurfaceVariant),
const SizedBox(width: 12),
Expanded(
child: Column(
@@ -1106,7 +1102,8 @@ class AppSettingsScreen extends StatelessWidget {
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: LinearProgressIndicator(
value: translationService.downloadFileName ==
value:
translationService.downloadFileName ==
'Merging chunks...'
? null
: translationService.downloadProgress,
@@ -1151,8 +1148,7 @@ class AppSettingsScreen extends StatelessWidget {
const SizedBox(width: 12),
Expanded(
child: InkWell(
borderRadius:
BorderRadius.circular(MeshRadii.xs),
borderRadius: BorderRadius.circular(MeshRadii.xs),
onTap: () => settingsService
.setTranslationSelectedModelId(model.id),
child: Column(
@@ -1194,9 +1190,7 @@ class AppSettingsScreen extends StatelessWidget {
const SizedBox(height: 8),
Text(
translationService.lastError!,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
],
],
@@ -1373,8 +1367,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageEn,
value: 'en',
selected:
settingsService.settings.languageOverride == 'en',
selected: settingsService.settings.languageOverride == 'en',
onTap: () {
settingsService.setLanguageOverride('en');
Navigator.pop(ctx);
@@ -1384,8 +1377,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageFr,
value: 'fr',
selected:
settingsService.settings.languageOverride == 'fr',
selected: settingsService.settings.languageOverride == 'fr',
onTap: () {
settingsService.setLanguageOverride('fr');
Navigator.pop(ctx);
@@ -1395,8 +1387,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageEs,
value: 'es',
selected:
settingsService.settings.languageOverride == 'es',
selected: settingsService.settings.languageOverride == 'es',
onTap: () {
settingsService.setLanguageOverride('es');
Navigator.pop(ctx);
@@ -1406,8 +1397,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageDe,
value: 'de',
selected:
settingsService.settings.languageOverride == 'de',
selected: settingsService.settings.languageOverride == 'de',
onTap: () {
settingsService.setLanguageOverride('de');
Navigator.pop(ctx);
@@ -1417,8 +1407,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languagePl,
value: 'pl',
selected:
settingsService.settings.languageOverride == 'pl',
selected: settingsService.settings.languageOverride == 'pl',
onTap: () {
settingsService.setLanguageOverride('pl');
Navigator.pop(ctx);
@@ -1428,8 +1417,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageSl,
value: 'sl',
selected:
settingsService.settings.languageOverride == 'sl',
selected: settingsService.settings.languageOverride == 'sl',
onTap: () {
settingsService.setLanguageOverride('sl');
Navigator.pop(ctx);
@@ -1439,8 +1427,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languagePt,
value: 'pt',
selected:
settingsService.settings.languageOverride == 'pt',
selected: settingsService.settings.languageOverride == 'pt',
onTap: () {
settingsService.setLanguageOverride('pt');
Navigator.pop(ctx);
@@ -1450,8 +1437,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageIt,
value: 'it',
selected:
settingsService.settings.languageOverride == 'it',
selected: settingsService.settings.languageOverride == 'it',
onTap: () {
settingsService.setLanguageOverride('it');
Navigator.pop(ctx);
@@ -1461,8 +1447,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageZh,
value: 'zh',
selected:
settingsService.settings.languageOverride == 'zh',
selected: settingsService.settings.languageOverride == 'zh',
onTap: () {
settingsService.setLanguageOverride('zh');
Navigator.pop(ctx);
@@ -1472,8 +1457,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageSv,
value: 'sv',
selected:
settingsService.settings.languageOverride == 'sv',
selected: settingsService.settings.languageOverride == 'sv',
onTap: () {
settingsService.setLanguageOverride('sv');
Navigator.pop(ctx);
@@ -1483,8 +1467,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageNl,
value: 'nl',
selected:
settingsService.settings.languageOverride == 'nl',
selected: settingsService.settings.languageOverride == 'nl',
onTap: () {
settingsService.setLanguageOverride('nl');
Navigator.pop(ctx);
@@ -1494,8 +1477,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageSk,
value: 'sk',
selected:
settingsService.settings.languageOverride == 'sk',
selected: settingsService.settings.languageOverride == 'sk',
onTap: () {
settingsService.setLanguageOverride('sk');
Navigator.pop(ctx);
@@ -1505,8 +1487,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageBg,
value: 'bg',
selected:
settingsService.settings.languageOverride == 'bg',
selected: settingsService.settings.languageOverride == 'bg',
onTap: () {
settingsService.setLanguageOverride('bg');
Navigator.pop(ctx);
@@ -1516,8 +1497,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageRu,
value: 'ru',
selected:
settingsService.settings.languageOverride == 'ru',
selected: settingsService.settings.languageOverride == 'ru',
onTap: () {
settingsService.setLanguageOverride('ru');
Navigator.pop(ctx);
@@ -1527,8 +1507,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageUk,
value: 'uk',
selected:
settingsService.settings.languageOverride == 'uk',
selected: settingsService.settings.languageOverride == 'uk',
onTap: () {
settingsService.setLanguageOverride('uk');
Navigator.pop(ctx);
@@ -1538,8 +1517,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageHu,
value: 'hu',
selected:
settingsService.settings.languageOverride == 'hu',
selected: settingsService.settings.languageOverride == 'hu',
onTap: () {
settingsService.setLanguageOverride('hu');
Navigator.pop(ctx);
@@ -1549,8 +1527,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageJa,
value: 'ja',
selected:
settingsService.settings.languageOverride == 'ja',
selected: settingsService.settings.languageOverride == 'ja',
onTap: () {
settingsService.setLanguageOverride('ja');
Navigator.pop(ctx);
@@ -1560,8 +1537,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_languageKo,
value: 'ko',
selected:
settingsService.settings.languageOverride == 'ko',
selected: settingsService.settings.languageOverride == 'ko',
onTap: () {
settingsService.setLanguageOverride('ko');
Navigator.pop(ctx);
@@ -1660,8 +1636,7 @@ class AppSettingsScreen extends StatelessWidget {
ctx,
label: context.l10n.appSettings_unitsMetric,
value: UnitSystem.metric,
selected:
settingsService.settings.unitSystem == UnitSystem.metric,
selected: settingsService.settings.unitSystem == UnitSystem.metric,
onTap: () {
settingsService.setUnitSystem(UnitSystem.metric);
Navigator.pop(ctx);
@@ -1874,9 +1849,7 @@ class AppSettingsScreen extends StatelessWidget {
if (nameController.text.isEmpty) {
showDismissibleSnackBar(
context,
content: Text(
context.l10n.settings_cyr2latProfileNameEmpty,
),
content: Text(context.l10n.settings_cyr2latProfileNameEmpty),
);
return;
}
@@ -1964,9 +1937,7 @@ class AppSettingsScreen extends StatelessWidget {
if (nameController.text.isEmpty) {
showDismissibleSnackBar(
context,
content: Text(
context.l10n.settings_cyr2latProfileNameEmpty,
),
content: Text(context.l10n.settings_cyr2latProfileNameEmpty),
);
return;
}
+59 -45
View File
@@ -462,7 +462,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
color: MeshPalette.magenta,
shape: BoxShape.circle,
border: Border.all(
color: Theme.of(context).colorScheme.surfaceContainerLow,
color: Theme.of(
context,
).colorScheme.surfaceContainerLow,
width: 2,
),
),
@@ -488,9 +490,8 @@ class _ChannelsScreenState extends State<ChannelsScreen>
Expanded(
child: Text(
channelLabel,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(fontWeight: FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -544,8 +545,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
),
const SizedBox(width: 4),
],
if (unreadCount > 0)
UnreadBadge(count: unreadCount),
if (unreadCount > 0) UnreadBadge(count: unreadCount),
],
),
],
@@ -806,12 +806,8 @@ class _ChannelsScreenState extends State<ChannelsScreen>
return MeshCard(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
borderColor: isSelected && enabled
? MeshPalette.blueLine
: null,
color: isSelected && enabled
? MeshPalette.blueBg
: null,
borderColor: isSelected && enabled ? MeshPalette.blueLine : null,
color: isSelected && enabled ? MeshPalette.blueBg : null,
onTap: enabled
? () {
setSheetState(() {
@@ -828,7 +824,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
name: title,
size: 38,
color: enabled
? (isSelected ? MeshPalette.blue : cardScheme.onSurfaceVariant)
? (isSelected
? MeshPalette.blue
: cardScheme.onSurfaceVariant)
: cardScheme.outline,
icon: icon,
),
@@ -840,17 +838,21 @@ class _ChannelsScreenState extends State<ChannelsScreen>
children: [
Text(
title,
style: Theme.of(sheetContext).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
color: enabled ? null : cardScheme.outline,
),
style: Theme.of(sheetContext).textTheme.bodyMedium
?.copyWith(
fontWeight: FontWeight.w500,
color: enabled ? null : cardScheme.outline,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: Theme.of(sheetContext).textTheme.bodySmall?.copyWith(
color: enabled ? cardScheme.onSurfaceVariant : cardScheme.outline,
),
style: Theme.of(sheetContext).textTheme.bodySmall
?.copyWith(
color: enabled
? cardScheme.onSurfaceVariant
: cardScheme.outline,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
@@ -860,7 +862,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
if (enabled)
Icon(
Icons.chevron_right,
color: isSelected ? MeshPalette.blue : cardScheme.onSurfaceVariant,
color: isSelected
? MeshPalette.blue
: cardScheme.onSurfaceVariant,
size: 20,
),
],
@@ -998,9 +1002,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
showDismissibleSnackBar(
context,
content: Text(
sheetContext
.l10n
.channels_pskMustBe32Hex,
sheetContext.l10n.channels_pskMustBe32Hex,
),
);
return;
@@ -1313,9 +1315,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
addPublicChannel = value ?? true;
});
},
title: Text(
sheetContext.l10n.community_addPublicChannel,
),
title: Text(sheetContext.l10n.community_addPublicChannel),
subtitle: Text(
sheetContext.l10n.community_addPublicChannelHint,
),
@@ -1418,9 +1418,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
maxChildSize: 0.95,
builder: (_, scrollController) => Column(
children: [
BottomSheetHeader(
title: sheetContext.l10n.channels_addChannel,
),
BottomSheetHeader(title: sheetContext.l10n.channels_addChannel),
Expanded(
child: ListView(
controller: scrollController,
@@ -1519,7 +1517,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
builder: (_, scrollController) => Column(
children: [
BottomSheetHeader(
title: sheetContext.l10n.channels_editChannelTitle(channel.index),
title: sheetContext.l10n.channels_editChannelTitle(
channel.index,
),
),
Expanded(
child: ListView(
@@ -1569,7 +1569,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: Text(sheetContext.l10n.channels_cyr2latCompression),
title: Text(
sheetContext.l10n.channels_cyr2latCompression,
),
subtitle: Text(
sheetContext.l10n.channels_cyr2latCompressionDscr,
),
@@ -1592,14 +1594,14 @@ class _ChannelsScreenState extends State<ChannelsScreen>
.channels_cyr2latSettingsSubheading,
border: const OutlineInputBorder(),
),
items: appSettingsService.settings.cyr2latProfiles.map((
profile,
) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
}).toList(),
items: appSettingsService.settings.cyr2latProfiles
.map((profile) {
return DropdownMenuItem(
value: profile.id,
child: Text(profile.name),
);
})
.toList(),
onChanged: (value) => setSheetState(() {
selectedCyr2LatProfileId = value;
}),
@@ -1633,14 +1635,20 @@ class _ChannelsScreenState extends State<ChannelsScreen>
} on FormatException {
showDismissibleSnackBar(
sheetContext,
content: Text(sheetContext.l10n.channels_pskMustBe32Hex),
content: Text(
sheetContext.l10n.channels_pskMustBe32Hex,
),
);
return;
}
Navigator.pop(sheetContext);
try {
await connector.setChannel(channel.index, name, psk);
await connector.setChannel(
channel.index,
name,
psk,
);
await connector.setChannelSmazEnabled(
channel.index,
smazEnabled,
@@ -1656,7 +1664,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
if (!context.mounted) return;
showDismissibleSnackBar(
context,
content: Text(context.l10n.channels_channelUpdated(name)),
content: Text(
context.l10n.channels_channelUpdated(name),
),
);
} catch (e, st) {
debugPrint(st.toString());
@@ -1732,7 +1742,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
},
child: Text(
dialogContext.l10n.common_delete,
style: TextStyle(color: Theme.of(dialogContext).colorScheme.error),
style: TextStyle(
color: Theme.of(dialogContext).colorScheme.error,
),
),
),
],
@@ -1975,7 +1987,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
},
child: Text(
dialogContext.l10n.community_delete,
style: TextStyle(color: Theme.of(dialogContext).colorScheme.error),
style: TextStyle(
color: Theme.of(dialogContext).colorScheme.error,
),
),
),
],
+130 -133
View File
@@ -109,10 +109,7 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
),
child: Text(
context.l10n.community_scanInstructions,
style: const TextStyle(
color: MeshPalette.ink2,
fontSize: 13,
),
style: const TextStyle(color: MeshPalette.ink2, fontSize: 13),
textAlign: TextAlign.center,
),
),
@@ -185,61 +182,61 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
builder: (sheetContext) {
final sheetScheme = Theme.of(sheetContext).colorScheme;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
BottomSheetHeader(title: context.l10n.community_alreadyMember),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 4),
child: Text(
context.l10n.community_alreadyMemberMessage(community.name),
style: TextStyle(color: sheetScheme.onSurfaceVariant),
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
BottomSheetHeader(title: context.l10n.community_alreadyMember),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 4),
child: Text(
context.l10n.community_alreadyMemberMessage(community.name),
style: TextStyle(color: sheetScheme.onSurfaceVariant),
),
),
),
MeshCard(
child: Row(
children: [
const Icon(
Icons.groups,
color: MeshPalette.magenta,
size: 32,
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
community.name,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
Text(
'ID: ${community.shortCommunityId}...',
style: MeshTheme.mono(
fontSize: 11.5,
color: sheetScheme.onSurfaceVariant,
),
),
],
MeshCard(
child: Row(
children: [
const Icon(
Icons.groups,
color: MeshPalette.magenta,
size: 32,
),
),
],
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
community.name,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
Text(
'ID: ${community.shortCommunityId}...',
style: MeshTheme.mono(
fontSize: 11.5,
color: sheetScheme.onSurfaceVariant,
),
),
],
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
child: FilledButton(
onPressed: () {
Navigator.pop(sheetContext);
Navigator.pop(context);
},
child: Text(context.l10n.common_ok),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
child: FilledButton(
onPressed: () {
Navigator.pop(sheetContext);
Navigator.pop(context);
},
child: Text(context.l10n.common_ok),
),
),
),
],
],
);
},
);
@@ -258,90 +255,90 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
builder: (sheetContext, setSheetState) {
final joinScheme = Theme.of(sheetContext).colorScheme;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
BottomSheetHeader(title: context.l10n.community_joinTitle),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 4),
child: Text(
context.l10n.community_joinConfirmation(community.name),
style: TextStyle(color: joinScheme.onSurfaceVariant),
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
BottomSheetHeader(title: context.l10n.community_joinTitle),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 4),
child: Text(
context.l10n.community_joinConfirmation(community.name),
style: TextStyle(color: joinScheme.onSurfaceVariant),
),
),
),
MeshCard(
child: Row(
children: [
AvatarCircle(
name: community.name,
icon: Icons.groups,
color: MeshPalette.magenta,
size: 44,
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
community.name,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
MeshCard(
child: Row(
children: [
AvatarCircle(
name: community.name,
icon: Icons.groups,
color: MeshPalette.magenta,
size: 44,
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
community.name,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
),
Text(
'ID: ${community.shortCommunityId}...',
style: MeshTheme.mono(
fontSize: 11.5,
color: joinScheme.onSurfaceVariant,
Text(
'ID: ${community.shortCommunityId}...',
style: MeshTheme.mono(
fontSize: 11.5,
color: joinScheme.onSurfaceVariant,
),
),
),
],
],
),
),
),
],
],
),
),
),
CheckboxListTile(
value: addPublicChannel,
onChanged: (value) {
setSheetState(() {
addPublicChannel = value ?? true;
});
},
title: Text(context.l10n.community_addPublicChannel),
subtitle: Text(context.l10n.community_addPublicChannelHint),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
completer.complete(false);
Navigator.pop(sheetContext);
},
child: Text(context.l10n.common_cancel),
),
),
const SizedBox(width: 12),
Expanded(
child: FilledButton(
onPressed: () {
completer.complete(true);
Navigator.pop(sheetContext);
},
child: Text(context.l10n.community_join),
),
),
],
CheckboxListTile(
value: addPublicChannel,
onChanged: (value) {
setSheetState(() {
addPublicChannel = value ?? true;
});
},
title: Text(context.l10n.community_addPublicChannel),
subtitle: Text(context.l10n.community_addPublicChannelHint),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
),
),
],
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
completer.complete(false);
Navigator.pop(sheetContext);
},
child: Text(context.l10n.common_cancel),
),
),
const SizedBox(width: 12),
Expanded(
child: FilledButton(
onPressed: () {
completer.complete(true);
Navigator.pop(sheetContext);
},
child: Text(context.l10n.community_join),
),
),
],
),
),
],
);
},
),
@@ -139,10 +139,7 @@ class _CompanionRadioStatsScreenState extends State<CompanionRadioStatsScreen> {
stats.lastSnrDb.toStringAsFixed(1),
),
Icons.signal_cellular_alt,
MeshTheme.snrColor(
stats.lastSnrDb,
blocked: false,
),
MeshTheme.snrColor(stats.lastSnrDb, blocked: false),
),
],
),
+5 -2
View File
@@ -1574,7 +1574,8 @@ class _ContactTile extends StatelessWidget {
case advTypeSensor:
return const Color(0xFF4ACCC4); // teal
default:
return MeshPalette.blue; // chat — AvatarCircle handles deterministic hue
return MeshPalette
.blue; // chat — AvatarCircle handles deterministic hue
}
}
@@ -1662,7 +1663,9 @@ class _ContactTile extends StatelessWidget {
Icon(
Icons.location_on,
size: 13,
color: scheme.onSurfaceVariant.withValues(alpha: 0.55),
color: scheme.onSurfaceVariant.withValues(
alpha: 0.55,
),
),
],
],
+1 -4
View File
@@ -351,10 +351,7 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
for (var i = 0; i < _parsedNeighbors!.length; i++)
ListEntrance(
index: i,
child: _buildNeighborRow(
_parsedNeighbors![i],
connector.currentSf,
),
child: _buildNeighborRow(_parsedNeighbors![i], connector.currentSf),
),
],
);
+5 -7
View File
@@ -801,9 +801,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen>
final selected = _selectedPath;
return selected != null ? [selected] : const [];
}
return _displayPaths
.where((p) => !_hiddenPathIds.contains(p.id))
.toList();
return _displayPaths.where((p) => !_hiddenPathIds.contains(p.id)).toList();
}
/// Updates the playback path, but only when the selected path's geometry
@@ -1249,7 +1247,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen>
final hex = hop.toRadixString(16).padLeft(2, '0').toUpperCase();
showSharedNodeSheet(
context,
title: '$hex: ${contact?.name ?? context.l10n.channelPath_unknownRepeater}',
title:
'$hex: ${contact?.name ?? context.l10n.channelPath_unknownRepeater}',
paths: paths,
onSelect: _selectPath,
);
@@ -1539,9 +1538,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen>
tooltip: _panelCollapsed
? l10n.pathMap_expandPanel
: l10n.pathMap_collapsePanel,
onPressed: () => setState(
() => _panelCollapsed = !_panelCollapsed,
),
onPressed: () =>
setState(() => _panelCollapsed = !_panelCollapsed),
),
],
),
+547 -152
View File
@@ -470,26 +470,16 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.terminal,
size: 48,
color: MeshPalette.ink4,
),
const Icon(Icons.terminal, size: 48, color: MeshPalette.ink4),
const SizedBox(height: 12),
Text(
l10n.repeater_noCommandsSent,
style: MeshTheme.mono(
fontSize: 13,
color: MeshPalette.ink3,
),
style: MeshTheme.mono(fontSize: 13, color: MeshPalette.ink3),
),
const SizedBox(height: 4),
Text(
l10n.repeater_typeCommandOrUseQuick,
style: const TextStyle(
fontSize: 12,
color: MeshPalette.ink4,
),
style: const TextStyle(fontSize: 12, color: MeshPalette.ink4),
),
],
),
@@ -518,9 +508,7 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
style: MeshTheme.mono(
fontSize: 12,
fontWeight: FontWeight.w700,
color: isCommand
? MeshPalette.blue
: MeshPalette.ink3,
color: isCommand ? MeshPalette.blue : MeshPalette.ink3,
),
),
),
@@ -530,9 +518,7 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
entry['text']!,
style: MeshTheme.mono(
fontSize: 12.5,
color: isCommand
? MeshPalette.blue
: MeshPalette.ink,
color: isCommand ? MeshPalette.blue : MeshPalette.ink,
),
),
),
@@ -559,156 +545,522 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
void _showCommandHelp(BuildContext context) {
final l10n = context.l10n;
final generalCommands = [
_CommandHelpEntry(command: 'advert', description: l10n.repeater_cliHelpAdvert),
_CommandHelpEntry(command: 'reboot', description: l10n.repeater_cliHelpReboot),
_CommandHelpEntry(command: 'clock', description: l10n.repeater_cliHelpClock),
_CommandHelpEntry(command: 'password {new-password}', description: l10n.repeater_cliHelpPassword),
_CommandHelpEntry(command: 'ver', description: l10n.repeater_cliHelpVersion),
_CommandHelpEntry(command: 'clear stats', description: l10n.repeater_cliHelpClearStats),
_CommandHelpEntry(command: 'poweroff', description: l10n.repeater_cliHelpPowerOff),
_CommandHelpEntry(command: 'shutdown', description: l10n.repeater_cliHelpPowerOff),
_CommandHelpEntry(command: 'clkreboot', description: l10n.repeater_cliHelpClkReboot),
_CommandHelpEntry(command: 'advert.zerohop', description: l10n.repeater_cliHelpAdvertZeroHop),
_CommandHelpEntry(command: 'start ota', description: l10n.repeater_cliHelpStartOta),
_CommandHelpEntry(command: 'time {epoch-seconds}', description: l10n.repeater_cliHelpTime),
_CommandHelpEntry(command: 'board', description: l10n.repeater_cliHelpBoard),
_CommandHelpEntry(command: 'discover.neighbors', description: l10n.repeater_cliHelpDiscoverNeighbors),
_CommandHelpEntry(command: 'powersaving', description: l10n.repeater_cliHelpPowersaving),
_CommandHelpEntry(command: 'powersaving {on|off}', description: l10n.repeater_cliHelpPowersavingOnOff),
_CommandHelpEntry(command: 'erase', description: l10n.repeater_cliHelpErase),
_CommandHelpEntry(command: 'stats-packets', description: l10n.repeater_cliHelpStatsPackets),
_CommandHelpEntry(command: 'stats-radio', description: l10n.repeater_cliHelpStatsRadio),
_CommandHelpEntry(command: 'stats-core', description: l10n.repeater_cliHelpStatsCore),
_CommandHelpEntry(
command: 'advert',
description: l10n.repeater_cliHelpAdvert,
),
_CommandHelpEntry(
command: 'reboot',
description: l10n.repeater_cliHelpReboot,
),
_CommandHelpEntry(
command: 'clock',
description: l10n.repeater_cliHelpClock,
),
_CommandHelpEntry(
command: 'password {new-password}',
description: l10n.repeater_cliHelpPassword,
),
_CommandHelpEntry(
command: 'ver',
description: l10n.repeater_cliHelpVersion,
),
_CommandHelpEntry(
command: 'clear stats',
description: l10n.repeater_cliHelpClearStats,
),
_CommandHelpEntry(
command: 'poweroff',
description: l10n.repeater_cliHelpPowerOff,
),
_CommandHelpEntry(
command: 'shutdown',
description: l10n.repeater_cliHelpPowerOff,
),
_CommandHelpEntry(
command: 'clkreboot',
description: l10n.repeater_cliHelpClkReboot,
),
_CommandHelpEntry(
command: 'advert.zerohop',
description: l10n.repeater_cliHelpAdvertZeroHop,
),
_CommandHelpEntry(
command: 'start ota',
description: l10n.repeater_cliHelpStartOta,
),
_CommandHelpEntry(
command: 'time {epoch-seconds}',
description: l10n.repeater_cliHelpTime,
),
_CommandHelpEntry(
command: 'board',
description: l10n.repeater_cliHelpBoard,
),
_CommandHelpEntry(
command: 'discover.neighbors',
description: l10n.repeater_cliHelpDiscoverNeighbors,
),
_CommandHelpEntry(
command: 'powersaving',
description: l10n.repeater_cliHelpPowersaving,
),
_CommandHelpEntry(
command: 'powersaving {on|off}',
description: l10n.repeater_cliHelpPowersavingOnOff,
),
_CommandHelpEntry(
command: 'erase',
description: l10n.repeater_cliHelpErase,
),
_CommandHelpEntry(
command: 'stats-packets',
description: l10n.repeater_cliHelpStatsPackets,
),
_CommandHelpEntry(
command: 'stats-radio',
description: l10n.repeater_cliHelpStatsRadio,
),
_CommandHelpEntry(
command: 'stats-core',
description: l10n.repeater_cliHelpStatsCore,
),
];
final settingsCommands = [
_CommandHelpEntry(command: 'set af {air-time-factor}', description: l10n.repeater_cliHelpSetAf),
_CommandHelpEntry(command: 'set tx {tx-power-dbm}', description: l10n.repeater_cliHelpSetTx),
_CommandHelpEntry(command: 'set repeat {on|off}', description: l10n.repeater_cliHelpSetRepeat),
_CommandHelpEntry(command: 'set allow.read.only {on|off}', description: l10n.repeater_cliHelpSetAllowReadOnly),
_CommandHelpEntry(command: 'set flood.max {max-hops}', description: l10n.repeater_cliHelpSetFloodMax),
_CommandHelpEntry(command: 'set int.thresh {db}', description: l10n.repeater_cliHelpSetIntThresh),
_CommandHelpEntry(command: 'set agc.reset.interval {seconds}', description: l10n.repeater_cliHelpSetAgcResetInterval),
_CommandHelpEntry(command: 'set multi.acks {0|1}', description: l10n.repeater_cliHelpSetMultiAcks),
_CommandHelpEntry(command: 'set advert.interval {minutes}', description: l10n.repeater_cliHelpSetAdvertInterval),
_CommandHelpEntry(command: 'set flood.advert.interval {hours}', description: l10n.repeater_cliHelpSetFloodAdvertInterval),
_CommandHelpEntry(command: 'set guest.password {guess-password}', description: l10n.repeater_cliHelpSetGuestPassword),
_CommandHelpEntry(command: 'set name {name}', description: l10n.repeater_cliHelpSetName),
_CommandHelpEntry(command: 'set lat {latitude}', description: l10n.repeater_cliHelpSetLat),
_CommandHelpEntry(command: 'set lon {longitude}', description: l10n.repeater_cliHelpSetLon),
_CommandHelpEntry(command: 'set radio {freq},{bw},{sf},{cr}', description: l10n.repeater_cliHelpSetRadio),
_CommandHelpEntry(command: 'set rxdelay {base}', description: l10n.repeater_cliHelpSetRxDelay),
_CommandHelpEntry(command: 'set txdelay {factor}', description: l10n.repeater_cliHelpSetTxDelay),
_CommandHelpEntry(command: 'set direct.txdelay {factor}', description: l10n.repeater_cliHelpSetDirectTxDelay),
_CommandHelpEntry(command: 'set bridge.enabled {on|off}', description: l10n.repeater_cliHelpSetBridgeEnabled),
_CommandHelpEntry(command: 'set bridge.delay {0-10000}', description: l10n.repeater_cliHelpSetBridgeDelay),
_CommandHelpEntry(command: 'set bridge.source {rx|tx}', description: l10n.repeater_cliHelpSetBridgeSource),
_CommandHelpEntry(command: 'set bridge.baud {speed}', description: l10n.repeater_cliHelpSetBridgeBaud),
_CommandHelpEntry(command: 'set bridge.secret {shared-secret}', description: l10n.repeater_cliHelpSetBridgeSecret),
_CommandHelpEntry(command: 'set adc.multiplier {factor}', description: l10n.repeater_cliHelpSetAdcMultiplier),
_CommandHelpEntry(command: 'tempradio {freq},{bw},{sf},{cr},{minutes}', description: l10n.repeater_cliHelpTempRadio),
_CommandHelpEntry(command: 'setperm {pubkey-hex} {permissions}', description: l10n.repeater_cliHelpSetPerm),
_CommandHelpEntry(command: 'set dutycycle {1-100}', description: l10n.repeater_cliHelpSetDutyCycle),
_CommandHelpEntry(command: 'set prv.key {hex}', description: l10n.repeater_cliHelpSetPrvKey),
_CommandHelpEntry(command: 'set radio.rxgain {on|off}', description: l10n.repeater_cliHelpSetRadioRxGain),
_CommandHelpEntry(command: 'set owner.info {text}', description: l10n.repeater_cliHelpSetOwnerInfo),
_CommandHelpEntry(command: 'set path.hash.mode {0|1|2}', description: l10n.repeater_cliHelpSetPathHashMode),
_CommandHelpEntry(command: 'set loop.detect {off|minimal|moderate|strict}', description: l10n.repeater_cliHelpSetLoopDetect),
_CommandHelpEntry(command: 'set freq {mhz}', description: l10n.repeater_cliHelpSetFreq),
_CommandHelpEntry(command: 'set bridge.channel {1-14}', description: l10n.repeater_cliHelpSetBridgeChannel),
_CommandHelpEntry(
command: 'set af {air-time-factor}',
description: l10n.repeater_cliHelpSetAf,
),
_CommandHelpEntry(
command: 'set tx {tx-power-dbm}',
description: l10n.repeater_cliHelpSetTx,
),
_CommandHelpEntry(
command: 'set repeat {on|off}',
description: l10n.repeater_cliHelpSetRepeat,
),
_CommandHelpEntry(
command: 'set allow.read.only {on|off}',
description: l10n.repeater_cliHelpSetAllowReadOnly,
),
_CommandHelpEntry(
command: 'set flood.max {max-hops}',
description: l10n.repeater_cliHelpSetFloodMax,
),
_CommandHelpEntry(
command: 'set int.thresh {db}',
description: l10n.repeater_cliHelpSetIntThresh,
),
_CommandHelpEntry(
command: 'set agc.reset.interval {seconds}',
description: l10n.repeater_cliHelpSetAgcResetInterval,
),
_CommandHelpEntry(
command: 'set multi.acks {0|1}',
description: l10n.repeater_cliHelpSetMultiAcks,
),
_CommandHelpEntry(
command: 'set advert.interval {minutes}',
description: l10n.repeater_cliHelpSetAdvertInterval,
),
_CommandHelpEntry(
command: 'set flood.advert.interval {hours}',
description: l10n.repeater_cliHelpSetFloodAdvertInterval,
),
_CommandHelpEntry(
command: 'set guest.password {guess-password}',
description: l10n.repeater_cliHelpSetGuestPassword,
),
_CommandHelpEntry(
command: 'set name {name}',
description: l10n.repeater_cliHelpSetName,
),
_CommandHelpEntry(
command: 'set lat {latitude}',
description: l10n.repeater_cliHelpSetLat,
),
_CommandHelpEntry(
command: 'set lon {longitude}',
description: l10n.repeater_cliHelpSetLon,
),
_CommandHelpEntry(
command: 'set radio {freq},{bw},{sf},{cr}',
description: l10n.repeater_cliHelpSetRadio,
),
_CommandHelpEntry(
command: 'set rxdelay {base}',
description: l10n.repeater_cliHelpSetRxDelay,
),
_CommandHelpEntry(
command: 'set txdelay {factor}',
description: l10n.repeater_cliHelpSetTxDelay,
),
_CommandHelpEntry(
command: 'set direct.txdelay {factor}',
description: l10n.repeater_cliHelpSetDirectTxDelay,
),
_CommandHelpEntry(
command: 'set bridge.enabled {on|off}',
description: l10n.repeater_cliHelpSetBridgeEnabled,
),
_CommandHelpEntry(
command: 'set bridge.delay {0-10000}',
description: l10n.repeater_cliHelpSetBridgeDelay,
),
_CommandHelpEntry(
command: 'set bridge.source {rx|tx}',
description: l10n.repeater_cliHelpSetBridgeSource,
),
_CommandHelpEntry(
command: 'set bridge.baud {speed}',
description: l10n.repeater_cliHelpSetBridgeBaud,
),
_CommandHelpEntry(
command: 'set bridge.secret {shared-secret}',
description: l10n.repeater_cliHelpSetBridgeSecret,
),
_CommandHelpEntry(
command: 'set adc.multiplier {factor}',
description: l10n.repeater_cliHelpSetAdcMultiplier,
),
_CommandHelpEntry(
command: 'tempradio {freq},{bw},{sf},{cr},{minutes}',
description: l10n.repeater_cliHelpTempRadio,
),
_CommandHelpEntry(
command: 'setperm {pubkey-hex} {permissions}',
description: l10n.repeater_cliHelpSetPerm,
),
_CommandHelpEntry(
command: 'set dutycycle {1-100}',
description: l10n.repeater_cliHelpSetDutyCycle,
),
_CommandHelpEntry(
command: 'set prv.key {hex}',
description: l10n.repeater_cliHelpSetPrvKey,
),
_CommandHelpEntry(
command: 'set radio.rxgain {on|off}',
description: l10n.repeater_cliHelpSetRadioRxGain,
),
_CommandHelpEntry(
command: 'set owner.info {text}',
description: l10n.repeater_cliHelpSetOwnerInfo,
),
_CommandHelpEntry(
command: 'set path.hash.mode {0|1|2}',
description: l10n.repeater_cliHelpSetPathHashMode,
),
_CommandHelpEntry(
command: 'set loop.detect {off|minimal|moderate|strict}',
description: l10n.repeater_cliHelpSetLoopDetect,
),
_CommandHelpEntry(
command: 'set freq {mhz}',
description: l10n.repeater_cliHelpSetFreq,
),
_CommandHelpEntry(
command: 'set bridge.channel {1-14}',
description: l10n.repeater_cliHelpSetBridgeChannel,
),
];
final bridgeCommands = [
_CommandHelpEntry(command: 'get bridge.type', description: l10n.repeater_cliHelpGetBridgeType),
_CommandHelpEntry(
command: 'get bridge.type',
description: l10n.repeater_cliHelpGetBridgeType,
),
];
final loggingCommands = [
_CommandHelpEntry(command: 'log start', description: l10n.repeater_cliHelpLogStart),
_CommandHelpEntry(command: 'log stop', description: l10n.repeater_cliHelpLogStop),
_CommandHelpEntry(command: 'log erase', description: l10n.repeater_cliHelpLogErase),
_CommandHelpEntry(
command: 'log start',
description: l10n.repeater_cliHelpLogStart,
),
_CommandHelpEntry(
command: 'log stop',
description: l10n.repeater_cliHelpLogStop,
),
_CommandHelpEntry(
command: 'log erase',
description: l10n.repeater_cliHelpLogErase,
),
];
final neighborCommands = [
_CommandHelpEntry(command: 'neighbors', description: l10n.repeater_cliHelpNeighbors),
_CommandHelpEntry(command: 'neighbor.remove {pubkey-prefix}', description: l10n.repeater_cliHelpNeighborRemove),
_CommandHelpEntry(
command: 'neighbors',
description: l10n.repeater_cliHelpNeighbors,
),
_CommandHelpEntry(
command: 'neighbor.remove {pubkey-prefix}',
description: l10n.repeater_cliHelpNeighborRemove,
),
];
final regionCommands = [
_CommandHelpEntry(command: 'region', description: l10n.repeater_cliHelpRegion),
_CommandHelpEntry(command: 'region load', description: l10n.repeater_cliHelpRegionLoad),
_CommandHelpEntry(command: 'region get {* | name-prefix}', description: l10n.repeater_cliHelpRegionGet),
_CommandHelpEntry(command: 'region put {name} {* | parent-name-prefix}', description: l10n.repeater_cliHelpRegionPut),
_CommandHelpEntry(command: 'region remove {name}', description: l10n.repeater_cliHelpRegionRemove),
_CommandHelpEntry(command: 'region allowf {* | name-prefix}', description: l10n.repeater_cliHelpRegionAllowf),
_CommandHelpEntry(command: 'region denyf {* | name-prefix}', description: l10n.repeater_cliHelpRegionDenyf),
_CommandHelpEntry(command: 'region home', description: l10n.repeater_cliHelpRegionHome),
_CommandHelpEntry(command: 'region home {* | name-prefix}', description: l10n.repeater_cliHelpRegionHomeSet),
_CommandHelpEntry(command: 'region save', description: l10n.repeater_cliHelpRegionSave),
_CommandHelpEntry(command: 'region default', description: l10n.repeater_cliHelpRegionDefault),
_CommandHelpEntry(command: 'region default {* | name-prefix | <null>}', description: l10n.repeater_cliHelpRegionDefaultSet),
_CommandHelpEntry(command: 'region list allowed', description: l10n.repeater_cliHelpRegionListAllowed),
_CommandHelpEntry(command: 'region list denied', description: l10n.repeater_cliHelpRegionListDenied),
_CommandHelpEntry(
command: 'region',
description: l10n.repeater_cliHelpRegion,
),
_CommandHelpEntry(
command: 'region load',
description: l10n.repeater_cliHelpRegionLoad,
),
_CommandHelpEntry(
command: 'region get {* | name-prefix}',
description: l10n.repeater_cliHelpRegionGet,
),
_CommandHelpEntry(
command: 'region put {name} {* | parent-name-prefix}',
description: l10n.repeater_cliHelpRegionPut,
),
_CommandHelpEntry(
command: 'region remove {name}',
description: l10n.repeater_cliHelpRegionRemove,
),
_CommandHelpEntry(
command: 'region allowf {* | name-prefix}',
description: l10n.repeater_cliHelpRegionAllowf,
),
_CommandHelpEntry(
command: 'region denyf {* | name-prefix}',
description: l10n.repeater_cliHelpRegionDenyf,
),
_CommandHelpEntry(
command: 'region home',
description: l10n.repeater_cliHelpRegionHome,
),
_CommandHelpEntry(
command: 'region home {* | name-prefix}',
description: l10n.repeater_cliHelpRegionHomeSet,
),
_CommandHelpEntry(
command: 'region save',
description: l10n.repeater_cliHelpRegionSave,
),
_CommandHelpEntry(
command: 'region default',
description: l10n.repeater_cliHelpRegionDefault,
),
_CommandHelpEntry(
command: 'region default {* | name-prefix | <null>}',
description: l10n.repeater_cliHelpRegionDefaultSet,
),
_CommandHelpEntry(
command: 'region list allowed',
description: l10n.repeater_cliHelpRegionListAllowed,
),
_CommandHelpEntry(
command: 'region list denied',
description: l10n.repeater_cliHelpRegionListDenied,
),
];
final getCommands = [
_CommandHelpEntry(command: 'get name', description: l10n.repeater_cliHelpGetName),
_CommandHelpEntry(command: 'get role', description: l10n.repeater_cliHelpGetRole),
_CommandHelpEntry(command: 'get public.key', description: l10n.repeater_cliHelpGetPublicKey),
_CommandHelpEntry(command: 'get prv.key', description: l10n.repeater_cliHelpGetPrvKey),
_CommandHelpEntry(command: 'get repeat', description: l10n.repeater_cliHelpGetRepeat),
_CommandHelpEntry(command: 'get tx', description: l10n.repeater_cliHelpGetTx),
_CommandHelpEntry(command: 'get freq', description: l10n.repeater_cliHelpGetFreq),
_CommandHelpEntry(command: 'get radio', description: l10n.repeater_cliHelpGetRadio),
_CommandHelpEntry(command: 'get radio.rxgain', description: l10n.repeater_cliHelpGetRadioRxGain),
_CommandHelpEntry(command: 'get af', description: l10n.repeater_cliHelpGetAf),
_CommandHelpEntry(command: 'get dutycycle', description: l10n.repeater_cliHelpGetDutyCycle),
_CommandHelpEntry(command: 'get int.thresh', description: l10n.repeater_cliHelpGetIntThresh),
_CommandHelpEntry(command: 'get agc.reset.interval', description: l10n.repeater_cliHelpGetAgcResetInterval),
_CommandHelpEntry(command: 'get multi.acks', description: l10n.repeater_cliHelpGetMultiAcks),
_CommandHelpEntry(command: 'get allow.read.only', description: l10n.repeater_cliHelpGetAllowReadOnly),
_CommandHelpEntry(command: 'get advert.interval', description: l10n.repeater_cliHelpGetAdvertInterval),
_CommandHelpEntry(command: 'get flood.advert.interval', description: l10n.repeater_cliHelpGetFloodAdvertInterval),
_CommandHelpEntry(command: 'get guest.password', description: l10n.repeater_cliHelpGetGuestPassword),
_CommandHelpEntry(command: 'get lat', description: l10n.repeater_cliHelpGetLat),
_CommandHelpEntry(command: 'get lon', description: l10n.repeater_cliHelpGetLon),
_CommandHelpEntry(command: 'get rxdelay', description: l10n.repeater_cliHelpGetRxDelay),
_CommandHelpEntry(command: 'get txdelay', description: l10n.repeater_cliHelpGetTxDelay),
_CommandHelpEntry(command: 'get direct.txdelay', description: l10n.repeater_cliHelpGetDirectTxDelay),
_CommandHelpEntry(command: 'get flood.max', description: l10n.repeater_cliHelpGetFloodMax),
_CommandHelpEntry(command: 'get owner.info', description: l10n.repeater_cliHelpGetOwnerInfo),
_CommandHelpEntry(command: 'get path.hash.mode', description: l10n.repeater_cliHelpGetPathHashMode),
_CommandHelpEntry(command: 'get loop.detect', description: l10n.repeater_cliHelpGetLoopDetect),
_CommandHelpEntry(command: 'get acl', description: l10n.repeater_cliHelpGetAcl),
_CommandHelpEntry(command: 'get bridge.enabled', description: l10n.repeater_cliHelpGetBridgeEnabled),
_CommandHelpEntry(command: 'get bridge.delay', description: l10n.repeater_cliHelpGetBridgeDelay),
_CommandHelpEntry(command: 'get bridge.source', description: l10n.repeater_cliHelpGetBridgeSource),
_CommandHelpEntry(command: 'get bridge.baud', description: l10n.repeater_cliHelpGetBridgeBaud),
_CommandHelpEntry(command: 'get bridge.channel', description: l10n.repeater_cliHelpGetBridgeChannel),
_CommandHelpEntry(command: 'get bridge.secret', description: l10n.repeater_cliHelpGetBridgeSecret),
_CommandHelpEntry(command: 'get bootloader.ver', description: l10n.repeater_cliHelpGetBootloaderVer),
_CommandHelpEntry(command: 'get adc.multiplier', description: l10n.repeater_cliHelpGetAdcMultiplier),
_CommandHelpEntry(
command: 'get name',
description: l10n.repeater_cliHelpGetName,
),
_CommandHelpEntry(
command: 'get role',
description: l10n.repeater_cliHelpGetRole,
),
_CommandHelpEntry(
command: 'get public.key',
description: l10n.repeater_cliHelpGetPublicKey,
),
_CommandHelpEntry(
command: 'get prv.key',
description: l10n.repeater_cliHelpGetPrvKey,
),
_CommandHelpEntry(
command: 'get repeat',
description: l10n.repeater_cliHelpGetRepeat,
),
_CommandHelpEntry(
command: 'get tx',
description: l10n.repeater_cliHelpGetTx,
),
_CommandHelpEntry(
command: 'get freq',
description: l10n.repeater_cliHelpGetFreq,
),
_CommandHelpEntry(
command: 'get radio',
description: l10n.repeater_cliHelpGetRadio,
),
_CommandHelpEntry(
command: 'get radio.rxgain',
description: l10n.repeater_cliHelpGetRadioRxGain,
),
_CommandHelpEntry(
command: 'get af',
description: l10n.repeater_cliHelpGetAf,
),
_CommandHelpEntry(
command: 'get dutycycle',
description: l10n.repeater_cliHelpGetDutyCycle,
),
_CommandHelpEntry(
command: 'get int.thresh',
description: l10n.repeater_cliHelpGetIntThresh,
),
_CommandHelpEntry(
command: 'get agc.reset.interval',
description: l10n.repeater_cliHelpGetAgcResetInterval,
),
_CommandHelpEntry(
command: 'get multi.acks',
description: l10n.repeater_cliHelpGetMultiAcks,
),
_CommandHelpEntry(
command: 'get allow.read.only',
description: l10n.repeater_cliHelpGetAllowReadOnly,
),
_CommandHelpEntry(
command: 'get advert.interval',
description: l10n.repeater_cliHelpGetAdvertInterval,
),
_CommandHelpEntry(
command: 'get flood.advert.interval',
description: l10n.repeater_cliHelpGetFloodAdvertInterval,
),
_CommandHelpEntry(
command: 'get guest.password',
description: l10n.repeater_cliHelpGetGuestPassword,
),
_CommandHelpEntry(
command: 'get lat',
description: l10n.repeater_cliHelpGetLat,
),
_CommandHelpEntry(
command: 'get lon',
description: l10n.repeater_cliHelpGetLon,
),
_CommandHelpEntry(
command: 'get rxdelay',
description: l10n.repeater_cliHelpGetRxDelay,
),
_CommandHelpEntry(
command: 'get txdelay',
description: l10n.repeater_cliHelpGetTxDelay,
),
_CommandHelpEntry(
command: 'get direct.txdelay',
description: l10n.repeater_cliHelpGetDirectTxDelay,
),
_CommandHelpEntry(
command: 'get flood.max',
description: l10n.repeater_cliHelpGetFloodMax,
),
_CommandHelpEntry(
command: 'get owner.info',
description: l10n.repeater_cliHelpGetOwnerInfo,
),
_CommandHelpEntry(
command: 'get path.hash.mode',
description: l10n.repeater_cliHelpGetPathHashMode,
),
_CommandHelpEntry(
command: 'get loop.detect',
description: l10n.repeater_cliHelpGetLoopDetect,
),
_CommandHelpEntry(
command: 'get acl',
description: l10n.repeater_cliHelpGetAcl,
),
_CommandHelpEntry(
command: 'get bridge.enabled',
description: l10n.repeater_cliHelpGetBridgeEnabled,
),
_CommandHelpEntry(
command: 'get bridge.delay',
description: l10n.repeater_cliHelpGetBridgeDelay,
),
_CommandHelpEntry(
command: 'get bridge.source',
description: l10n.repeater_cliHelpGetBridgeSource,
),
_CommandHelpEntry(
command: 'get bridge.baud',
description: l10n.repeater_cliHelpGetBridgeBaud,
),
_CommandHelpEntry(
command: 'get bridge.channel',
description: l10n.repeater_cliHelpGetBridgeChannel,
),
_CommandHelpEntry(
command: 'get bridge.secret',
description: l10n.repeater_cliHelpGetBridgeSecret,
),
_CommandHelpEntry(
command: 'get bootloader.ver',
description: l10n.repeater_cliHelpGetBootloaderVer,
),
_CommandHelpEntry(
command: 'get adc.multiplier',
description: l10n.repeater_cliHelpGetAdcMultiplier,
),
];
final powerMgmtCommands = [
_CommandHelpEntry(command: 'get pwrmgt.support', description: l10n.repeater_cliHelpGetPwrMgtSupport),
_CommandHelpEntry(command: 'get pwrmgt.source', description: l10n.repeater_cliHelpGetPwrMgtSource),
_CommandHelpEntry(command: 'get pwrmgt.bootreason', description: l10n.repeater_cliHelpGetPwrMgtBootReason),
_CommandHelpEntry(command: 'get pwrmgt.bootmv', description: l10n.repeater_cliHelpGetPwrMgtBootMv),
_CommandHelpEntry(
command: 'get pwrmgt.support',
description: l10n.repeater_cliHelpGetPwrMgtSupport,
),
_CommandHelpEntry(
command: 'get pwrmgt.source',
description: l10n.repeater_cliHelpGetPwrMgtSource,
),
_CommandHelpEntry(
command: 'get pwrmgt.bootreason',
description: l10n.repeater_cliHelpGetPwrMgtBootReason,
),
_CommandHelpEntry(
command: 'get pwrmgt.bootmv',
description: l10n.repeater_cliHelpGetPwrMgtBootMv,
),
];
final sensorCommands = [
_CommandHelpEntry(command: 'sensor get {key}', description: l10n.repeater_cliHelpSensorGet),
_CommandHelpEntry(command: 'sensor set {key} {value}', description: l10n.repeater_cliHelpSensorSet),
_CommandHelpEntry(command: 'sensor list [start]', description: l10n.repeater_cliHelpSensorList),
_CommandHelpEntry(
command: 'sensor get {key}',
description: l10n.repeater_cliHelpSensorGet,
),
_CommandHelpEntry(
command: 'sensor set {key} {value}',
description: l10n.repeater_cliHelpSensorSet,
),
_CommandHelpEntry(
command: 'sensor list [start]',
description: l10n.repeater_cliHelpSensorList,
),
];
final gpsCommands = [
_CommandHelpEntry(command: 'gps', description: l10n.repeater_cliHelpGps),
_CommandHelpEntry(command: 'gps {on|off}', description: l10n.repeater_cliHelpGpsOnOff),
_CommandHelpEntry(command: 'gps sync', description: l10n.repeater_cliHelpGpsSync),
_CommandHelpEntry(command: 'gps setloc', description: l10n.repeater_cliHelpGpsSetLoc),
_CommandHelpEntry(command: 'gps advert', description: l10n.repeater_cliHelpGpsAdvert),
_CommandHelpEntry(command: 'gps advert {none|share|prefs}', description: l10n.repeater_cliHelpGpsAdvertSet),
_CommandHelpEntry(
command: 'gps {on|off}',
description: l10n.repeater_cliHelpGpsOnOff,
),
_CommandHelpEntry(
command: 'gps sync',
description: l10n.repeater_cliHelpGpsSync,
),
_CommandHelpEntry(
command: 'gps setloc',
description: l10n.repeater_cliHelpGpsSetLoc,
),
_CommandHelpEntry(
command: 'gps advert',
description: l10n.repeater_cliHelpGpsAdvert,
),
_CommandHelpEntry(
command: 'gps advert {none|share|prefs}',
description: l10n.repeater_cliHelpGpsAdvertSet,
),
];
showDialog(
@@ -720,27 +1072,64 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.repeater_commandsListNote, style: const TextStyle(fontSize: 13)),
Text(
l10n.repeater_commandsListNote,
style: const TextStyle(fontSize: 13),
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_general, generalCommands),
_buildHelpSection(
context,
l10n.repeater_general,
generalCommands,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_getCategory, getCommands),
_buildHelpSection(
context,
l10n.repeater_getCategory,
getCommands,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_settingsCategory, settingsCommands),
_buildHelpSection(
context,
l10n.repeater_settingsCategory,
settingsCommands,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_powerMgmt, powerMgmtCommands),
_buildHelpSection(
context,
l10n.repeater_powerMgmt,
powerMgmtCommands,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_sensors, sensorCommands),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_bridge, bridgeCommands),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_logging, loggingCommands),
_buildHelpSection(
context,
l10n.repeater_logging,
loggingCommands,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_neighborsRepeaterOnly, neighborCommands),
_buildHelpSection(
context,
l10n.repeater_neighborsRepeaterOnly,
neighborCommands,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_regionManagementRepeaterOnly, regionCommands, note: l10n.repeater_regionNote),
_buildHelpSection(
context,
l10n.repeater_regionManagementRepeaterOnly,
regionCommands,
note: l10n.repeater_regionNote,
),
const SizedBox(height: 16),
_buildHelpSection(context, l10n.repeater_gpsManagement, gpsCommands, note: l10n.repeater_gpsNote),
_buildHelpSection(
context,
l10n.repeater_gpsManagement,
gpsCommands,
note: l10n.repeater_gpsNote,
),
],
),
),
@@ -764,10 +1153,16 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15)),
Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
if (note != null) ...[
const SizedBox(height: 4),
Text(note, style: TextStyle(fontSize: 11, color: scheme.onSurfaceVariant)),
Text(
note,
style: TextStyle(fontSize: 11, color: scheme.onSurfaceVariant),
),
],
const SizedBox(height: 8),
...commands.map((entry) => _buildHelpCommandCard(context, entry)),
+10 -16
View File
@@ -70,9 +70,7 @@ class RepeaterHubScreen extends StatelessWidget {
children: [
Text(
repeater.name,
style: Theme.of(context)
.textTheme
.titleMedium
style: Theme.of(context).textTheme.titleMedium
?.copyWith(fontWeight: FontWeight.w700),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -88,9 +86,7 @@ class RepeaterHubScreen extends StatelessWidget {
const SizedBox(height: 4),
Text(
repeater.pathLabel(l10n),
style: Theme.of(context)
.textTheme
.bodySmall
style: Theme.of(context).textTheme.bodySmall
?.copyWith(color: scheme.onSurfaceVariant),
),
if (repeater.hasLocation) ...[
@@ -122,7 +118,9 @@ class RepeaterHubScreen extends StatelessWidget {
),
StatusChip(
label: isAdmin ? 'ADMIN' : 'GUEST',
color: isAdmin ? MeshPalette.blue : scheme.onSurfaceVariant,
color: isAdmin
? MeshPalette.blue
: scheme.onSurfaceVariant,
),
],
),
@@ -169,7 +167,9 @@ class RepeaterHubScreen extends StatelessWidget {
// Tools
SectionHeader(
isAdmin ? l10n.repeater_managementTools : l10n.repeater_guestTools,
isAdmin
? l10n.repeater_managementTools
: l10n.repeater_guestTools,
),
_HubActionTile(
@@ -306,9 +306,7 @@ class _HubActionTile extends StatelessWidget {
decoration: BoxDecoration(
color: accentColor.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(MeshRadii.md),
border: Border.all(
color: accentColor.withValues(alpha: 0.3),
),
border: Border.all(color: accentColor.withValues(alpha: 0.3)),
),
alignment: Alignment.center,
child: Icon(icon, size: 22, color: accentColor),
@@ -336,11 +334,7 @@ class _HubActionTile extends StatelessWidget {
],
),
),
Icon(
Icons.chevron_right,
color: scheme.onSurfaceVariant,
size: 20,
),
Icon(Icons.chevron_right, color: scheme.onSurfaceVariant, size: 20),
],
),
),
+10 -15
View File
@@ -1196,9 +1196,7 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
const SizedBox(height: 12),
DropdownButtonFormField<int>(
initialValue: _bandwidth,
decoration: InputDecoration(
labelText: l10n.repeater_bandwidth,
),
decoration: InputDecoration(labelText: l10n.repeater_bandwidth),
items: _bandwidthOptions.map((bw) {
return DropdownMenuItem(
value: bw,
@@ -1478,7 +1476,9 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
child: ListTile(
title: Text(l10n.repeater_localAdvertInterval),
subtitle: Text(
l10n.repeater_localAdvertIntervalMinutes(_advertInterval),
l10n.repeater_localAdvertIntervalMinutes(
_advertInterval,
),
),
trailing: Switch(
value: _advertEnable,
@@ -1516,7 +1516,9 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
min: 60,
max: 240,
divisions: 18,
label: l10n.repeater_localAdvertIntervalMinutes(_advertInterval),
label: l10n.repeater_localAdvertIntervalMinutes(
_advertInterval,
),
onChanged: _advertEnable
? (value) {
setState(() {
@@ -2014,9 +2016,7 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
),
subtitle: Text(
l10n.repeater_rebootRepeaterSubtitle,
style: const TextStyle(
color: MeshPalette.warnDim,
),
style: const TextStyle(color: MeshPalette.warnDim),
),
onTap: () => _confirmAction(
l10n.repeater_rebootRepeater,
@@ -2027,19 +2027,14 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
),
// Regenerate identity key - hidden until fully implemented
ListTile(
leading: const Icon(
Icons.delete_forever,
color: MeshPalette.alert,
),
leading: const Icon(Icons.delete_forever, color: MeshPalette.alert),
title: Text(
l10n.repeater_eraseFileSystem,
style: const TextStyle(color: MeshPalette.alert),
),
subtitle: Text(
l10n.repeater_eraseFileSystemSubtitle,
style: const TextStyle(
color: MeshPalette.warnDim,
),
style: const TextStyle(color: MeshPalette.warnDim),
),
onTap: () => _confirmAction(
l10n.repeater_eraseFileSystem,
+10 -7
View File
@@ -605,7 +605,8 @@ class _RepeaterStatusScreenState extends State<RepeaterStatusScreen> {
final batteryMv =
connector.getRepeaterBatteryMillivolts(widget.repeater.publicKeyHex) ??
_batteryMv;
if (batteryMv == null) return Theme.of(context).colorScheme.onSurfaceVariant;
if (batteryMv == null)
return Theme.of(context).colorScheme.onSurfaceVariant;
final percent = estimateBatteryPercentFromMillivolts(
batteryMv,
_batteryChemistry(),
@@ -624,12 +625,14 @@ class _RepeaterStatusScreenState extends State<RepeaterStatusScreen> {
crossAxisSpacing: 8,
childAspectRatio: 2.2,
children: items
.map((item) => StatTile(
icon: item.icon,
label: item.label,
value: item.value,
color: item.color,
))
.map(
(item) => StatTile(
icon: item.icon,
label: item.label,
value: item.value,
color: item.color,
),
)
.toList(),
);
}
+21 -25
View File
@@ -482,30 +482,30 @@ class _ConnectionStatusHeader extends StatelessWidget {
final (String label, Color color, bool pulse) = switch (connector.state) {
MeshCoreConnectionState.scanning => (
l10n.scanner_scanning,
MeshPalette.blue,
true,
),
l10n.scanner_scanning,
MeshPalette.blue,
true,
),
MeshCoreConnectionState.connecting => (
l10n.scanner_connecting,
MeshPalette.warn,
true,
),
l10n.scanner_connecting,
MeshPalette.warn,
true,
),
MeshCoreConnectionState.connected => (
l10n.scanner_connectedTo(connector.deviceDisplayName),
MeshPalette.signal,
false,
),
l10n.scanner_connectedTo(connector.deviceDisplayName),
MeshPalette.signal,
false,
),
MeshCoreConnectionState.disconnecting => (
l10n.scanner_disconnecting,
MeshPalette.warn,
true,
),
l10n.scanner_disconnecting,
MeshPalette.warn,
true,
),
MeshCoreConnectionState.disconnected => (
l10n.scanner_notConnected,
scheme.onSurfaceVariant,
false,
),
l10n.scanner_notConnected,
scheme.onSurfaceVariant,
false,
),
};
return Padding(
@@ -515,11 +515,7 @@ class _ConnectionStatusHeader extends StatelessWidget {
child: Align(
key: ValueKey(connector.state),
alignment: Alignment.centerLeft,
child: StatusChip(
label: label,
color: color,
pulse: pulse,
),
child: StatusChip(label: label, color: color, pulse: pulse),
),
),
);
+2 -10
View File
@@ -196,11 +196,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
),
if (showChevron)
Icon(
Icons.chevron_right,
color: scheme.onSurfaceVariant,
size: 16,
),
Icon(Icons.chevron_right, color: scheme.onSurfaceVariant, size: 16),
],
);
}
@@ -694,11 +690,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
],
),
),
Icon(
Icons.chevron_right,
color: scheme.onSurfaceVariant,
size: 16,
),
Icon(Icons.chevron_right, color: scheme.onSurfaceVariant, size: 16),
],
),
),
+1 -3
View File
@@ -220,9 +220,7 @@ class _TcpScreenState extends State<TcpScreen> {
if (connector.isTcpTransportConnected) {
return StatusChip(
label: l10n.scanner_connectedTo(
connector.activeTcpEndpoint ?? 'TCP',
),
label: l10n.scanner_connectedTo(connector.activeTcpEndpoint ?? 'TCP'),
color: MeshPalette.signal,
);
} else if (connector.state == MeshCoreConnectionState.connecting &&
+59 -60
View File
@@ -604,66 +604,68 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildAutoRefreshNumberField(
controller: _autoRefreshIntervalController,
label: l10n.common_interval,
min: _autoRefreshMinIntervalSeconds,
max: _autoRefreshMaxIntervalSeconds,
fallback: _autoRefreshIntervalSeconds,
),
const SizedBox(height: 12),
_buildAutoRefreshNumberField(
controller: _autoRefreshQuantityController,
label: l10n.telemetry_autoFetchQuantity,
min: _autoRefreshMinQuantity,
max: _autoRefreshMaxQuantity,
fallback: _autoRefreshDefaultQuantity,
),
if (counterText != null) ...[
const SizedBox(height: 12),
Text(
counterText,
textAlign: TextAlign.center,
style: TextStyle(
color: _autoRefreshLastAttemptFailed
? Theme.of(context).colorScheme.error
: null,
fontWeight: FontWeight.w600,
),
controller: _autoRefreshIntervalController,
label: l10n.common_interval,
min: _autoRefreshMinIntervalSeconds,
max: _autoRefreshMaxIntervalSeconds,
fallback: _autoRefreshIntervalSeconds,
),
],
const SizedBox(height: 12),
FilledButton(
onPressed: _isLoading && !_isAutoRefreshEnabled
? null
: _toggleAutoRefresh,
child: _isAutoRefreshEnabled
? SizedBox(
width: double.infinity,
height: 20,
child: Stack(
alignment: Alignment.center,
children: [
Center(child: Text(l10n.common_disable)),
Positioned(
right: 0,
child: SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Theme.of(context).colorScheme.onPrimary,
const SizedBox(height: 12),
_buildAutoRefreshNumberField(
controller: _autoRefreshQuantityController,
label: l10n.telemetry_autoFetchQuantity,
min: _autoRefreshMinQuantity,
max: _autoRefreshMaxQuantity,
fallback: _autoRefreshDefaultQuantity,
),
if (counterText != null) ...[
const SizedBox(height: 12),
Text(
counterText,
textAlign: TextAlign.center,
style: TextStyle(
color: _autoRefreshLastAttemptFailed
? Theme.of(context).colorScheme.error
: null,
fontWeight: FontWeight.w600,
),
),
],
const SizedBox(height: 12),
FilledButton(
onPressed: _isLoading && !_isAutoRefreshEnabled
? null
: _toggleAutoRefresh,
child: _isAutoRefreshEnabled
? SizedBox(
width: double.infinity,
height: 20,
child: Stack(
alignment: Alignment.center,
children: [
Center(child: Text(l10n.common_disable)),
Positioned(
right: 0,
child: SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Theme.of(
context,
).colorScheme.onPrimary,
),
),
),
),
],
),
)
: Text(l10n.common_enable),
),
],
],
),
)
: Text(l10n.common_enable),
),
],
),
),
),
],
],
);
}
@@ -912,10 +914,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
const SizedBox(width: 8),
Text(
value,
style: MeshTheme.mono(
fontSize: 13,
color: scheme.onSurface,
),
style: MeshTheme.mono(fontSize: 13, color: scheme.onSurface),
textAlign: TextAlign.end,
),
],
+3 -11
View File
@@ -108,9 +108,7 @@ class _UsbScreenState extends State<UsbScreen> {
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Align(
key: ValueKey(
'${connector.state}_$_isLoadingPorts',
),
key: ValueKey('${connector.state}_$_isLoadingPorts'),
alignment: Alignment.centerLeft,
child: _buildStatusChip(context, connector),
),
@@ -230,17 +228,11 @@ class _UsbScreenState extends State<UsbScreen> {
final l10n = context.l10n;
if (_isLoadingPorts) {
return EmptyState(
icon: Icons.usb,
title: l10n.usbStatus_searching,
);
return EmptyState(icon: Icons.usb, title: l10n.usbStatus_searching);
}
if (_ports.isEmpty) {
return EmptyState(
icon: Icons.usb,
title: l10n.usbScreenEmptyState,
);
return EmptyState(icon: Icons.usb, title: l10n.usbScreenEmptyState);
}
final isConnecting =
+7 -9
View File
@@ -44,7 +44,8 @@ class RetryServiceConfig {
int messageBytes, {
String? contactKey,
int? deviceTimeoutMs,
})? calculateTimeout;
})?
calculateTimeout;
final Uint8List? Function()? getSelfPublicKey;
final String Function(Contact, String)? prepareContactOutboundText;
final AppSettingsService? appSettingsService;
@@ -436,8 +437,7 @@ class MessageRetryService extends ChangeNotifier {
final outboundTextForTimeout =
config.prepareContactOutboundText?.call(contact, message.text) ??
message.text;
final messageBytesForTimeout =
utf8.encode(outboundTextForTimeout).length;
final messageBytesForTimeout = utf8.encode(outboundTextForTimeout).length;
int actualTimeout = timeoutMs;
if (config.calculateTimeout != null) {
@@ -704,13 +704,11 @@ class MessageRetryService extends ChangeNotifier {
tripTimeMs > 0 &&
message.pathLength != null) {
final outboundTextForObserved =
config!.prepareContactOutboundText?.call(
contact,
message.text,
) ??
config!.prepareContactOutboundText?.call(contact, message.text) ??
message.text;
final messageBytesForObserved =
utf8.encode(outboundTextForObserved).length;
final messageBytesForObserved = utf8
.encode(outboundTextForObserved)
.length;
config.onDeliveryObserved!(
contact.publicKeyHex,
message.pathLength!,
+5 -10
View File
@@ -30,7 +30,9 @@ class DeviceTile extends StatelessWidget {
final name = device.platformName.isNotEmpty
? device.platformName
: scanResult.advertisementData.advName;
final displayName = name.isNotEmpty ? name : context.l10n.common_unknownDevice;
final displayName = name.isNotEmpty
? name
: context.l10n.common_unknownDevice;
final mac = device.remoteId.toString();
final scheme = Theme.of(context).colorScheme;
@@ -55,11 +57,7 @@ class DeviceTile extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
child: Row(
children: [
AvatarCircle(
name: displayName,
size: 42,
icon: Icons.router,
),
AvatarCircle(name: displayName, size: 42, icon: Icons.router),
const SizedBox(width: 12),
Expanded(
child: Column(
@@ -107,10 +105,7 @@ class DeviceTile extends StatelessWidget {
const SizedBox(height: 3),
Text(
'$rssi dBm',
style: MeshTheme.mono(
fontSize: 10,
color: signalUi.color,
),
style: MeshTheme.mono(fontSize: 10, color: signalUi.color),
),
],
),
+1 -4
View File
@@ -60,10 +60,7 @@ class _FeatureToggleRow extends State<FeatureToggleRow> {
Row(
mainAxisSize: MainAxisSize.min,
children: [
Switch(
value: widget.value,
onChanged: widget.onChanged,
),
Switch(value: widget.value, onChanged: widget.onChanged),
if (widget.hasRefreshing) ...[
const SizedBox(width: 4),
widget.isRefreshing
+1 -4
View File
@@ -29,10 +29,7 @@ class JumpToBottomButton extends StatelessWidget {
decoration: BoxDecoration(
shape: BoxShape.circle,
color: scheme.surfaceContainerHigh.withValues(alpha: 0.92),
border: Border.all(
color: scheme.outlineVariant,
width: 1,
),
border: Border.all(color: scheme.outlineVariant, width: 1),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.18),
+6 -12
View File
@@ -350,7 +350,9 @@ class RouteChip extends StatelessWidget {
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final label = isDirect
? (hops == null || hops == 0 ? 'DIRECT' : '$hops HOP${hops == 1 ? '' : 'S'}')
? (hops == null || hops == 0
? 'DIRECT'
: '$hops HOP${hops == 1 ? '' : 'S'}')
: 'FLOOD';
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
@@ -536,9 +538,7 @@ Future<T?> showMeshSheet<T>(
useSafeArea: true,
showDragHandle: false,
builder: (context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.viewInsetsOf(context).bottom,
),
padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom),
child: builder(context),
),
);
@@ -574,10 +574,7 @@ class ErrorRetryCard extends StatelessWidget {
),
),
if (onRetry != null)
TextButton(
onPressed: onRetry,
child: Text(retryLabel ?? 'Retry'),
),
TextButton(onPressed: onRetry, child: Text(retryLabel ?? 'Retry')),
],
),
);
@@ -610,10 +607,7 @@ class _ListEntranceState extends State<ListEntrance>
vsync: this,
duration: const Duration(milliseconds: 280),
);
_curve = CurvedAnimation(
parent: _controller,
curve: Curves.easeOutCubic,
);
_curve = CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic);
final delay = Duration(milliseconds: 24 * widget.index.clamp(0, 12));
Future.delayed(delay, () {
if (mounted) _controller.forward();
+1 -5
View File
@@ -72,11 +72,7 @@ class _MessageStatusIconState extends State<MessageStatusIcon>
if (widget.isFailed) {
return Semantics(
label: l10n.messageStatus_failed,
child: Icon(
Icons.cancel,
size: size,
color: colorScheme.error,
),
child: Icon(Icons.cancel, size: size, color: colorScheme.error),
);
}
+8 -6
View File
@@ -160,10 +160,7 @@ List<Polyline> buildPacketTrailPolylines(
}
/// The moving packet dot and the pulse ring at the hop it just reached.
List<Marker> buildPacketMarkers(
PathPlaybackController playback,
Color color,
) {
List<Marker> buildPacketMarkers(PathPlaybackController playback, Color color) {
if (!playback.started || !playback.hasPath) return const [];
final markers = <Marker>[];
@@ -412,7 +409,9 @@ class PathAnimationControls extends StatelessWidget {
),
controlButton(
icon: playback.playing ? Icons.pause : Icons.play_arrow,
tooltip: playback.playing ? l10n.pathMap_pause : l10n.pathMap_play,
tooltip: playback.playing
? l10n.pathMap_pause
: l10n.pathMap_play,
onPressed: enabled ? playback.togglePlay : null,
),
controlButton(
@@ -635,7 +634,10 @@ class PathSummaryList extends StatelessWidget {
parts.join(' · '),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: MeshTheme.mono(fontSize: 10.5, color: MeshPalette.ink3),
style: MeshTheme.mono(
fontSize: 10.5,
color: MeshPalette.ink3,
),
),
),
),
+4 -15
View File
@@ -332,19 +332,12 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.error,
size: 18,
color: scheme.error,
),
Icon(Icons.error, size: 18, color: scheme.error),
const SizedBox(width: 8),
Expanded(
child: Text(
_loginError!,
style: TextStyle(
color: scheme.error,
fontSize: 13,
),
style: TextStyle(color: scheme.error, fontSize: 13),
),
),
],
@@ -435,9 +428,7 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
Icon(
Icons.auto_mode,
size: 20,
color: !isFloodMode
? scheme.primary
: null,
color: !isFloodMode ? scheme.primary : null,
),
const SizedBox(width: 8),
Text(
@@ -458,9 +449,7 @@ class _RepeaterLoginDialogState extends State<RepeaterLoginDialog> {
Icon(
Icons.waves,
size: 20,
color: isFloodMode
? scheme.primary
: null,
color: isFloodMode ? scheme.primary : null,
),
const SizedBox(width: 8),
Text(
+2 -6
View File
@@ -364,9 +364,7 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
Icon(
Icons.auto_mode,
size: 20,
color: !isFloodMode
? scheme.primary
: null,
color: !isFloodMode ? scheme.primary : null,
),
const SizedBox(width: 8),
Text(
@@ -387,9 +385,7 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
Icon(
Icons.waves,
size: 20,
color: isFloodMode
? scheme.primary
: null,
color: isFloodMode ? scheme.primary : null,
),
const SizedBox(width: 8),
Text(
+2 -8
View File
@@ -15,10 +15,7 @@ class UnreadDivider extends StatelessWidget {
child: Row(
children: [
Expanded(
child: Container(
height: 1,
color: color.withValues(alpha: 0.25),
),
child: Container(height: 1, color: color.withValues(alpha: 0.25)),
),
const SizedBox(width: 10),
Container(
@@ -39,10 +36,7 @@ class UnreadDivider extends StatelessWidget {
),
const SizedBox(width: 10),
Expanded(
child: Container(
height: 1,
color: color.withValues(alpha: 0.25),
),
child: Container(height: 1, color: color.withValues(alpha: 0.25)),
),
],
),