mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 08:20:36 +00:00
fix: Reset fallback sharing state
This commit is contained in:
@@ -272,7 +272,7 @@ class _AddContactScreenState extends State<AddContactScreen> {
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Contact advert',
|
||||
hintText: 'Paste a share link or hex advert',
|
||||
hintText: AppLocalizations.of(context)!.pasteShareLinkOrHexAdvert,
|
||||
alignLabelWithHint: true,
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: _validationError,
|
||||
|
||||
@@ -534,6 +534,7 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
final result = await context.read<AppProvider>().setChannelLocationSharingEnabled(
|
||||
channelIdx,
|
||||
enabled,
|
||||
l10n: AppLocalizations.of(context),
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
ToastLogger.success(context, result.message);
|
||||
@@ -1442,7 +1443,7 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
height: 1.1,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search this section',
|
||||
hintText: AppLocalizations.of(context)!.searchThisSection,
|
||||
hintStyle: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.85,
|
||||
@@ -1601,7 +1602,7 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
final availableModes = _availableSortModes(section);
|
||||
|
||||
return PopupMenuButton<ContactSortMode>(
|
||||
tooltip: 'Sort',
|
||||
tooltip: AppLocalizations.of(context)!.sort,
|
||||
initialValue: selectedMode,
|
||||
onSelected: (sortMode) {
|
||||
setState(() {
|
||||
@@ -1862,7 +1863,7 @@ class _InferredContactGroupCard extends StatelessWidget {
|
||||
if (onDelete != null) ...[
|
||||
const SizedBox(width: 2),
|
||||
IconButton(
|
||||
tooltip: 'Delete group',
|
||||
tooltip: AppLocalizations.of(context)!.deleteGroup,
|
||||
onPressed: onDelete,
|
||||
visualDensity: VisualDensity.compact,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
|
||||
@@ -854,7 +854,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!mounted) return;
|
||||
setState(() => _buzzerLoading = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed to set buzzer mode: $e')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.failedToSetBuzzerMode(e.toString()))),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -874,7 +874,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
setState(() => _gpsLoading = false);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Failed to set GPS mode: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.failedToSetGpsMode(e.toString()))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -980,8 +980,8 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
}
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Device time synced to this phone.'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.deviceTimeSynced),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -989,7 +989,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to sync device time: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToSyncDeviceTime(e.toString())),
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
);
|
||||
@@ -1055,7 +1055,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!mounted) return;
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to wipe device data: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToWipeDeviceData(e.toString())),
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
);
|
||||
@@ -1146,7 +1146,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!mounted) return;
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to clear contacts: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToClearContacts(e.toString())),
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
);
|
||||
@@ -1252,7 +1252,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!mounted) return;
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to clear channels: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToClearChannels(e.toString())),
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
);
|
||||
@@ -1273,7 +1273,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
final locationSet = _advertLocationPolicy != 0;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Device Settings')),
|
||||
appBar: AppBar(title: Text(AppLocalizations.of(context)!.deviceSettings)),
|
||||
body: ColoredBox(
|
||||
color: colorScheme.surface,
|
||||
child: SafeArea(
|
||||
@@ -1303,7 +1303,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_ConfigSectionCard(
|
||||
title: 'Device info',
|
||||
title: AppLocalizations.of(context)!.deviceInfo,
|
||||
subtitle:
|
||||
'Capabilities, storage, and maintenance tools.',
|
||||
icon: Icons.info_outline_rounded,
|
||||
@@ -1323,7 +1323,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
SizedBox(
|
||||
width: cardWidth,
|
||||
child: _StorageStat(
|
||||
label: 'BLE PIN',
|
||||
label: AppLocalizations.of(context)!.blePin,
|
||||
value: _formatBlePin(deviceInfo.blePin),
|
||||
compact: true,
|
||||
),
|
||||
@@ -1356,7 +1356,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
SizedBox(
|
||||
width: cardWidth,
|
||||
child: _StorageStat(
|
||||
label: 'Path hash',
|
||||
label: AppLocalizations.of(context)!.pathHash,
|
||||
value: _pathHashModeLabel(
|
||||
deviceInfo.pathHashMode!,
|
||||
),
|
||||
@@ -1416,7 +1416,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
icon: _buzzerEnabled!
|
||||
? Icons.volume_up_rounded
|
||||
: Icons.volume_off_rounded,
|
||||
title: 'Buzzer alerts',
|
||||
title: AppLocalizations.of(context)!.buzzerAlerts,
|
||||
description: 'Onboard buzzer for radio alerts',
|
||||
accentColor: _buzzerEnabled!
|
||||
? colorScheme.primary
|
||||
@@ -1440,7 +1440,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
icon: _multiAcksEnabled
|
||||
? Icons.mark_email_read_outlined
|
||||
: Icons.mark_email_unread_outlined,
|
||||
title: 'Multi-ACK mode',
|
||||
title: AppLocalizations.of(context)!.multiAckMode,
|
||||
description: 'Request extra acknowledgements',
|
||||
accentColor: _multiAcksEnabled
|
||||
? colorScheme.primary
|
||||
@@ -1463,7 +1463,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
SizedBox(height: 12),
|
||||
_ConfigSectionCard(
|
||||
title: AppLocalizations.of(context)!.publicInfo,
|
||||
subtitle: 'Name and telemetry shared with other devices.',
|
||||
subtitle: AppLocalizations.of(context)!.nameAndTelemetryShared,
|
||||
icon: Icons.public_rounded,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -1484,15 +1484,15 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_ConfigDropdownField(
|
||||
label: 'Base telemetry',
|
||||
label: AppLocalizations.of(context)!.baseTelemetry,
|
||||
value: _baseTelemetryMode,
|
||||
items: const [
|
||||
DropdownMenuItem(value: 0, child: Text('Deny')),
|
||||
items: [
|
||||
DropdownMenuItem(value: 0, child: Text(AppLocalizations.of(context)!.deny)),
|
||||
DropdownMenuItem(
|
||||
value: 1,
|
||||
child: Text('Use contact flags'),
|
||||
child: Text(AppLocalizations.of(context)!.useContactFlags),
|
||||
),
|
||||
DropdownMenuItem(value: 2, child: Text('Allow all')),
|
||||
DropdownMenuItem(value: 2, child: Text(AppLocalizations.of(context)!.allowAll)),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
@@ -1504,15 +1504,15 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_ConfigDropdownField(
|
||||
label: 'Location telemetry',
|
||||
label: AppLocalizations.of(context)!.locationTelemetry,
|
||||
value: _locationTelemetryMode,
|
||||
items: const [
|
||||
DropdownMenuItem(value: 0, child: Text('Deny')),
|
||||
items: [
|
||||
DropdownMenuItem(value: 0, child: Text(AppLocalizations.of(context)!.deny)),
|
||||
DropdownMenuItem(
|
||||
value: 1,
|
||||
child: Text('Use contact flags'),
|
||||
child: Text(AppLocalizations.of(context)!.useContactFlags),
|
||||
),
|
||||
DropdownMenuItem(value: 2, child: Text('Allow all')),
|
||||
DropdownMenuItem(value: 2, child: Text(AppLocalizations.of(context)!.allowAll)),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
@@ -1524,15 +1524,15 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_ConfigDropdownField(
|
||||
label: 'Environmental telemetry',
|
||||
label: AppLocalizations.of(context)!.environmentalTelemetry,
|
||||
value: _environmentTelemetryMode,
|
||||
items: const [
|
||||
DropdownMenuItem(value: 0, child: Text('Deny')),
|
||||
items: [
|
||||
DropdownMenuItem(value: 0, child: Text(AppLocalizations.of(context)!.deny)),
|
||||
DropdownMenuItem(
|
||||
value: 1,
|
||||
child: Text('Use contact flags'),
|
||||
child: Text(AppLocalizations.of(context)!.useContactFlags),
|
||||
),
|
||||
DropdownMenuItem(value: 2, child: Text('Allow all')),
|
||||
DropdownMenuItem(value: 2, child: Text(AppLocalizations.of(context)!.allowAll)),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
@@ -1566,24 +1566,24 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_ConfigSectionCard(
|
||||
title: 'GPS',
|
||||
subtitle: 'Location sharing, hardware, and update interval.',
|
||||
title: AppLocalizations.of(context)!.gpsSection,
|
||||
subtitle: AppLocalizations.of(context)!.locationSharingHardwareAndUpdateInterval,
|
||||
icon: Icons.gps_fixed,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_ConfigDropdownField(
|
||||
label: 'GPS advert policy',
|
||||
label: AppLocalizations.of(context)!.gpsAdvertPolicy,
|
||||
value: _advertLocationPolicy,
|
||||
items: const [
|
||||
DropdownMenuItem(value: 0, child: Text('Hidden')),
|
||||
items: [
|
||||
DropdownMenuItem(value: 0, child: Text(AppLocalizations.of(context)!.hidden)),
|
||||
DropdownMenuItem(
|
||||
value: 1,
|
||||
child: Text('Share live GPS'),
|
||||
child: Text(AppLocalizations.of(context)!.shareLiveGps),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 2,
|
||||
child: Text('Use saved coordinates'),
|
||||
child: Text(AppLocalizations.of(context)!.useSavedCoordinates),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
@@ -1641,20 +1641,20 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
fillColor: colorScheme.surfaceContainerLowest,
|
||||
),
|
||||
isExpanded: true,
|
||||
items: const [
|
||||
DropdownMenuItem(value: null, child: Text('Not set')),
|
||||
DropdownMenuItem(value: 0, child: Text('Off (0 s)')),
|
||||
DropdownMenuItem(value: 5, child: Text('5 seconds')),
|
||||
DropdownMenuItem(value: 10, child: Text('10 seconds')),
|
||||
DropdownMenuItem(value: 15, child: Text('15 seconds')),
|
||||
DropdownMenuItem(value: 30, child: Text('30 seconds')),
|
||||
DropdownMenuItem(value: 60, child: Text('1 minute')),
|
||||
DropdownMenuItem(value: 120, child: Text('2 minutes')),
|
||||
DropdownMenuItem(value: 300, child: Text('5 minutes')),
|
||||
DropdownMenuItem(value: 600, child: Text('10 minutes')),
|
||||
DropdownMenuItem(value: 900, child: Text('15 minutes')),
|
||||
DropdownMenuItem(value: 1800, child: Text('30 minutes')),
|
||||
DropdownMenuItem(value: 3600, child: Text('1 hour')),
|
||||
items: [
|
||||
DropdownMenuItem(value: null, child: Text(AppLocalizations.of(context)!.notSet)),
|
||||
DropdownMenuItem(value: 0, child: Text(AppLocalizations.of(context)!.offZeroSeconds)),
|
||||
DropdownMenuItem(value: 5, child: Text(AppLocalizations.of(context)!.fiveSeconds)),
|
||||
DropdownMenuItem(value: 10, child: Text(AppLocalizations.of(context)!.tenSeconds)),
|
||||
DropdownMenuItem(value: 15, child: Text(AppLocalizations.of(context)!.fifteenSeconds)),
|
||||
DropdownMenuItem(value: 30, child: Text(AppLocalizations.of(context)!.thirtySeconds)),
|
||||
DropdownMenuItem(value: 60, child: Text(AppLocalizations.of(context)!.oneMinute)),
|
||||
DropdownMenuItem(value: 120, child: Text(AppLocalizations.of(context)!.twoMinutes)),
|
||||
DropdownMenuItem(value: 300, child: Text(AppLocalizations.of(context)!.fiveMinutes)),
|
||||
DropdownMenuItem(value: 600, child: Text(AppLocalizations.of(context)!.tenMinutes)),
|
||||
DropdownMenuItem(value: 900, child: Text(AppLocalizations.of(context)!.fifteenMinutes)),
|
||||
DropdownMenuItem(value: 1800, child: Text(AppLocalizations.of(context)!.thirtyMinutes)),
|
||||
DropdownMenuItem(value: 3600, child: Text(AppLocalizations.of(context)!.oneHour)),
|
||||
],
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@@ -1687,7 +1687,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
Expanded(
|
||||
child: _CompactCoordinateField(
|
||||
controller: _latController,
|
||||
label: 'Latitude',
|
||||
label: AppLocalizations.of(context)!.latitude,
|
||||
onChanged: (_) => _markPublicInfoDirty(),
|
||||
),
|
||||
),
|
||||
@@ -1695,7 +1695,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
Expanded(
|
||||
child: _CompactCoordinateField(
|
||||
controller: _lonController,
|
||||
label: 'Longitude',
|
||||
label: AppLocalizations.of(context)!.longitude,
|
||||
onChanged: (_) => _markPublicInfoDirty(),
|
||||
),
|
||||
),
|
||||
@@ -1994,18 +1994,18 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
helperText:
|
||||
'Hash size used in adverts and flood paths',
|
||||
),
|
||||
items: const [
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: 0,
|
||||
child: Text('1 byte (mode 0)'),
|
||||
child: Text(AppLocalizations.of(context)!.oneByteMode0),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 1,
|
||||
child: Text('2 bytes (mode 1)'),
|
||||
child: Text(AppLocalizations.of(context)!.twoBytesMode1),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 2,
|
||||
child: Text('3 bytes (mode 2)'),
|
||||
child: Text(AppLocalizations.of(context)!.threeBytesMode2),
|
||||
),
|
||||
],
|
||||
onChanged: (int? newValue) {
|
||||
@@ -2070,7 +2070,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
SizedBox(height: 12),
|
||||
_ConfigSectionCard(
|
||||
title: AppLocalizations.of(context)!.autoDiscovery,
|
||||
subtitle: 'How the radio auto-adds discovered nodes.',
|
||||
subtitle: AppLocalizations.of(context)!.howTheRadioAutoAddsDiscoveredNodes,
|
||||
icon: Icons.person_search_rounded,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -2754,14 +2754,14 @@ class _SaveActionButton extends StatelessWidget {
|
||||
? Row(
|
||||
key: const ValueKey('saving'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
SizedBox(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2.2),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text('Saving...'),
|
||||
const SizedBox(width: 10),
|
||||
Text(AppLocalizations.of(context)!.saving),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
@@ -2881,14 +2881,14 @@ class _GpsDiagnosticsCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
_GpsDiagnosticsRow(label: 'Fix', value: fixValue),
|
||||
_GpsDiagnosticsRow(label: AppLocalizations.of(context)!.fix, value: fixValue),
|
||||
const SizedBox(height: 4),
|
||||
_GpsDiagnosticsRow(label: 'Satellites', value: satellitesValue),
|
||||
_GpsDiagnosticsRow(label: AppLocalizations.of(context)!.satellites, value: satellitesValue),
|
||||
const SizedBox(height: 4),
|
||||
_GpsDiagnosticsRow(label: 'Last fix', value: lastFixValue),
|
||||
_GpsDiagnosticsRow(label: AppLocalizations.of(context)!.lastFix, value: lastFixValue),
|
||||
const SizedBox(height: 4),
|
||||
_GpsDiagnosticsRow(
|
||||
label: 'Location',
|
||||
label: AppLocalizations.of(context)!.location,
|
||||
value: locationValue,
|
||||
maxLines: 2,
|
||||
),
|
||||
|
||||
@@ -69,7 +69,7 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
@@ -256,7 +256,7 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
||||
if (addError != null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed to add contact: $addError')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.failedToAddContact(addError.toString()))),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
@@ -775,7 +775,7 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
icon: const Icon(Icons.person_add_alt_1),
|
||||
tooltip: 'Resolve contact',
|
||||
tooltip: AppLocalizations.of(context)!.resolveContact,
|
||||
onPressed: isConnected
|
||||
? () => _resolveAdvert(advert)
|
||||
: null,
|
||||
@@ -848,7 +848,7 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
||||
height: 1.1,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search discovered nodes',
|
||||
hintText: AppLocalizations.of(context)!.searchDiscoveredNodes,
|
||||
hintStyle: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.85,
|
||||
|
||||
@@ -155,17 +155,17 @@ class _LiveTrafficScreenState extends State<LiveTrafficScreen> {
|
||||
if (widget.openPacketLogs != null)
|
||||
IconButton(
|
||||
onPressed: widget.openPacketLogs,
|
||||
tooltip: 'Open packet logs',
|
||||
tooltip: AppLocalizations.of(context)!.openPacketLogs,
|
||||
icon: const Icon(Icons.list_alt_rounded),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _openStatsDashboard,
|
||||
tooltip: 'View public stats',
|
||||
tooltip: AppLocalizations.of(context)!.viewPublicStats,
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => _showPacketTypeHelpSheet(context),
|
||||
tooltip: 'Packet type help',
|
||||
tooltip: AppLocalizations.of(context)!.packetTypeHelp,
|
||||
icon: const Icon(Icons.help_outline),
|
||||
),
|
||||
IconButton(
|
||||
@@ -174,7 +174,7 @@ class _LiveTrafficScreenState extends State<LiveTrafficScreen> {
|
||||
_clearedAt = widget.now();
|
||||
});
|
||||
},
|
||||
tooltip: 'Clear live view',
|
||||
tooltip: AppLocalizations.of(context)!.clearLiveView,
|
||||
icon: const Icon(Icons.cleaning_services_outlined),
|
||||
),
|
||||
],
|
||||
@@ -986,7 +986,7 @@ class _LiveTrafficCard extends StatelessWidget {
|
||||
style: TextStyle(color: scheme.onSurfaceVariant),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('Hex', style: Theme.of(context).textTheme.titleSmall),
|
||||
Text(AppLocalizations.of(context)!.hex, style: Theme.of(context).textTheme.titleSmall),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
@@ -1006,7 +1006,7 @@ class _LiveTrafficCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('ASCII', style: Theme.of(context).textTheme.titleSmall),
|
||||
Text(AppLocalizations.of(context)!.ascii, style: Theme.of(context).textTheme.titleSmall),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
|
||||
@@ -1881,7 +1881,7 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to send SAR marker: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToSendSarMarker(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1691,7 +1691,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(dialogContext)!.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
@@ -1776,6 +1776,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
final result = await context.read<AppProvider>().setChannelLocationSharingEnabled(
|
||||
channelIdx,
|
||||
enabled,
|
||||
l10n: AppLocalizations.of(context),
|
||||
);
|
||||
if (!mounted) return;
|
||||
ToastLogger.success(context, result.message);
|
||||
@@ -2059,7 +2060,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
controller: searchController,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search in current filter',
|
||||
hintText: AppLocalizations.of(context)!.searchInCurrentFilter,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
suffixIcon: searchController.text.isEmpty
|
||||
? null
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -42,7 +43,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Offline Maps'),
|
||||
title: Text(AppLocalizations.of(context)!.offlineMaps),
|
||||
actions: [
|
||||
Consumer<OfflineTilesProvider>(
|
||||
builder: (context, provider, _) {
|
||||
@@ -67,7 +68,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
builder: (context, provider, _) {
|
||||
return PopupMenuButton<MapLayer>(
|
||||
icon: const Icon(Icons.layers),
|
||||
tooltip: 'Map Style',
|
||||
tooltip: AppLocalizations.of(context)!.mapStyle,
|
||||
onSelected: (layer) => provider.setSelectedLayer(layer),
|
||||
itemBuilder: (_) => [
|
||||
for (final layer in MapLayer.allLayers)
|
||||
@@ -195,7 +196,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
isExpanded: true,
|
||||
hint: const Text('Load a saved region'),
|
||||
hint: Text(AppLocalizations.of(context)!.loadASavedRegion),
|
||||
items: provider.localStyles
|
||||
.where((s) => s.region != null)
|
||||
.map((style) => DropdownMenuItem(
|
||||
@@ -224,7 +225,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _ZoomSelector(
|
||||
label: 'Min Zoom',
|
||||
label: AppLocalizations.of(context)!.minZoom,
|
||||
value: provider.minZoom,
|
||||
onChanged: provider.setMinZoom,
|
||||
),
|
||||
@@ -232,7 +233,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _ZoomSelector(
|
||||
label: 'Max Zoom',
|
||||
label: AppLocalizations.of(context)!.maxZoom,
|
||||
value: provider.maxZoom,
|
||||
onChanged: provider.setMaxZoom,
|
||||
),
|
||||
@@ -305,7 +306,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
? () => provider.startDownload()
|
||||
: null,
|
||||
icon: const Icon(Icons.download),
|
||||
label: const Text('Download'),
|
||||
label: Text(AppLocalizations.of(context)!.download),
|
||||
),
|
||||
),
|
||||
if (provider.tileOverlays.isNotEmpty) ...[
|
||||
@@ -313,7 +314,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
IconButton(
|
||||
onPressed: provider.clearOverlays,
|
||||
icon: const Icon(Icons.layers_clear),
|
||||
tooltip: 'Clear overlay',
|
||||
tooltip: AppLocalizations.of(context)!.clearOverlay,
|
||||
),
|
||||
],
|
||||
if (provider.cacheSizeBytes > 0) ...[
|
||||
@@ -321,7 +322,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
IconButton(
|
||||
onPressed: () => _confirmClearCache(provider),
|
||||
icon: const Icon(Icons.delete_forever),
|
||||
tooltip: 'Clear cache',
|
||||
tooltip: AppLocalizations.of(context)!.clearCache,
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -330,7 +331,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => provider.cancelDownload(),
|
||||
icon: const Icon(Icons.cancel),
|
||||
label: const Text('Cancel'),
|
||||
label: Text(AppLocalizations.of(context)!.cancel),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.red,
|
||||
),
|
||||
@@ -390,7 +391,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
|
||||
// Server toggle
|
||||
SwitchListTile(
|
||||
title: const Text('Share my tiles'),
|
||||
title: Text(AppLocalizations.of(context)!.shareMyTiles),
|
||||
subtitle: Text(
|
||||
provider.isServerRunning
|
||||
? 'Other devices can fetch tiles from this device'
|
||||
@@ -449,7 +450,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline,
|
||||
size: 20),
|
||||
tooltip: 'Delete',
|
||||
tooltip: AppLocalizations.of(context)!.delete,
|
||||
onPressed: () => _confirmDeleteStyle(
|
||||
context, provider, style),
|
||||
),
|
||||
@@ -481,13 +482,13 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
else
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh, size: 20),
|
||||
tooltip: 'Refresh',
|
||||
tooltip: AppLocalizations.of(context)!.refresh,
|
||||
onPressed: () =>
|
||||
provider.refreshPeerCatalogs(),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add, size: 20),
|
||||
tooltip: 'Add peer manually',
|
||||
tooltip: AppLocalizations.of(context)!.addPeerManually,
|
||||
onPressed: () =>
|
||||
_showAddPeerDialog(context, provider),
|
||||
),
|
||||
@@ -524,7 +525,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
.map((peer) => ListTile(
|
||||
leading: const Icon(Icons.devices),
|
||||
title: Text(peer.ipAddress),
|
||||
subtitle: const Text('Fetching catalog...'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.fetchingCatalog),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.remove_circle_outline,
|
||||
@@ -556,7 +557,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
if (provider.isSyncing)
|
||||
TextButton(
|
||||
onPressed: () => provider.cancelSync(),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -691,7 +692,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Add Peer'),
|
||||
title: Text(AppLocalizations.of(context)!.addPeer),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
@@ -710,7 +711,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -720,7 +721,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: const Text('Add'),
|
||||
child: Text(AppLocalizations.of(context)!.add),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -735,7 +736,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text('Delete ${style.displayName}?'),
|
||||
title: Text(AppLocalizations.of(context)!.deleteStyleConfirm(style.displayName)),
|
||||
content: Text(
|
||||
'${_formatNumber(style.tileCount)} tiles, '
|
||||
'${_formatBytes(style.sizeBytes)} will be deleted.',
|
||||
@@ -743,7 +744,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -751,7 +752,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
Navigator.pop(dialogContext);
|
||||
},
|
||||
child:
|
||||
const Text('Delete', style: TextStyle(color: Colors.red)),
|
||||
Text(AppLocalizations.of(context)!.delete, style: const TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -762,13 +763,13 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Clear offline cache?'),
|
||||
title: Text(AppLocalizations.of(context)!.clearOfflineCache),
|
||||
content: Text(
|
||||
'This will delete ${_formatBytes(provider.cacheSizeBytes)} of cached tiles.'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -776,7 +777,7 @@ class _OfflineMapScreenState extends State<OfflineMapScreen> {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child:
|
||||
const Text('Delete', style: TextStyle(color: Colors.red)),
|
||||
Text(AppLocalizations.of(context)!.delete, style: const TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -96,7 +96,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Export failed: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.exportFailed(e.toString()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Export failed: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.exportFailed(e.toString()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +222,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
? Icons.arrow_downward
|
||||
: Icons.arrow_upward,
|
||||
),
|
||||
tooltip: 'Filter by direction',
|
||||
tooltip: AppLocalizations.of(context)!.filterByDirection,
|
||||
onSelected: (direction) {
|
||||
setState(() {
|
||||
_filterDirection = direction;
|
||||
@@ -314,7 +314,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
// Export menu
|
||||
PopupMenuButton(
|
||||
icon: const Icon(Icons.share),
|
||||
tooltip: 'Export logs',
|
||||
tooltip: AppLocalizations.of(context)!.exportLogs,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 'csv',
|
||||
@@ -348,7 +348,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
// Clear logs
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
tooltip: 'Clear logs',
|
||||
tooltip: AppLocalizations.of(context)!.clearLogs,
|
||||
onPressed: () => _clearLogs(context),
|
||||
),
|
||||
],
|
||||
@@ -360,7 +360,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search logs...',
|
||||
hintText: AppLocalizations.of(context)!.searchLogs,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
suffixIcon: _searchQuery.isNotEmpty
|
||||
? IconButton(
|
||||
@@ -615,7 +615,7 @@ class _PacketLogCard extends StatelessWidget {
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
onPressed: onCopy,
|
||||
tooltip: 'Copy full hex',
|
||||
tooltip: AppLocalizations.of(context)!.copyFullHex,
|
||||
icon: const Icon(Icons.copy_all_rounded, size: 18),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
|
||||
@@ -25,7 +25,7 @@ class ProfilesScreen extends StatelessWidget {
|
||||
.importProfileFromFile();
|
||||
},
|
||||
icon: const Icon(Icons.file_open),
|
||||
tooltip: 'Import profile',
|
||||
tooltip: AppLocalizations.of(context)!.importProfile,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -143,9 +143,9 @@ class ProfilesScreen extends StatelessWidget {
|
||||
child: Text(AppLocalizations.of(context)!.rename),
|
||||
),
|
||||
if (!profile.isDefault)
|
||||
const PopupMenuItem(
|
||||
PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
child: Text(AppLocalizations.of(context)!.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -197,7 +197,7 @@ class ProfilesScreen extends StatelessWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(controller.text.trim()),
|
||||
@@ -231,11 +231,11 @@ class ProfilesScreen extends StatelessWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(controller.text.trim()),
|
||||
child: const Text('Save'),
|
||||
child: Text(AppLocalizations.of(context)!.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -269,12 +269,12 @@ class _RepeatersMapScreenState extends State<RepeatersMapScreen> {
|
||||
await connectionProvider.getContact(_hexToBytes(repeater.publicKey));
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('${repeater.name} added to contacts')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.addedToContacts(repeater.name))),
|
||||
);
|
||||
} catch (error) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed to add ${repeater.name}: $error')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.failedToAddName(repeater.name, error.toString()))),
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
@@ -374,7 +374,7 @@ class _RepeatersMapScreenState extends State<RepeatersMapScreen> {
|
||||
? null
|
||||
: () => _loadRepeaters(forceRefresh: true),
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: 'Refresh',
|
||||
tooltip: AppLocalizations.of(context)!.refresh,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -491,7 +491,7 @@ class _RepeatersMapScreenState extends State<RepeatersMapScreen> {
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _jumpToMyLocation,
|
||||
tooltip: 'My location',
|
||||
tooltip: AppLocalizations.of(context)!.myLocation,
|
||||
child: const Icon(Icons.my_location),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -108,7 +108,7 @@ class _SensorsTabState extends State<SensorsTab> {
|
||||
if (!sheetContext.mounted) return;
|
||||
Navigator.of(sheetContext).pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('${contact.displayName} added to Sensors')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.addedToSensors(contact.displayName))),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -183,14 +183,14 @@ class _SensorsTabState extends State<SensorsTab> {
|
||||
actions: [
|
||||
if (sensorsProvider.labelOverrideFor(publicKeyHex, option.key) !=
|
||||
null)
|
||||
TextButton(onPressed: controller.clear, child: const Text('Reset')),
|
||||
TextButton(onPressed: controller.clear, child: Text(AppLocalizations.of(dialogContext)!.reset)),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(dialogContext)!.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: const Text('Save'),
|
||||
child: Text(AppLocalizations.of(dialogContext)!.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -543,10 +543,10 @@ class _AddSensorSheetState extends State<AddSensorSheet> {
|
||||
child: TextField(
|
||||
controller: _searchController,
|
||||
onChanged: (_) => setState(() {}),
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.search),
|
||||
hintText: 'Search sensors',
|
||||
border: OutlineInputBorder(),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
hintText: AppLocalizations.of(context)!.searchSensors,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -655,7 +655,7 @@ class SensorCustomizeView extends StatelessWidget {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Customize ${contact?.displayName ?? 'Sensor'}'),
|
||||
title: Text(AppLocalizations.of(context)!.customizeSensor(contact?.displayName ?? AppLocalizations.of(context)!.sensor)),
|
||||
),
|
||||
body: ListView(
|
||||
padding: EdgeInsets.fromLTRB(16, 16, 16, 24),
|
||||
@@ -1094,12 +1094,12 @@ class SensorMetricSelectorItem extends StatelessWidget {
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
tooltip: 'Move up',
|
||||
tooltip: AppLocalizations.of(context)!.moveUp,
|
||||
onPressed: canMoveUp ? onMoveUp : null,
|
||||
icon: const Icon(Icons.arrow_upward),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Move down',
|
||||
tooltip: AppLocalizations.of(context)!.moveDown,
|
||||
onPressed: canMoveDown ? onMoveDown : null,
|
||||
icon: const Icon(Icons.arrow_downward),
|
||||
),
|
||||
@@ -1259,7 +1259,7 @@ class _SelfOnlySensorsCta extends StatelessWidget {
|
||||
child: FilledButton.icon(
|
||||
onPressed: onAddSensor,
|
||||
icon: const Icon(Icons.add, size: 18),
|
||||
label: const Text('Choose'),
|
||||
label: Text(AppLocalizations.of(context)!.choose),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -453,7 +453,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (parsed == null) return;
|
||||
Navigator.pop(context, parsed.clamp(10.0, 1000.0));
|
||||
},
|
||||
child: const Text('Save'),
|
||||
child: Text(AppLocalizations.of(context)!.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -494,7 +494,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (parsed == null) return;
|
||||
Navigator.pop(context, parsed.clamp(60, 60));
|
||||
},
|
||||
child: const Text('Save'),
|
||||
child: Text(AppLocalizations.of(context)!.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -559,7 +559,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.block),
|
||||
title: const Text('Disable fast GPS publishing'),
|
||||
title: Text(AppLocalizations.of(context)!.disableFastGpsPublishing),
|
||||
trailing: _fastLocationChannelIdx == null
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
@@ -663,7 +663,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to load preview image: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToLoadPreviewImage(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -816,7 +816,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Error checking for updates: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.errorCheckingForUpdates(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -1009,7 +1009,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
debugPrint('Error handling location permission: $e');
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error: $e'), backgroundColor: Colors.red),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.errorGeneric(e.toString())), backgroundColor: Colors.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1233,8 +1233,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Appearance ──
|
||||
_buildSection(
|
||||
icon: Icons.palette_rounded,
|
||||
title: 'Appearance',
|
||||
subtitle: 'Theme, language, and display preferences',
|
||||
title: AppLocalizations.of(context)!.appearance,
|
||||
subtitle: AppLocalizations.of(context)!.themeLanguageAndDisplayPreferences,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
@@ -1304,8 +1304,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Notifications ──
|
||||
_buildSection(
|
||||
icon: Icons.notifications_outlined,
|
||||
title: 'Notifications',
|
||||
subtitle: 'Control which alerts you receive',
|
||||
title: AppLocalizations.of(context)!.notifications,
|
||||
subtitle: AppLocalizations.of(context)!.controlWhichAlertsYouReceive,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
dense: true,
|
||||
@@ -1357,7 +1357,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
dense: true,
|
||||
secondary: const Icon(Icons.visibility_off_outlined, size: 20),
|
||||
title: Text(l10n.muteWhileAppIsOpen),
|
||||
subtitle: const Text('Suppress notifications while in foreground'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.suppressNotificationsWhileInForeground),
|
||||
value: _muteForegroundNotifications,
|
||||
onChanged: (value) async {
|
||||
setState(() => _muteForegroundNotifications = value);
|
||||
@@ -1373,8 +1373,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Consumer<AppProvider>(
|
||||
builder: (context, appProvider, child) => _buildSection(
|
||||
icon: Icons.dashboard_customize_rounded,
|
||||
title: 'Tabs & Navigation',
|
||||
subtitle: 'Choose which tabs and contact sections to show',
|
||||
title: AppLocalizations.of(context)!.tabsAndNavigation,
|
||||
subtitle: AppLocalizations.of(context)!.chooseWhichTabsAndContactSectionsToShow,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
dense: true,
|
||||
@@ -1485,8 +1485,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Messaging ──
|
||||
_buildSection(
|
||||
icon: Icons.chat_rounded,
|
||||
title: 'Messaging',
|
||||
subtitle: 'Routing, retries, and destination lock',
|
||||
title: AppLocalizations.of(context)!.messaging,
|
||||
subtitle: AppLocalizations.of(context)!.routingRetriesAndDestinationLock,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
@@ -1509,7 +1509,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
dense: true,
|
||||
secondary: const Icon(Icons.route, size: 20),
|
||||
title: Text(l10n.nearestRepeaterFallback),
|
||||
subtitle: const Text('Resend through nearest repeater on failure'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.resendThroughNearestRepeaterOnFailure),
|
||||
value: appProvider.nearestRelayFallbackEnabled,
|
||||
onChanged: (value) async {
|
||||
await appProvider.toggleNearestRelayFallbackEnabled(value);
|
||||
@@ -1521,7 +1521,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
dense: true,
|
||||
secondary: const Icon(Icons.cleaning_services, size: 20),
|
||||
title: Text(l10n.clearPathOnMaxRetry),
|
||||
subtitle: const Text('Clear route only after all retries fail'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.clearRouteOnlyAfterAllRetriesFail),
|
||||
value: appProvider.clearPathOnMaxRetry,
|
||||
onChanged: (value) async {
|
||||
await appProvider.toggleClearPathOnMaxRetry(value);
|
||||
@@ -1532,7 +1532,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
SwitchListTile(
|
||||
dense: true,
|
||||
secondary: const Icon(Icons.lock_outline, size: 20),
|
||||
title: const Text('Lock destination'),
|
||||
title: Text(AppLocalizations.of(context)!.lockDestination),
|
||||
subtitle: const Text(
|
||||
'Fix Messages tab to one channel or room',
|
||||
),
|
||||
@@ -1617,8 +1617,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Map & Tracing ──
|
||||
_buildSection(
|
||||
icon: Icons.map_rounded,
|
||||
title: 'Map',
|
||||
subtitle: 'Display, markers, and trace database',
|
||||
title: AppLocalizations.of(context)!.map,
|
||||
subtitle: AppLocalizations.of(context)!.displayMarkersAndTraceDatabase,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
dense: true,
|
||||
@@ -1701,8 +1701,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── GPS & Location ──
|
||||
_buildSection(
|
||||
icon: Icons.gps_fixed,
|
||||
title: 'GPS & Location',
|
||||
subtitle: 'Fast updates, thresholds, and permissions',
|
||||
title: AppLocalizations.of(context)!.gpsAndLocation,
|
||||
subtitle: AppLocalizations.of(context)!.fastUpdatesThresholdsAndPermissions,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
dense: true,
|
||||
@@ -1749,7 +1749,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Icons.forum, size: 20),
|
||||
title: const Text('Fast GPS target channel'),
|
||||
title: Text(AppLocalizations.of(context)!.fastGpsTargetChannel),
|
||||
subtitle: Text(
|
||||
_describeFastLocationChannel(
|
||||
context.watch<ContactsProvider>().channels,
|
||||
@@ -1761,7 +1761,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Icons.send, size: 20),
|
||||
title: const Text('Test send update'),
|
||||
title: Text(AppLocalizations.of(context)!.testSendUpdate),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: _sendTestFastLocationUpdate,
|
||||
),
|
||||
@@ -1831,8 +1831,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
_buildSection(
|
||||
icon: Icons.mic_rounded,
|
||||
title: 'Voice',
|
||||
subtitle: 'Codec, bitrate, and audio processing',
|
||||
title: AppLocalizations.of(context)!.voice,
|
||||
subtitle: AppLocalizations.of(context)!.codecBitrateAndAudioProcessing,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
@@ -1933,8 +1933,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Images ──
|
||||
_buildSection(
|
||||
icon: Icons.image_rounded,
|
||||
title: 'Images',
|
||||
subtitle: 'Size, compression, and preview',
|
||||
title: AppLocalizations.of(context)!.images,
|
||||
subtitle: AppLocalizations.of(context)!.sizeCompressionAndPreview,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
@@ -1970,7 +1970,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
dense: true,
|
||||
secondary: const Icon(Icons.invert_colors, size: 20),
|
||||
title: Text(l10n.grayscale),
|
||||
subtitle: const Text('Smaller file size'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.smallerFileSize),
|
||||
value: _imageGrayscale,
|
||||
onChanged: (value) async {
|
||||
await ImagePreferences.setGrayscale(value);
|
||||
@@ -1982,7 +1982,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
dense: true,
|
||||
secondary: const Icon(Icons.compress, size: 20),
|
||||
title: Text(l10n.ultraMode),
|
||||
subtitle: const Text('Extra-aggressive AVIF compression'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.extraAggressiveAvifCompression),
|
||||
value: _imageUltraMode,
|
||||
onChanged: (value) async {
|
||||
await ImagePreferences.setUltraMode(value);
|
||||
@@ -2000,8 +2000,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Profiles ──
|
||||
_buildSection(
|
||||
icon: Icons.layers_outlined,
|
||||
title: 'Profiles',
|
||||
subtitle: 'Multi-device workspace management',
|
||||
title: AppLocalizations.of(context)!.profiles,
|
||||
subtitle: AppLocalizations.of(context)!.multiDeviceWorkspaceManagement,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
dense: true,
|
||||
@@ -2043,8 +2043,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Help ──
|
||||
_buildSection(
|
||||
icon: Icons.help_outline_rounded,
|
||||
title: 'Help',
|
||||
subtitle: 'Templates and tutorials',
|
||||
title: AppLocalizations.of(context)!.help,
|
||||
subtitle: AppLocalizations.of(context)!.templatesAndTutorials,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
@@ -2125,8 +2125,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// ── Data & Developer ──
|
||||
_buildSection(
|
||||
icon: Icons.storage_rounded,
|
||||
title: 'Data',
|
||||
subtitle: 'Traffic stats, message history, and developer tools',
|
||||
title: AppLocalizations.of(context)!.data,
|
||||
subtitle: AppLocalizations.of(context)!.trafficStatsMessageHistoryAndDeveloperTools,
|
||||
children: [
|
||||
Consumer<AppProvider>(
|
||||
builder: (context, appProvider, child) => ListenableBuilder(
|
||||
|
||||
Reference in New Issue
Block a user