fix: Reset fallback sharing state

This commit is contained in:
Janez T
2026-04-07 14:04:45 +02:00
parent e5514b4ef9
commit 44a668df93
64 changed files with 12741 additions and 8677 deletions

View File

@@ -654,7 +654,7 @@ class _ConnectionDialogState extends State<ConnectionDialog>
title: device.platformName.isNotEmpty
? device.platformName
: 'Unknown Device',
subtitle: 'Signal $rssi dBm',
subtitle: AppLocalizations.of(context)!.signalDbm(rssi.toString()),
trailing: isConnecting
? const SizedBox(
width: 24,
@@ -665,7 +665,7 @@ class _ConnectionDialogState extends State<ConnectionDialog>
)
: FilledButton.tonal(
onPressed: connectBle,
child: const Text('Connect'),
child: Text(AppLocalizations.of(context)!.connect),
),
onTap: isConnecting ? null : connectBle,
enabled: !isConnecting,
@@ -721,7 +721,7 @@ class _ConnectionDialogState extends State<ConnectionDialog>
: _discoveredServers.isEmpty
? _buildEmptyState(
icon: Icons.wifi_off_rounded,
title: 'No servers found',
title: AppLocalizations.of(context)!.noServersFound,
actionLabel: 'Scan Again',
onAction: _startNetworkScan,
)
@@ -792,7 +792,7 @@ class _ConnectionDialogState extends State<ConnectionDialog>
onPressed: isAnyConnectionInProgress
? null
: connectServer,
child: const Text('Connect'),
child: Text(AppLocalizations.of(context)!.connect),
),
enabled: !isAnyConnectionInProgress,
onTap: isAnyConnectionInProgress ? null : connectServer,
@@ -885,7 +885,7 @@ class _ManualTcpHostDialogState extends State<_ManualTcpHostDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Connect by IP address'),
title: Text(AppLocalizations.of(context)!.connectByIpAddress),
content: TextField(
controller: _controller,
autofocus: true,
@@ -910,11 +910,11 @@ class _ManualTcpHostDialogState extends State<_ManualTcpHostDialog> {
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
child: Text(AppLocalizations.of(context)!.cancel),
),
FilledButton(
onPressed: _submit,
child: const Text('Connect'),
child: Text(AppLocalizations.of(context)!.connect),
),
],
);
@@ -1053,7 +1053,7 @@ class _SerialDeviceListState extends State<_SerialDeviceList> {
if (!mounted) return;
setState(() => _isConnecting = false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Failed to connect via serial')),
SnackBar(content: Text(AppLocalizations.of(context)!.failedToConnectViaSerial)),
);
}
} catch (e) {
@@ -1061,7 +1061,7 @@ class _SerialDeviceListState extends State<_SerialDeviceList> {
setState(() => _isConnecting = false);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Serial error: $e')));
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.serialError(e.toString()))));
}
}
@@ -1127,7 +1127,7 @@ class _SerialDeviceListState extends State<_SerialDeviceList> {
onPressed: _isConnecting
? null
: () => _connectToDevice(device),
child: const Text('Connect'),
child: Text(AppLocalizations.of(context)!.connect),
),
enabled: !_isConnecting,
onTap: _isConnecting ? null : () => _connectToDevice(device),

View File

@@ -320,7 +320,7 @@ class _ContactRouteDialogState extends State<ContactRouteDialog> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Selected relays', style: Theme.of(context).textTheme.titleSmall),
Text(AppLocalizations.of(context)!.selectedRelays, style: Theme.of(context).textTheme.titleSmall),
const SizedBox(height: 8),
Wrap(
spacing: 8,
@@ -355,11 +355,11 @@ class _ContactRouteDialogState extends State<ContactRouteDialog> {
TextField(
controller: _relaySearchController,
onChanged: (_) => setState(() {}),
decoration: const InputDecoration(
prefixIcon: Icon(Icons.search),
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
labelText: 'Find relay',
hintText: 'Search by name or token',
border: OutlineInputBorder(),
hintText: AppLocalizations.of(context)!.searchByNameOrToken,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 12),
@@ -509,7 +509,7 @@ class _ContactRouteDialogState extends State<ContactRouteDialog> {
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Path size', style: Theme.of(context).textTheme.labelLarge),
Text(AppLocalizations.of(context)!.pathSize, style: Theme.of(context).textTheme.labelLarge),
const Spacer(),
SegmentedButton<int>(
segments: [
@@ -621,7 +621,7 @@ class _ContactRouteDialogState extends State<ContactRouteDialog> {
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text('Set Path for ${widget.contact.displayName}'),
title: Text(AppLocalizations.of(context)!.setPathForContact(widget.contact.displayName)),
bottom: const TabBar(
tabs: [
Tab(text: 'Build'),

View File

@@ -437,6 +437,7 @@ class ContactTile extends StatelessWidget {
final result = await context.read<AppProvider>().setChannelLocationSharingEnabled(
channelIdx,
enabled,
l10n: AppLocalizations.of(context),
);
if (!context.mounted) return;
ToastLogger.success(context, result.message);
@@ -655,7 +656,7 @@ class ContactTile extends StatelessWidget {
if (contact.type == ContactType.repeater)
_ContactSheetAction(
icon: Icons.hub_outlined,
label: 'View Neighbours',
label: AppLocalizations.of(context)!.viewNeighbours,
onTap: () async {
Navigator.pop(context);
_showNeighbours(context, contact);
@@ -665,7 +666,7 @@ class ContactTile extends StatelessWidget {
contact.type == ContactType.room)
_ContactSheetAction(
icon: Icons.network_ping,
label: 'Ping',
label: AppLocalizations.of(context)!.ping,
onTap: () async {
Navigator.pop(context);
_pingRelay(context, contact);
@@ -2395,7 +2396,7 @@ class _NeighboursSheetState extends State<_NeighboursSheet> {
),
)
: _neighbours.isEmpty
? const Center(child: Text('No neighbours found'))
? Center(child: Text(AppLocalizations.of(context)!.noNeighboursFound))
: Padding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: Column(
@@ -2768,7 +2769,7 @@ class _PingRelaySheetState extends State<_PingRelaySheet> {
IconButton(
onPressed: () => setState(() => _history.clear()),
icon: const Icon(Icons.delete_outline, size: 20),
tooltip: 'Clear history',
tooltip: AppLocalizations.of(context)!.clearHistory,
style: IconButton.styleFrom(
foregroundColor: colorScheme.onSurfaceVariant,
),

View File

@@ -6,6 +6,7 @@ import 'package:flutter_map/flutter_map.dart' as flutter_map;
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import '../../l10n/app_localizations.dart';
import '../../models/contact.dart';
import '../../providers/connection_provider.dart';
import '../../providers/contacts_provider.dart';
@@ -84,7 +85,7 @@ class _ContactTraceSheetState extends State<ContactTraceSheet> {
child: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text('Failed to load trace: ${snapshot.error}'),
child: Text(AppLocalizations.of(context)!.failedToLoadTrace(snapshot.error.toString())),
),
),
);

View File

@@ -179,7 +179,7 @@ class DrawingMarkersLayer extends StatelessWidget {
context: context,
builder: (context) => AlertDialog(
title: Text(AppLocalizations.of(context)!.deleteDrawing),
content: Text('Delete this ${drawing.type.name}?'),
content: Text(AppLocalizations.of(context)!.deleteThisDrawing(drawing.type.name)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),

View File

@@ -398,7 +398,7 @@ class DrawingToolbar extends StatelessWidget {
);
}
},
tooltip: 'Share',
tooltip: AppLocalizations.of(context)!.share,
color: Colors.blue,
padding: const EdgeInsets.all(4),
constraints: const BoxConstraints(),
@@ -415,7 +415,7 @@ class DrawingToolbar extends StatelessWidget {
drawing,
);
},
tooltip: 'Delete',
tooltip: AppLocalizations.of(context)!.delete,
color: Colors.red,
padding: const EdgeInsets.all(4),
constraints: const BoxConstraints(),

View File

@@ -5,6 +5,7 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import '../../l10n/app_localizations.dart';
import '../../providers/offline_tiles_provider.dart';
/// Renders polygon/rectangle drawing interaction on the map.
@@ -199,7 +200,7 @@ class DrawingToolbar extends StatelessWidget {
children: [
_ToolButton(
icon: Icons.crop_square,
label: 'Rectangle',
label: AppLocalizations.of(context)!.rectangle,
isActive: provider.drawingMode == DrawingMode.rectangle,
onTap: () => provider.setDrawingMode(
provider.drawingMode == DrawingMode.rectangle
@@ -210,7 +211,7 @@ class DrawingToolbar extends StatelessWidget {
const SizedBox(height: 8),
_ToolButton(
icon: Icons.pentagon_outlined,
label: 'Polygon',
label: AppLocalizations.of(context)!.polygon,
isActive: provider.drawingMode == DrawingMode.polygon,
onTap: () => provider.setDrawingMode(
provider.drawingMode == DrawingMode.polygon
@@ -223,7 +224,7 @@ class DrawingToolbar extends StatelessWidget {
const SizedBox(height: 8),
_ToolButton(
icon: Icons.check,
label: 'Finish',
label: AppLocalizations.of(context)!.finish,
isActive: false,
color: Colors.green,
onTap: () => provider.finishPolygon(),
@@ -234,7 +235,7 @@ class DrawingToolbar extends StatelessWidget {
const SizedBox(height: 8),
_ToolButton(
icon: Icons.undo,
label: 'Undo',
label: AppLocalizations.of(context)!.undo,
isActive: false,
onTap: () => provider.undoLastVertex(),
),
@@ -243,7 +244,7 @@ class DrawingToolbar extends StatelessWidget {
const SizedBox(height: 8),
_ToolButton(
icon: Icons.delete_outline,
label: 'Clear',
label: AppLocalizations.of(context)!.clear,
isActive: false,
color: Colors.red,
onTap: () => provider.clearPolygons(),

View File

@@ -394,7 +394,7 @@ class _CustomMapSarUpdateSheetState extends State<CustomMapSarUpdateSheet> {
controller: _notesController,
maxLines: 3,
decoration: InputDecoration(
hintText: 'Add additional details',
hintText: AppLocalizations.of(context)!.addAdditionalDetails,
filled: true,
fillColor: colorScheme.surfaceContainerHighest,
border: OutlineInputBorder(

View File

@@ -11,6 +11,7 @@ import '../../providers/contacts_provider.dart';
import '../../providers/image_provider.dart' as ip;
import '../../providers/messages_provider.dart';
import '../../utils/image_message_parser.dart';
import '../../l10n/app_localizations.dart';
import '../../utils/transmission_target_resolver.dart';
import 'transfer_timeout.dart';
@@ -237,7 +238,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
onPressed: () => _cancelReceive(envelope.sessionId),
icon: const Icon(Icons.close, size: 20),
color: Colors.white70,
tooltip: 'Cancel image receive',
tooltip: AppLocalizations.of(context)!.cancelImageReceive,
),
),
] else if (_errorText != null) ...[
@@ -255,7 +256,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
pathLen: pathLen,
),
icon: const Icon(Icons.refresh, size: 18),
label: const Text('Retry'),
label: Text(AppLocalizations.of(context)!.retry),
style: TextButton.styleFrom(
foregroundColor: Colors.white70,
),
@@ -657,7 +658,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
onPressed: () => Navigator.of(dialogContext).pop(),
icon: const Icon(Icons.close),
color: Colors.white,
tooltip: 'Close',
tooltip: AppLocalizations.of(context)!.close,
),
),
),

View File

@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart' as flutter_map;
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import '../../l10n/app_localizations.dart';
import '../../models/ble_packet_log.dart';
import '../../models/message.dart';
import '../../providers/connection_provider.dart';
@@ -133,7 +134,7 @@ class _MessageTraceSheetState extends State<MessageTraceSheet> {
child: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text('Failed to load trace: ${snapshot.error}'),
child: Text(AppLocalizations.of(context)!.failedToLoadTrace(snapshot.error.toString())),
),
),
);

View File

@@ -602,7 +602,7 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
final colorScheme = Theme.of(context).colorScheme;
return PopupMenuButton<_RecipientSortMode>(
tooltip: 'Sort',
tooltip: AppLocalizations.of(context)!.sort,
initialValue: _sortMode,
onSelected: (sortMode) {
setState(() {
@@ -642,13 +642,13 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
],
),
),
const PopupMenuItem<_RecipientSortMode>(
PopupMenuItem<_RecipientSortMode>(
value: _RecipientSortMode.alphabetical,
child: Row(
children: [
Icon(Icons.sort_by_alpha_rounded, size: 18),
SizedBox(width: 8),
Text('A-Z'),
const Icon(Icons.sort_by_alpha_rounded, size: 18),
const SizedBox(width: 8),
Text(AppLocalizations.of(context)!.aToZ),
],
),
),

View File

@@ -176,7 +176,7 @@ class _PermissionRequestDialogState extends State<PermissionRequestDialog> {
Navigator.of(context).pop();
widget.onPermissionsDenied?.call();
},
child: const Text('Skip'),
child: Text(AppLocalizations.of(context)!.skip),
),
if (_errorMessage != null && !_isRequesting)
ElevatedButton(
@@ -184,13 +184,13 @@ class _PermissionRequestDialogState extends State<PermissionRequestDialog> {
// Open app settings
await Geolocator.openLocationSettings();
},
child: const Text('Open Settings'),
child: Text(AppLocalizations.of(context)!.openSettings),
),
if (_errorMessage != null && !_isRequesting &&
!_errorMessage!.contains('permanently denied'))
ElevatedButton(
onPressed: _checkAndRequestPermissions,
child: const Text('Retry'),
child: Text(AppLocalizations.of(context)!.retry),
),
],
),

View File

@@ -3,6 +3,7 @@ import 'dart:math' as math;
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import '../../l10n/app_localizations.dart';
import 'package:meshcore_client/meshcore_client.dart' show Message;
import 'package:provider/provider.dart';
@@ -233,7 +234,7 @@ class _BTHomeMetHistorySheetState extends State<_BTHomeMetHistorySheet> {
),
),
icon: const Icon(Icons.chevron_left),
label: const Text('Newer'),
label: Text(AppLocalizations.of(context)!.newer),
),
const SizedBox(width: 8),
OutlinedButton.icon(
@@ -249,7 +250,7 @@ class _BTHomeMetHistorySheetState extends State<_BTHomeMetHistorySheet> {
),
),
icon: const Icon(Icons.chevron_right),
label: const Text('Older'),
label: Text(AppLocalizations.of(context)!.older),
),
const Spacer(),
if (_loading)
@@ -276,13 +277,13 @@ class _BTHomeMetHistorySheetState extends State<_BTHomeMetHistorySheet> {
if (_error != null)
_HistoryMessageCard(
icon: Icons.error_outline,
title: 'Could not load MET history',
title: AppLocalizations.of(context)!.couldNotLoadMetHistory,
body: _error!,
)
else if (_loading && history == null)
const _HistoryMessageCard(
_HistoryMessageCard(
icon: Icons.hourglass_top,
title: 'Loading',
title: AppLocalizations.of(context)!.loading,
body: 'Waiting for the sensor to reply.',
)
else if (history != null) ...[
@@ -362,19 +363,19 @@ class _HistoryStatsGrid extends StatelessWidget {
final compact = constraints.maxWidth < 520;
final tiles = <Widget>[
_HistoryStatTile(
label: 'Latest',
label: AppLocalizations.of(context)!.latest,
value: _formatMeasurementValue(measurement, history.latest),
),
_HistoryStatTile(
label: 'Min',
label: AppLocalizations.of(context)!.min,
value: _formatMeasurementValue(measurement, history.minimum),
),
_HistoryStatTile(
label: 'Max',
label: AppLocalizations.of(context)!.max,
value: _formatMeasurementValue(measurement, history.maximum),
),
_HistoryStatTile(
label: 'Samples',
label: AppLocalizations.of(context)!.samples,
value: history.values.length.toString(),
),
];

View File

@@ -1140,7 +1140,7 @@ class SensorTelemetryCard extends StatelessWidget {
await Clipboard.setData(ClipboardData(text: rawTelemetry));
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Raw response copied')),
SnackBar(content: Text(AppLocalizations.of(context)!.rawResponseCopied)),
);
}
}
@@ -1173,13 +1173,13 @@ class SensorTelemetryCard extends StatelessWidget {
if (onPing != null)
_SensorSheetAction(
icon: Icons.network_ping,
label: 'Ping',
label: AppLocalizations.of(context)!.ping,
onTap: () => _handleAction(context, 'ping'),
),
if (_rawTelemetryHex(contact?.telemetry) != null)
_SensorSheetAction(
icon: Icons.copy_all_outlined,
label: 'Copy raw response',
label: AppLocalizations.of(context)!.copyRawResponse,
onTap: () => _handleAction(context, 'copy_raw'),
),
if (onCustomize != null)
@@ -1193,7 +1193,7 @@ class SensorTelemetryCard extends StatelessWidget {
supportsBTHomeMetHistory(contact))
_SensorSheetAction(
icon: Icons.show_chart,
label: 'MET history',
label: AppLocalizations.of(context)!.metHistory,
onTap: () => _handleAction(context, 'met_history'),
),
if (onRemove != null)
@@ -1309,10 +1309,10 @@ class SensorTelemetryCard extends StatelessWidget {
tooltip: l10n.refreshing,
),
if (state == SensorRefreshState.success)
const _InlineStateMeta(
_InlineStateMeta(
color: Color(0xFF218B63),
icon: Icons.check_circle,
tooltip: 'Updated',
tooltip: AppLocalizations.of(context)!.updated,
),
if (state == SensorRefreshState.unavailable)
_InlineStateMeta(

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../l10n/app_localizations.dart';
import '../../services/traffic_stats_reporting_service.dart';
class TrafficStatsReportingSection extends StatelessWidget {
@@ -18,7 +19,7 @@ class TrafficStatsReportingSection extends StatelessWidget {
SwitchListTile(
dense: true,
secondary: const Icon(Icons.cloud_upload_outlined, size: 20),
title: const Text('Anonymous RX stats'),
title: Text(AppLocalizations.of(context)!.anonymousRxStats),
subtitle: const Text(
'Upload packet totals every 5 min',
),
@@ -43,7 +44,7 @@ class TrafficStatsReportingSection extends StatelessWidget {
TextButton.icon(
onPressed: _openStatsDashboard,
icon: const Icon(Icons.open_in_new, size: 16),
label: const Text('View'),
label: Text(AppLocalizations.of(context)!.view),
style: TextButton.styleFrom(
visualDensity: VisualDensity.compact,
textStyle: theme.textTheme.labelSmall,

View File

@@ -170,7 +170,7 @@ class UpdateDialog extends StatelessWidget {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error opening download: $e'),
content: Text(AppLocalizations.of(context)!.errorOpeningDownload(e.toString())),
backgroundColor: Colors.red,
),
);