Move settings around part 2

This commit is contained in:
stopflock
2025-09-29 23:56:28 -05:00
parent 82501a3131
commit b3a87fc56a
11 changed files with 178 additions and 39 deletions

View File

@@ -39,7 +39,7 @@ const String kClientName = 'DeFlock';
const String kClientVersion = '0.9.13';
// Development/testing features - set to false for production builds
const bool kEnableDevelopmentModes = false; // Set to false to hide sandbox/simulate modes and force production mode
const bool kEnableDevelopmentModes = true; // Set to false to hide sandbox/simulate modes and force production mode
// Marker/node interaction
const int kCameraMinZoomLevel = 10; // Minimum zoom to show nodes (Overpass)

View File

@@ -42,7 +42,8 @@
"offlineSettings": "Offline Settings",
"offlineSettingsSubtitle": "Manage offline mode and downloaded areas",
"advancedSettings": "Advanced Settings",
"advancedSettingsSubtitle": "Performance and tile provider settings"
"advancedSettingsSubtitle": "Performance, alerts, and tile provider settings",
"proximityAlerts": "Proximity Alerts"
},
"node": {
"title": "Node #{}",

View File

@@ -42,7 +42,8 @@
"offlineSettings": "Configuración Sin Conexión",
"offlineSettingsSubtitle": "Gestionar modo sin conexión y áreas descargadas",
"advancedSettings": "Configuración Avanzada",
"advancedSettingsSubtitle": "Configuración de rendimiento y proveedores de teselas"
"advancedSettingsSubtitle": "Configuración de rendimiento, alertas y proveedores de teselas",
"proximityAlerts": "Alertas de Proximidad"
},
"node": {
"title": "Nodo #{}",

View File

@@ -42,7 +42,8 @@
"offlineSettings": "Paramètres Hors Ligne",
"offlineSettingsSubtitle": "Gérer le mode hors ligne et les zones téléchargées",
"advancedSettings": "Paramètres Avancés",
"advancedSettingsSubtitle": "Paramètres de performance et de fournisseurs de tuiles"
"advancedSettingsSubtitle": "Paramètres de performance, alertes et fournisseurs de tuiles",
"proximityAlerts": "Alertes de Proximité"
},
"node": {
"title": "Nœud #{}",

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'settings_screen_sections/max_nodes_section.dart';
import 'settings_screen_sections/proximity_alerts_section.dart';
import 'settings_screen_sections/tile_provider_section.dart';
import '../services/localization_service.dart';
@@ -21,6 +22,8 @@ class AdvancedSettingsScreen extends StatelessWidget {
children: const [
MaxNodesSection(),
Divider(),
ProximityAlertsSection(),
Divider(),
TileProviderSection(),
],
),

View File

@@ -3,7 +3,6 @@ import 'settings_screen_sections/auth_section.dart';
import 'settings_screen_sections/upload_mode_section.dart';
import 'settings_screen_sections/queue_section.dart';
import 'settings_screen_sections/about_section.dart';
import 'settings_screen_sections/proximity_alerts_section.dart';
import 'settings_screen_sections/language_section.dart';
import '../services/localization_service.dart';
import '../dev_config.dart';
@@ -31,8 +30,6 @@ class SettingsScreen extends StatelessWidget {
const Divider(),
const QueueSection(),
const Divider(),
const ProximityAlertsSection(),
const Divider(),
// Navigation to sub-pages
_buildNavigationTile(

View File

@@ -57,15 +57,11 @@ class _LanguageSectionState extends State<LanguageSection> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text(
locService.t('settings.language'),
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
),
),
Text(
locService.t('settings.language'),
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
// System Default option
RadioListTile<String?>(
title: Text(locService.t('settings.systemDefault')),

View File

@@ -22,7 +22,10 @@ class OperatorProfileListSection extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(locService.t('operatorProfiles.title'), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
Text(
locService.t('operatorProfiles.title'),
style: Theme.of(context).textTheme.titleMedium,
),
TextButton.icon(
onPressed: () => Navigator.push(
context,

View File

@@ -22,7 +22,10 @@ class ProfileListSection extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(locService.t('profiles.nodeProfiles'), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
Text(
locService.t('profiles.nodeProfiles'),
style: Theme.of(context).textTheme.titleMedium,
),
TextButton.icon(
onPressed: () => Navigator.push(
context,

View File

@@ -82,14 +82,14 @@ class _ProximityAlertsSectionState extends State<ProximityAlertsSection> {
Widget build(BuildContext context) {
return Consumer<AppState>(
builder: (context, appState, child) {
final locService = LocalizationService.instance;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Proximity Alerts',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
locService.t('settings.proximityAlerts'),
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),

View File

@@ -4,7 +4,7 @@ import 'package:provider/provider.dart';
import '../../app_state.dart';
import '../../models/tile_provider.dart';
import '../../services/localization_service.dart';
import '../tile_provider_management_screen.dart';
import '../tile_provider_editor_screen.dart';
class TileProviderSection extends StatelessWidget {
const TileProviderSection({super.key});
@@ -15,32 +15,166 @@ class TileProviderSection extends StatelessWidget {
animation: LocalizationService.instance,
builder: (context, child) {
final locService = LocalizationService.instance;
final appState = context.watch<AppState>();
final providers = appState.tileProviders;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
locService.t('mapTiles.title'),
style: Theme.of(context).textTheme.titleMedium,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
locService.t('mapTiles.title'),
style: Theme.of(context).textTheme.titleMedium,
),
TextButton.icon(
onPressed: () => _addProvider(context),
icon: const Icon(Icons.add),
label: Text(locService.t('tileProviders.addProvider')),
),
],
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const TileProviderManagementScreen(),
),
);
},
icon: const Icon(Icons.settings),
label: Text(locService.t('mapTiles.manageProviders')),
),
),
if (providers.isEmpty)
Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(locService.t('tileProviders.noProvidersConfigured')),
),
)
else
...providers.map((provider) => _buildProviderTile(context, provider, appState)).toList(),
],
);
},
);
}
Widget _buildProviderTile(BuildContext context, TileProvider provider, AppState appState) {
final locService = LocalizationService.instance;
final isSelected = appState.selectedTileProvider?.id == provider.id;
return Card(
margin: const EdgeInsets.symmetric(vertical: 4),
child: ListTile(
title: Text(
provider.name,
style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : null,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(locService.t('tileProviders.tileTypesCount', params: [provider.tileTypes.length.toString()])),
if (provider.apiKey?.isNotEmpty == true)
Text(
locService.t('tileProviders.apiKeyConfigured'),
style: const TextStyle(
fontStyle: FontStyle.italic,
fontSize: 12,
),
),
if (!provider.isUsable)
Text(
locService.t('tileProviders.needsApiKey'),
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 12,
),
),
],
),
leading: CircleAvatar(
backgroundColor: isSelected
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.surfaceVariant,
child: Icon(
Icons.map,
color: isSelected
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
trailing: appState.tileProviders.length > 1
? PopupMenuButton<String>(
onSelected: (action) {
switch (action) {
case 'edit':
_editProvider(context, provider);
break;
case 'delete':
_deleteProvider(context, provider);
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'edit',
child: Row(
children: [
const Icon(Icons.edit),
const SizedBox(width: 8),
Text(locService.t('actions.edit')),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
children: [
const Icon(Icons.delete),
const SizedBox(width: 8),
Text(locService.t('tileProviders.deleteProvider')),
],
),
),
],
)
: const Icon(Icons.lock, size: 16), // Can't delete last provider
onTap: () => _editProvider(context, provider),
),
);
}
void _addProvider(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const TileProviderEditorScreen(),
),
);
}
void _editProvider(BuildContext context, TileProvider provider) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => TileProviderEditorScreen(provider: provider),
),
);
}
void _deleteProvider(BuildContext context, TileProvider provider) {
final locService = LocalizationService.instance;
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(locService.t('tileProviders.deleteProvider')),
content: Text(locService.t('tileProviders.deleteProviderConfirm', params: [provider.name])),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(locService.t('actions.cancel')),
),
TextButton(
onPressed: () {
context.read<AppState>().deleteTileProvider(provider.id);
Navigator.of(context).pop();
},
child: Text(locService.t('tileProviders.deleteProvider')),
),
],
),
);
}
}