mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-12 00:40:28 +00:00
Refactor code for improved readability and consistency across multiple files
- Updated formatting in `drawing_toolbar.dart` for better readability, including breaking long lines and ensuring consistent indentation. - Enhanced the `recipient_selector_sheet.dart` by adjusting line breaks and improving the layout of text styles. - Refactored `sar_update_sheet.dart` to improve readability, including restructuring widget layouts and ensuring consistent formatting. - Cleaned up imports and removed unnecessary lines in `widget_test.dart`.
This commit is contained in:
@@ -209,7 +209,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.failedToSave(e.toString())),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.failedToSave(e.toString()),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -282,7 +284,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.failedToSave(e.toString())),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.failedToSave(e.toString()),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -365,9 +369,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.failedToGetLocation(e.toString()))));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.failedToGetLocation(e.toString()),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,14 +460,33 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_InfoRow(AppLocalizations.of(context)!.bleName, deviceInfo.deviceName ?? AppLocalizations.of(context)!.unknown),
|
||||
_InfoRow(AppLocalizations.of(context)!.meshName, deviceInfo.selfName ?? AppLocalizations.of(context)!.notSet),
|
||||
_InfoRow(AppLocalizations.of(context)!.type, _getDeviceTypeString(context, deviceInfo.deviceType)),
|
||||
_InfoRow(AppLocalizations.of(context)!.model, deviceInfo.manufacturerModel ?? AppLocalizations.of(context)!.unknown),
|
||||
_InfoRow(AppLocalizations.of(context)!.version, deviceInfo.semanticVersion ?? AppLocalizations.of(context)!.unknown),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.bleName,
|
||||
deviceInfo.deviceName ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.meshName,
|
||||
deviceInfo.selfName ?? AppLocalizations.of(context)!.notSet,
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.type,
|
||||
_getDeviceTypeString(context, deviceInfo.deviceType),
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.model,
|
||||
deviceInfo.manufacturerModel ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.version,
|
||||
deviceInfo.semanticVersion ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.buildDate,
|
||||
deviceInfo.firmwareBuildDate ?? AppLocalizations.of(context)!.unknown,
|
||||
deviceInfo.firmwareBuildDate ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.firmware,
|
||||
@@ -467,11 +494,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.maxContacts,
|
||||
deviceInfo.maxContacts?.toString() ?? AppLocalizations.of(context)!.unknown,
|
||||
deviceInfo.maxContacts?.toString() ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
_InfoRow(
|
||||
AppLocalizations.of(context)!.maxChannels,
|
||||
deviceInfo.maxChannels?.toString() ?? AppLocalizations.of(context)!.unknown,
|
||||
deviceInfo.maxChannels?.toString() ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
_CopyableInfoRow(
|
||||
AppLocalizations.of(context)!.publicKey,
|
||||
@@ -521,7 +550,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.meshNetworkName,
|
||||
border: const OutlineInputBorder(),
|
||||
helperText: AppLocalizations.of(context)!.nameBroadcastInMesh,
|
||||
helperText: AppLocalizations.of(
|
||||
context,
|
||||
)!.nameBroadcastInMesh,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -531,7 +562,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.telemetryAndLocationSharing,
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.telemetryAndLocationSharing,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
@@ -584,7 +617,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
IconButton.filled(
|
||||
onPressed: _useCurrentLocation,
|
||||
icon: const Icon(Icons.my_location, size: 20),
|
||||
tooltip: AppLocalizations.of(context)!.useCurrentLocation,
|
||||
tooltip: AppLocalizations.of(
|
||||
context,
|
||||
)!.useCurrentLocation,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -627,7 +662,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.frequencyMHz,
|
||||
border: const OutlineInputBorder(),
|
||||
helperText: AppLocalizations.of(context)!.frequencyExample,
|
||||
helperText: AppLocalizations.of(
|
||||
context,
|
||||
)!.frequencyExample,
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
@@ -637,7 +674,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
// Bandwidth
|
||||
DropdownButtonFormField<String>(
|
||||
value: _selectedBandwidth,
|
||||
initialValue: _selectedBandwidth,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.bandwidth,
|
||||
border: const OutlineInputBorder(),
|
||||
@@ -660,7 +697,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
// Spreading Factor
|
||||
DropdownButtonFormField<int>(
|
||||
value: _selectedSpreadingFactor,
|
||||
initialValue: _selectedSpreadingFactor,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.spreadingFactor,
|
||||
border: const OutlineInputBorder(),
|
||||
@@ -685,7 +722,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
// Coding Rate
|
||||
DropdownButtonFormField<int>(
|
||||
value: _selectedCodingRate,
|
||||
initialValue: _selectedCodingRate,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.codingRate,
|
||||
border: const OutlineInputBorder(),
|
||||
@@ -714,7 +751,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.txPowerDbm,
|
||||
border: const OutlineInputBorder(),
|
||||
helperText: AppLocalizations.of(context)!.maxPowerDbm(deviceInfo.maxTxPower ?? 22),
|
||||
helperText: AppLocalizations.of(
|
||||
context,
|
||||
)!.maxPowerDbm(deviceInfo.maxTxPower ?? 22),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
@@ -815,7 +854,11 @@ class _CopyableInfoRow extends StatelessWidget {
|
||||
Clipboard.setData(ClipboardData(text: value));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.copiedToClipboardShort(label)),
|
||||
content: Text(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.copiedToClipboardShort(label),
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
|
||||
@@ -426,7 +426,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${rssi} dBm',
|
||||
'$rssi dBm',
|
||||
style: TextStyle(
|
||||
color: signalColor,
|
||||
fontSize: 12,
|
||||
@@ -789,8 +789,9 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
PacketLogScreen(bleService: provider.bleService),
|
||||
builder: (context) => PacketLogScreen(
|
||||
bleService: provider.bleService,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -109,7 +109,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_statusMessage = AppLocalizations.of(context)!.errorLoadingStats(e.toString());
|
||||
_statusMessage = AppLocalizations.of(
|
||||
context,
|
||||
)!.errorLoadingStats(e.toString());
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -153,7 +155,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.mbtilesImportedSuccessfully),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.mbtilesImportedSuccessfully,
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -174,7 +178,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.deleteMbtilesConfirmTitle),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.deleteMbtilesConfirmMessage(metadata.name),
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.deleteMbtilesConfirmMessage(metadata.name),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
@@ -205,7 +211,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.mbtilesDeletedSuccessfully),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.mbtilesDeletedSuccessfully,
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -246,13 +254,21 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
// Validate zoom levels
|
||||
final minZoomResult = validator.validateZoomLevel(_minZoom);
|
||||
if (!minZoomResult.isValid) {
|
||||
_showError(AppLocalizations.of(context)!.minZoomError(minZoomResult.errorMessage!));
|
||||
_showError(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.minZoomError(minZoomResult.errorMessage!),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final maxZoomResult = validator.validateZoomLevel(_maxZoom);
|
||||
if (!maxZoomResult.isValid) {
|
||||
_showError(AppLocalizations.of(context)!.maxZoomError(maxZoomResult.errorMessage!));
|
||||
_showError(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.maxZoomError(maxZoomResult.errorMessage!),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -261,10 +277,7 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
final bounds = LatLngBounds(
|
||||
LatLng(south!, west!),
|
||||
LatLng(north!, east!),
|
||||
);
|
||||
final bounds = LatLngBounds(LatLng(south!, west!), LatLng(north!, east!));
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
@@ -291,7 +304,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDownloading = false;
|
||||
_statusMessage = AppLocalizations.of(context)!.downloadCompletedSuccessfully;
|
||||
_statusMessage = AppLocalizations.of(
|
||||
context,
|
||||
)!.downloadCompletedSuccessfully;
|
||||
});
|
||||
|
||||
await _loadCacheStats();
|
||||
@@ -308,7 +323,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDownloading = false;
|
||||
_statusMessage = AppLocalizations.of(context)!.downloadFailed(e.toString());
|
||||
_statusMessage = AppLocalizations.of(
|
||||
context,
|
||||
)!.downloadFailed(e.toString());
|
||||
});
|
||||
_showError(AppLocalizations.of(context)!.downloadFailed(e.toString()));
|
||||
}
|
||||
@@ -317,7 +334,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
Future<void> _cancelDownload() async {
|
||||
try {
|
||||
if (!mounted) return;
|
||||
setState(() => _statusMessage = AppLocalizations.of(context)!.cancellingDownload);
|
||||
setState(
|
||||
() => _statusMessage = AppLocalizations.of(context)!.cancellingDownload,
|
||||
);
|
||||
|
||||
await widget.tileCacheService.cancelDownload();
|
||||
|
||||
@@ -341,7 +360,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDownloading = false;
|
||||
_statusMessage = AppLocalizations.of(context)!.cancelFailed(e.toString());
|
||||
_statusMessage = AppLocalizations.of(
|
||||
context,
|
||||
)!.cancelFailed(e.toString());
|
||||
});
|
||||
_showError(AppLocalizations.of(context)!.cancelFailed(e.toString()));
|
||||
}
|
||||
@@ -352,9 +373,7 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.clearMapsConfirmTitle),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.clearMapsConfirmMessage,
|
||||
),
|
||||
content: Text(AppLocalizations.of(context)!.clearMapsConfirmMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
@@ -382,7 +401,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.cacheClearedSuccessfully),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.cacheClearedSuccessfully,
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -410,10 +431,13 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
|
||||
// Export to temporary directory first (works on all platforms)
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final fileName = 'meshcore_tiles_${DateTime.now().millisecondsSinceEpoch}.fmtc';
|
||||
final fileName =
|
||||
'meshcore_tiles_${DateTime.now().millisecondsSinceEpoch}.fmtc';
|
||||
final tempFilePath = '${tempDir.path}/$fileName';
|
||||
|
||||
final exportedCount = await widget.tileCacheService.exportStore(tempFilePath);
|
||||
final exportedCount = await widget.tileCacheService.exportStore(
|
||||
tempFilePath,
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
@@ -443,7 +467,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
if (result.status == ShareResultStatus.success) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.exportSuccess(exportedCount)),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.exportSuccess(exportedCount),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -484,7 +510,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
|
||||
// Optional: Preview stores in archive before importing
|
||||
try {
|
||||
final stores = await widget.tileCacheService.listArchiveStores(filePath);
|
||||
final stores = await widget.tileCacheService.listArchiveStores(
|
||||
filePath,
|
||||
);
|
||||
debugPrint('Archive contains stores: $stores');
|
||||
} catch (e) {
|
||||
debugPrint('Could not list stores: $e');
|
||||
@@ -504,7 +532,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.importSuccess(importResult['successfulStores'] as int),
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.importSuccess(importResult['successfulStores'] as int),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
@@ -535,9 +565,7 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.mapManagement),
|
||||
),
|
||||
appBar: AppBar(title: Text(AppLocalizations.of(context)!.mapManagement)),
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: SingleChildScrollView(
|
||||
@@ -622,7 +650,10 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
Icon(icon, size: 20, color: Colors.grey[600]),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(label, style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
Text(value, style: TextStyle(color: Colors.grey[600])),
|
||||
],
|
||||
@@ -667,7 +698,11 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(Icons.map_outlined, size: 48, color: Colors.grey[400]),
|
||||
Icon(
|
||||
Icons.map_outlined,
|
||||
size: 48,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.noMbtilesFiles,
|
||||
@@ -678,74 +713,79 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
),
|
||||
)
|
||||
else
|
||||
..._mbtilesFiles.map((metadata) => Card(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: ExpansionTile(
|
||||
leading: Icon(
|
||||
metadata.isVector ? Icons.layers : Icons.image,
|
||||
color: metadata.isVector ? Colors.blue : Colors.orange,
|
||||
),
|
||||
title: Text(
|
||||
metadata.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Text(
|
||||
'${metadata.fileSizeFormatted} • ${metadata.format?.toUpperCase() ?? "Unknown"}',
|
||||
),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (metadata.description != null) ...[
|
||||
Text(
|
||||
metadata.description!,
|
||||
style: TextStyle(color: Colors.grey[700]),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.zoomLevels,
|
||||
'${metadata.minZoom ?? "?"} - ${metadata.maxZoom ?? "?"}',
|
||||
..._mbtilesFiles.map(
|
||||
(metadata) => Card(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: ExpansionTile(
|
||||
leading: Icon(
|
||||
metadata.isVector ? Icons.layers : Icons.image,
|
||||
color: metadata.isVector ? Colors.blue : Colors.orange,
|
||||
),
|
||||
title: Text(
|
||||
metadata.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Text(
|
||||
'${metadata.fileSizeFormatted} • ${metadata.format?.toUpperCase() ?? "Unknown"}',
|
||||
),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (metadata.description != null) ...[
|
||||
Text(
|
||||
metadata.description!,
|
||||
style: TextStyle(color: Colors.grey[700]),
|
||||
),
|
||||
if (metadata.bounds != null)
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.bounds,
|
||||
metadata.bounds!,
|
||||
),
|
||||
if (metadata.isVector) ...[
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.type,
|
||||
AppLocalizations.of(context)!.vectorTiles,
|
||||
),
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.schema,
|
||||
_mbtilesService.getVectorSchema(metadata) ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => _deleteMbtilesFile(metadata),
|
||||
icon: const Icon(Icons.delete, color: Colors.red),
|
||||
label: Text(
|
||||
AppLocalizations.of(context)!.delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.zoomLevels,
|
||||
'${metadata.minZoom ?? "?"} - ${metadata.maxZoom ?? "?"}',
|
||||
),
|
||||
if (metadata.bounds != null)
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.bounds,
|
||||
metadata.bounds!,
|
||||
),
|
||||
if (metadata.isVector) ...[
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.type,
|
||||
AppLocalizations.of(context)!.vectorTiles,
|
||||
),
|
||||
_buildInfoRow(
|
||||
AppLocalizations.of(context)!.schema,
|
||||
_mbtilesService.getVectorSchema(metadata) ??
|
||||
AppLocalizations.of(context)!.unknown,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => _deleteMbtilesFile(metadata),
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
),
|
||||
label: Text(
|
||||
AppLocalizations.of(context)!.delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -840,10 +880,7 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: TextStyle(color: Colors.grey[800]),
|
||||
),
|
||||
child: Text(value, style: TextStyle(color: Colors.grey[800])),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -865,7 +902,7 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
|
||||
// Map Layer Selection
|
||||
DropdownButtonFormField<MapLayer>(
|
||||
value: _selectedLayer,
|
||||
initialValue: _selectedLayer,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.mapLayer,
|
||||
border: const OutlineInputBorder(),
|
||||
@@ -876,11 +913,13 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
child: Text(layer.getLocalizedName(context)),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: _isDownloading ? null : (layer) {
|
||||
if (layer != null) {
|
||||
setState(() => _selectedLayer = layer);
|
||||
}
|
||||
},
|
||||
onChanged: _isDownloading
|
||||
? null
|
||||
: (layer) {
|
||||
if (layer != null) {
|
||||
setState(() => _selectedLayer = layer);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -900,7 +939,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: '46.1',
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
enabled: !_isDownloading,
|
||||
),
|
||||
),
|
||||
@@ -913,7 +954,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: '46.0',
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
enabled: !_isDownloading,
|
||||
),
|
||||
),
|
||||
@@ -930,7 +973,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: '14.6',
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
enabled: !_isDownloading,
|
||||
),
|
||||
),
|
||||
@@ -943,7 +988,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: '14.4',
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
),
|
||||
enabled: !_isDownloading,
|
||||
),
|
||||
),
|
||||
@@ -970,14 +1017,16 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
max: 19,
|
||||
divisions: 18,
|
||||
label: '$_minZoom',
|
||||
onChanged: _isDownloading ? null : (value) {
|
||||
setState(() {
|
||||
_minZoom = value.toInt();
|
||||
if (_minZoom > _maxZoom) {
|
||||
_maxZoom = _minZoom;
|
||||
}
|
||||
});
|
||||
},
|
||||
onChanged: _isDownloading
|
||||
? null
|
||||
: (value) {
|
||||
setState(() {
|
||||
_minZoom = value.toInt();
|
||||
if (_minZoom > _maxZoom) {
|
||||
_maxZoom = _minZoom;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -994,14 +1043,16 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
max: 19,
|
||||
divisions: 18,
|
||||
label: '$_maxZoom',
|
||||
onChanged: _isDownloading ? null : (value) {
|
||||
setState(() {
|
||||
_maxZoom = value.toInt();
|
||||
if (_maxZoom < _minZoom) {
|
||||
_minZoom = _maxZoom;
|
||||
}
|
||||
});
|
||||
},
|
||||
onChanged: _isDownloading
|
||||
? null
|
||||
: (value) {
|
||||
setState(() {
|
||||
_maxZoom = value.toInt();
|
||||
if (_maxZoom < _minZoom) {
|
||||
_minZoom = _maxZoom;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -1018,7 +1069,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
@@ -1029,10 +1082,13 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
_statusMessage ?? AppLocalizations.of(context)!.downloadingDots,
|
||||
_statusMessage ??
|
||||
AppLocalizations.of(context)!.downloadingDots,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -1043,7 +1099,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1054,7 +1112,9 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
child: LinearProgressIndicator(
|
||||
value: _downloadProgress / 100,
|
||||
minHeight: 8,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withOpacity(0.2),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
|
||||
@@ -35,7 +35,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
String? _highlightedMessageId;
|
||||
|
||||
// Message destination state
|
||||
String _destinationType = MessageDestinationPreferences.destinationTypeChannel;
|
||||
String _destinationType =
|
||||
MessageDestinationPreferences.destinationTypeChannel;
|
||||
Contact? _selectedRecipient;
|
||||
|
||||
/// Helper method to compare two public keys for equality
|
||||
@@ -129,7 +130,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
|
||||
/// Load saved message destination from preferences
|
||||
Future<void> _loadSavedDestination() async {
|
||||
final savedDestination = await MessageDestinationPreferences.getDestination();
|
||||
final savedDestination =
|
||||
await MessageDestinationPreferences.getDestination();
|
||||
|
||||
if (savedDestination == null || !mounted) {
|
||||
// Default to public channel
|
||||
@@ -160,7 +162,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
'⚠️ [MessagesTab] Saved recipient not found, falling back to public channel',
|
||||
);
|
||||
setState(() {
|
||||
_destinationType = MessageDestinationPreferences.destinationTypeChannel;
|
||||
_destinationType =
|
||||
MessageDestinationPreferences.destinationTypeChannel;
|
||||
_selectedRecipient = null;
|
||||
});
|
||||
await MessageDestinationPreferences.clearDestination();
|
||||
@@ -197,7 +200,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
/// Handle recipient selection
|
||||
Future<void> _onRecipientSelected(String type, Contact? recipient) async {
|
||||
// Get display name before async gap
|
||||
final recipientName = type == MessageDestinationPreferences.destinationTypeChannel
|
||||
final recipientName =
|
||||
type == MessageDestinationPreferences.destinationTypeChannel
|
||||
? AppLocalizations.of(context)!.publicChannel
|
||||
: (recipient?.displayName ?? recipient?.advName ?? 'Unknown');
|
||||
|
||||
@@ -214,17 +218,16 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
|
||||
// Show confirmation toast
|
||||
if (!mounted) return;
|
||||
ToastLogger.success(
|
||||
context,
|
||||
'Messages will be sent to: $recipientName',
|
||||
);
|
||||
ToastLogger.success(context, 'Messages will be sent to: $recipientName');
|
||||
}
|
||||
|
||||
/// Get icon for current destination type
|
||||
IconData _getDestinationIcon() {
|
||||
if (_destinationType == MessageDestinationPreferences.destinationTypeChannel) {
|
||||
if (_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeChannel) {
|
||||
return Icons.public;
|
||||
} else if (_destinationType == MessageDestinationPreferences.destinationTypeRoom) {
|
||||
} else if (_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeRoom) {
|
||||
return Icons.meeting_room;
|
||||
} else {
|
||||
return Icons.person;
|
||||
@@ -234,10 +237,12 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
/// Get tooltip for destination button
|
||||
String _getDestinationTooltip() {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
if (_destinationType == MessageDestinationPreferences.destinationTypeChannel) {
|
||||
if (_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeChannel) {
|
||||
return '${l10n.publicChannel} (tap to change)';
|
||||
} else if (_selectedRecipient != null) {
|
||||
final recipientName = _selectedRecipient!.displayName ?? _selectedRecipient!.advName;
|
||||
final recipientName =
|
||||
_selectedRecipient!.displayName ?? _selectedRecipient!.advName;
|
||||
return '$recipientName (tap to change)';
|
||||
}
|
||||
return 'Select recipient';
|
||||
@@ -259,7 +264,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
|
||||
try {
|
||||
// Check destination type and send accordingly
|
||||
if (_destinationType == MessageDestinationPreferences.destinationTypeChannel) {
|
||||
if (_destinationType ==
|
||||
MessageDestinationPreferences.destinationTypeChannel) {
|
||||
// Send to public channel
|
||||
await _sendToChannel(text, connectionProvider, messagesProvider);
|
||||
} else if (_selectedRecipient != null) {
|
||||
@@ -272,7 +278,9 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
);
|
||||
} else {
|
||||
// Fallback to public channel if no recipient selected
|
||||
debugPrint('⚠️ [MessagesTab] No recipient selected, falling back to channel');
|
||||
debugPrint(
|
||||
'⚠️ [MessagesTab] No recipient selected, falling back to channel',
|
||||
);
|
||||
await _sendToChannel(text, connectionProvider, messagesProvider);
|
||||
}
|
||||
|
||||
@@ -422,7 +430,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
|
||||
if (sendToChannel) {
|
||||
// Create message ID
|
||||
final messageId = '${DateTime.now().millisecondsSinceEpoch}_channel_sent';
|
||||
final messageId =
|
||||
'${DateTime.now().millisecondsSinceEpoch}_channel_sent';
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
|
||||
// Get current device's public key (first 6 bytes)
|
||||
@@ -455,10 +464,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
ToastLogger.success(
|
||||
context,
|
||||
'SAR marker broadcast to public channel',
|
||||
);
|
||||
ToastLogger.success(context, 'SAR marker broadcast to public channel');
|
||||
} else {
|
||||
// Create message ID
|
||||
final messageId = '${DateTime.now().millisecondsSinceEpoch}_sent';
|
||||
@@ -490,7 +496,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
final contactsProvider = context.read<ContactsProvider>();
|
||||
final roomContact = contactsProvider.contacts.where((c) {
|
||||
return c.publicKey.length >= roomPublicKey!.length &&
|
||||
_publicKeysMatch(c.publicKey, roomPublicKey!);
|
||||
_publicKeysMatch(c.publicKey, roomPublicKey);
|
||||
}).firstOrNull;
|
||||
|
||||
// Send SAR message to selected room (persisted and immutable)
|
||||
@@ -507,10 +513,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
ToastLogger.success(
|
||||
context,
|
||||
'SAR marker sent to room',
|
||||
);
|
||||
ToastLogger.success(context, 'SAR marker sent to room');
|
||||
}
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
@@ -615,7 +618,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
itemCount: messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
final message = messages[index];
|
||||
final isHighlighted = message.id == _highlightedMessageId;
|
||||
final isHighlighted =
|
||||
message.id == _highlightedMessageId;
|
||||
|
||||
// Display system messages with minimal styling
|
||||
if (message.isSystemMessage) {
|
||||
@@ -680,10 +684,18 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
tooltip: _getDestinationTooltip(),
|
||||
onPressed: _showRecipientSelector,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: _destinationType == MessageDestinationPreferences.destinationTypeChannel
|
||||
? Theme.of(context).colorScheme.surfaceContainerHighest
|
||||
backgroundColor:
|
||||
_destinationType ==
|
||||
MessageDestinationPreferences
|
||||
.destinationTypeChannel
|
||||
? Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerHighest
|
||||
: Theme.of(context).colorScheme.secondaryContainer,
|
||||
foregroundColor: _destinationType == MessageDestinationPreferences.destinationTypeChannel
|
||||
foregroundColor:
|
||||
_destinationType ==
|
||||
MessageDestinationPreferences
|
||||
.destinationTypeChannel
|
||||
? Theme.of(context).colorScheme.onSurface
|
||||
: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
),
|
||||
@@ -699,9 +711,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
maxLengthEnforcement: MaxLengthEnforcement.enforced,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
decoration: InputDecoration(
|
||||
hintText: AppLocalizations.of(
|
||||
context,
|
||||
)!.typeYourMessage,
|
||||
hintText: AppLocalizations.of(context)!.typeYourMessage,
|
||||
hintStyle: const TextStyle(fontSize: 14),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
@@ -718,9 +728,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
fontSize: 10,
|
||||
color: _characterCount > _maxCharacters * 0.9
|
||||
? Colors.orange
|
||||
: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall?.color,
|
||||
: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
@@ -885,7 +893,10 @@ class _MessageBubble extends StatelessWidget {
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: message.text));
|
||||
Navigator.pop(context);
|
||||
ToastLogger.success(context, AppLocalizations.of(context)!.textCopiedToClipboard);
|
||||
ToastLogger.success(
|
||||
context,
|
||||
AppLocalizations.of(context)!.textCopiedToClipboard,
|
||||
);
|
||||
},
|
||||
),
|
||||
// Delete message option
|
||||
@@ -1100,8 +1111,8 @@ class _MessageBubble extends StatelessWidget {
|
||||
color: isHighlighted
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: isSarMarker
|
||||
? _getSarMarkerColor(context, isDarkMode)
|
||||
: _getMessageBubbleColor(context, isOwnMessage, isDarkMode),
|
||||
? _getSarMarkerColor(context, isDarkMode)
|
||||
: _getMessageBubbleColor(context, isOwnMessage, isDarkMode),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: isHighlighted
|
||||
? Border.all(
|
||||
@@ -1109,43 +1120,45 @@ class _MessageBubble extends StatelessWidget {
|
||||
width: 3,
|
||||
)
|
||||
: isSarMarker
|
||||
? Border.all(
|
||||
color: _getSarMarkerBorderColor(context, isDarkMode),
|
||||
width: 2,
|
||||
)
|
||||
: isOwnMessage
|
||||
? Border.all(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.3),
|
||||
width: 1.5,
|
||||
)
|
||||
: !message.isRead &&
|
||||
!message.isSentMessage &&
|
||||
!message.isSystemMessage
|
||||
? Border.all(color: Colors.blue, width: 1.5)
|
||||
: null,
|
||||
? Border.all(
|
||||
color: _getSarMarkerBorderColor(context, isDarkMode),
|
||||
width: 2,
|
||||
)
|
||||
: isOwnMessage
|
||||
? Border.all(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.3),
|
||||
width: 1.5,
|
||||
)
|
||||
: !message.isRead &&
|
||||
!message.isSentMessage &&
|
||||
!message.isSystemMessage
|
||||
? Border.all(color: Colors.blue, width: 1.5)
|
||||
: null,
|
||||
boxShadow: isHighlighted
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.5),
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withValues(alpha: 0.5),
|
||||
blurRadius: 12,
|
||||
spreadRadius: 2,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: isSarMarker
|
||||
? [
|
||||
BoxShadow(
|
||||
color: _getSarMarkerBorderColor(
|
||||
context,
|
||||
isDarkMode,
|
||||
).withValues(alpha: 0.3),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
? [
|
||||
BoxShadow(
|
||||
color: _getSarMarkerBorderColor(
|
||||
context,
|
||||
isDarkMode,
|
||||
).withValues(alpha: 0.3),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -1272,9 +1285,12 @@ class _MessageBubble extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
// Show template name (sarNotes) if available, otherwise show localized type name
|
||||
message.sarNotes != null && message.sarNotes!.isNotEmpty
|
||||
message.sarNotes != null &&
|
||||
message.sarNotes!.isNotEmpty
|
||||
? message.sarNotes!
|
||||
: message.sarMarkerType!.getLocalizedName(context),
|
||||
: message.sarMarkerType!.getLocalizedName(
|
||||
context,
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleSmall
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
@@ -1411,7 +1427,7 @@ class _MessageBubble extends StatelessWidget {
|
||||
).colorScheme.primaryContainer.withValues(alpha: 0.15);
|
||||
} else {
|
||||
// Others' messages: default surface color
|
||||
return Theme.of(context).colorScheme.surfaceVariant;
|
||||
return Theme.of(context).colorScheme.surfaceContainerHighest;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,10 +86,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
_locationService.onError = (error) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(error),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
SnackBar(content: Text(error), backgroundColor: Colors.orange),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -119,7 +116,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
// Load settings and restore tracking state
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final wasTracking = prefs.getBool('background_tracking_enabled') ?? false;
|
||||
final wasTracking =
|
||||
prefs.getBool('background_tracking_enabled') ?? false;
|
||||
|
||||
if (wasTracking) {
|
||||
await _startBackgroundTracking();
|
||||
@@ -239,7 +237,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.failedToLoadSampleData(e.toString())),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.failedToLoadSampleData(e.toString()),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -296,7 +296,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// Permission granted
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.locationPermissionGranted),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.locationPermissionGranted,
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -305,7 +307,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// Permission denied
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.locationPermissionRequiredForGps),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.locationPermissionRequiredForGps,
|
||||
),
|
||||
backgroundColor: Colors.orange,
|
||||
duration: const Duration(seconds: 4),
|
||||
),
|
||||
@@ -316,7 +320,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.locationPermissionAlreadyGranted),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.locationPermissionAlreadyGranted,
|
||||
),
|
||||
backgroundColor: Colors.blue,
|
||||
),
|
||||
);
|
||||
@@ -325,10 +331,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('Error: $e'), backgroundColor: Colors.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -366,9 +369,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.clearAllDataConfirmTitle),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.clearAllDataConfirmMessage,
|
||||
),
|
||||
content: Text(AppLocalizations.of(context)!.clearAllDataConfirmMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
@@ -438,7 +439,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
builder: (context, appProvider, child) => SwitchListTile(
|
||||
secondary: const Icon(Icons.visibility_off),
|
||||
title: Text(AppLocalizations.of(context)!.simpleMode),
|
||||
subtitle: Text(AppLocalizations.of(context)!.simpleModeDescription),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.simpleModeDescription,
|
||||
),
|
||||
value: appProvider.isSimpleMode,
|
||||
onChanged: (value) async {
|
||||
await appProvider.toggleSimpleMode(value);
|
||||
@@ -485,19 +488,27 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
switch (permission) {
|
||||
case LocationPermission.always:
|
||||
statusText = AppLocalizations.of(context)!.locationPermissionGrantedAlways;
|
||||
statusText = AppLocalizations.of(
|
||||
context,
|
||||
)!.locationPermissionGrantedAlways;
|
||||
statusColor = Colors.green;
|
||||
break;
|
||||
case LocationPermission.whileInUse:
|
||||
statusText = AppLocalizations.of(context)!.locationPermissionGrantedWhileInUse;
|
||||
statusText = AppLocalizations.of(
|
||||
context,
|
||||
)!.locationPermissionGrantedWhileInUse;
|
||||
statusColor = Colors.green;
|
||||
break;
|
||||
case LocationPermission.denied:
|
||||
statusText = AppLocalizations.of(context)!.locationPermissionDeniedTapToRequest;
|
||||
statusText = AppLocalizations.of(
|
||||
context,
|
||||
)!.locationPermissionDeniedTapToRequest;
|
||||
statusColor = Colors.orange;
|
||||
break;
|
||||
case LocationPermission.deniedForever:
|
||||
statusText = AppLocalizations.of(context)!.locationPermissionPermanentlyDeniedOpenSettings;
|
||||
statusText = AppLocalizations.of(
|
||||
context,
|
||||
)!.locationPermissionPermanentlyDeniedOpenSettings;
|
||||
statusColor = Colors.red;
|
||||
break;
|
||||
default:
|
||||
@@ -505,10 +516,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
statusColor = Colors.grey;
|
||||
}
|
||||
|
||||
return Text(
|
||||
statusText,
|
||||
style: TextStyle(color: statusColor),
|
||||
);
|
||||
return Text(statusText, style: TextStyle(color: statusColor));
|
||||
},
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
@@ -517,13 +525,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
const Divider(),
|
||||
|
||||
// Location Settings Section
|
||||
_buildSectionHeader(AppLocalizations.of(context)!.locationBroadcasting),
|
||||
_buildSectionHeader(
|
||||
AppLocalizations.of(context)!.locationBroadcasting,
|
||||
),
|
||||
|
||||
// Automatic tracking settings
|
||||
SwitchListTile(
|
||||
secondary: const Icon(Icons.location_on),
|
||||
title: Text(AppLocalizations.of(context)!.autoLocationTracking),
|
||||
subtitle: Text(AppLocalizations.of(context)!.automaticallyBroadcastPosition),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.automaticallyBroadcastPosition,
|
||||
),
|
||||
value: _locationService.isTracking,
|
||||
onChanged: (value) {
|
||||
if (value) {
|
||||
@@ -538,7 +550,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.tune),
|
||||
title: Text(AppLocalizations.of(context)!.configureTracking),
|
||||
subtitle: Text(AppLocalizations.of(context)!.distanceAndTimeThresholds),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.distanceAndTimeThresholds,
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _showTrackingConfigDialog(),
|
||||
),
|
||||
@@ -654,7 +668,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setDialogState) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.locationTrackingConfiguration),
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.locationTrackingConfiguration,
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -663,29 +679,31 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// Description
|
||||
Text(
|
||||
AppLocalizations.of(context)!.configureWhenLocationBroadcasts,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall?.copyWith(color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Minimum Distance
|
||||
Text(
|
||||
AppLocalizations.of(context)!.minimumDistance,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.broadcastAfterMoving(tempMinDistance.toStringAsFixed(0)),
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.broadcastAfterMoving(tempMinDistance.toStringAsFixed(0)),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
),
|
||||
data: SliderTheme.of(
|
||||
context,
|
||||
).copyWith(showValueIndicator: ShowValueIndicator.onDrag),
|
||||
child: Slider(
|
||||
value: tempMinDistance,
|
||||
min: 1,
|
||||
@@ -718,20 +736,22 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// Maximum Distance
|
||||
Text(
|
||||
AppLocalizations.of(context)!.maximumDistance,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.alwaysBroadcastAfterMoving(tempMaxDistance.toStringAsFixed(0)),
|
||||
AppLocalizations.of(context)!.alwaysBroadcastAfterMoving(
|
||||
tempMaxDistance.toStringAsFixed(0),
|
||||
),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
),
|
||||
data: SliderTheme.of(
|
||||
context,
|
||||
).copyWith(showValueIndicator: ShowValueIndicator.onDrag),
|
||||
child: Slider(
|
||||
value: tempMaxDistance,
|
||||
min: tempMinDistance,
|
||||
@@ -750,9 +770,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('${tempMinDistance.toStringAsFixed(0)}m',
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
Text('500m', style: Theme.of(context).textTheme.bodySmall),
|
||||
Text(
|
||||
'${tempMinDistance.toStringAsFixed(0)}m',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
Text(
|
||||
'500m',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -761,20 +786,22 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// Minimum Time Interval
|
||||
Text(
|
||||
AppLocalizations.of(context)!.minimumTimeInterval,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.alwaysBroadcastEvery(_formatDuration(tempTimeInterval)),
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
)!.alwaysBroadcastEvery(_formatDuration(tempTimeInterval)),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
),
|
||||
data: SliderTheme.of(
|
||||
context,
|
||||
).copyWith(showValueIndicator: ShowValueIndicator.onDrag),
|
||||
child: Slider(
|
||||
value: tempTimeInterval.toDouble(),
|
||||
min: 10,
|
||||
@@ -794,7 +821,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('10s', style: Theme.of(context).textTheme.bodySmall),
|
||||
Text('10min', style: Theme.of(context).textTheme.bodySmall),
|
||||
Text(
|
||||
'10min',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -819,7 +849,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
// Update tracking if active
|
||||
if (_locationService.isTracking) {
|
||||
await _locationService.updateDistanceThreshold(tempMinDistance);
|
||||
await _locationService.updateDistanceThreshold(
|
||||
tempMinDistance,
|
||||
);
|
||||
}
|
||||
|
||||
// Close dialog before setState
|
||||
@@ -897,7 +929,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Text(AppLocalizations.of(context)!.alertEmergencyMode),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.alertEmergencyMode,
|
||||
),
|
||||
value: AppThemeMode.sarRed,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -945,7 +979,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Text(AppLocalizations.of(context)!.sarNavyBlueDescription),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.sarNavyBlueDescription,
|
||||
),
|
||||
value: AppThemeMode.sarNavyBlue,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -1043,9 +1079,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.aboutDescription,
|
||||
),
|
||||
Text(AppLocalizations.of(context)!.aboutDescription),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.technologiesUsed,
|
||||
@@ -1054,9 +1088,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.technologiesList,
|
||||
),
|
||||
Text(AppLocalizations.of(context)!.technologiesList),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user