mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-07-07 21:27:57 +02:00
Monolithic reimplementation of node fetching from overpass/offline areas. Prevent submissions in areas without cache coverage. Also fixes offline node loading.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
import '../app_state.dart';
|
||||
import '../dev_config.dart';
|
||||
@@ -8,6 +10,7 @@ import '../models/node_profile.dart';
|
||||
import '../models/operator_profile.dart';
|
||||
import '../services/localization_service.dart';
|
||||
import '../services/map_data_provider.dart';
|
||||
import '../services/node_data_manager.dart';
|
||||
import '../services/changelog_service.dart';
|
||||
import 'refine_tags_sheet.dart';
|
||||
import 'proximity_warning_dialog.dart';
|
||||
@@ -31,6 +34,13 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkTutorialStatus();
|
||||
// Listen to node data manager for cache updates
|
||||
NodeDataManager().addListener(_onCacheUpdated);
|
||||
}
|
||||
|
||||
void _onCacheUpdated() {
|
||||
// Rebuild when cache updates (e.g., when new data loads)
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _checkTutorialStatus() async {
|
||||
@@ -76,6 +86,9 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Remove listener
|
||||
NodeDataManager().removeListener(_onCacheUpdated);
|
||||
|
||||
// Clear tutorial callback when widget is disposed
|
||||
if (_showTutorial) {
|
||||
try {
|
||||
@@ -401,10 +414,34 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
|
||||
final session = widget.session;
|
||||
final submittableProfiles = appState.enabledProfiles.where((p) => p.isSubmittable).toList();
|
||||
|
||||
// Check if we have good cache coverage around the node position
|
||||
bool hasGoodCoverage = true;
|
||||
if (session.target != null) {
|
||||
// Create a small bounds around the target position to check coverage
|
||||
const double bufferDegrees = 0.001; // ~100m buffer
|
||||
final targetBounds = LatLngBounds(
|
||||
LatLng(session.target!.latitude - bufferDegrees, session.target!.longitude - bufferDegrees),
|
||||
LatLng(session.target!.latitude + bufferDegrees, session.target!.longitude + bufferDegrees),
|
||||
);
|
||||
hasGoodCoverage = MapDataProvider().hasGoodCoverageFor(targetBounds);
|
||||
|
||||
// If strict coverage check fails, fall back to checking if we have any nodes nearby
|
||||
// This handles the timing issue where cache might not be marked as "covered" yet
|
||||
if (!hasGoodCoverage) {
|
||||
final nearbyNodes = MapDataProvider().findNodesWithinDistance(
|
||||
session.target!,
|
||||
200.0, // 200m radius - if we have nodes nearby, we likely have good data
|
||||
);
|
||||
hasGoodCoverage = nearbyNodes.isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
final allowSubmit = appState.isLoggedIn &&
|
||||
submittableProfiles.isNotEmpty &&
|
||||
session.profile != null &&
|
||||
session.profile!.isSubmittable;
|
||||
session.profile!.isSubmittable &&
|
||||
hasGoodCoverage;
|
||||
|
||||
void _navigateToLogin() {
|
||||
Navigator.pushNamed(context, '/settings/osm-account');
|
||||
@@ -517,6 +554,22 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else if (!hasGoodCoverage)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.cloud_download, color: Colors.blue, size: 20),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
locService.t('addNode.loadingAreaData'),
|
||||
style: const TextStyle(color: Colors.blue, fontSize: 13),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
import '../app_state.dart';
|
||||
import '../dev_config.dart';
|
||||
@@ -8,6 +10,7 @@ import '../models/node_profile.dart';
|
||||
import '../models/operator_profile.dart';
|
||||
import '../services/localization_service.dart';
|
||||
import '../services/map_data_provider.dart';
|
||||
import '../services/node_data_manager.dart';
|
||||
import '../services/changelog_service.dart';
|
||||
import '../state/settings_state.dart';
|
||||
import 'refine_tags_sheet.dart';
|
||||
@@ -33,6 +36,13 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkTutorialStatus();
|
||||
// Listen to node data manager for cache updates
|
||||
NodeDataManager().addListener(_onCacheUpdated);
|
||||
}
|
||||
|
||||
void _onCacheUpdated() {
|
||||
// Rebuild when cache updates (e.g., when new data loads)
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _checkTutorialStatus() async {
|
||||
@@ -59,8 +69,12 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@override
|
||||
void dispose() {
|
||||
// Remove listener
|
||||
NodeDataManager().removeListener(_onCacheUpdated);
|
||||
|
||||
// Clear tutorial callback when widget is disposed
|
||||
if (_showTutorial) {
|
||||
try {
|
||||
@@ -287,11 +301,33 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
final session = widget.session;
|
||||
final submittableProfiles = appState.enabledProfiles.where((p) => p.isSubmittable).toList();
|
||||
final isSandboxMode = appState.uploadMode == UploadMode.sandbox;
|
||||
|
||||
// Check if we have good cache coverage around the node position
|
||||
bool hasGoodCoverage = true;
|
||||
final nodeCoord = session.originalNode.coord;
|
||||
const double bufferDegrees = 0.001; // ~100m buffer
|
||||
final targetBounds = LatLngBounds(
|
||||
LatLng(nodeCoord.latitude - bufferDegrees, nodeCoord.longitude - bufferDegrees),
|
||||
LatLng(nodeCoord.latitude + bufferDegrees, nodeCoord.longitude + bufferDegrees),
|
||||
);
|
||||
hasGoodCoverage = MapDataProvider().hasGoodCoverageFor(targetBounds);
|
||||
|
||||
// If strict coverage check fails, fall back to checking if we have any nodes nearby
|
||||
// This handles the timing issue where cache might not be marked as "covered" yet
|
||||
if (!hasGoodCoverage) {
|
||||
final nearbyNodes = MapDataProvider().findNodesWithinDistance(
|
||||
nodeCoord,
|
||||
200.0, // 200m radius - if we have nodes nearby, we likely have good data
|
||||
);
|
||||
hasGoodCoverage = nearbyNodes.isNotEmpty;
|
||||
}
|
||||
|
||||
final allowSubmit = kEnableNodeEdits &&
|
||||
appState.isLoggedIn &&
|
||||
submittableProfiles.isNotEmpty &&
|
||||
session.profile != null &&
|
||||
session.profile!.isSubmittable;
|
||||
session.profile!.isSubmittable &&
|
||||
hasGoodCoverage;
|
||||
|
||||
void _navigateToLogin() {
|
||||
Navigator.pushNamed(context, '/settings/osm-account');
|
||||
@@ -478,6 +514,22 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else if (!hasGoodCoverage)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.cloud_download, color: Colors.blue, size: 20),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
locService.t('editNode.loadingAreaData'),
|
||||
style: const TextStyle(color: Colors.blue, fontSize: 13),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
|
||||
@@ -19,12 +19,12 @@ class NodeProviderWithCache extends ChangeNotifier {
|
||||
NodeProviderWithCache._internal();
|
||||
|
||||
final NodeDataManager _nodeDataManager = NodeDataManager();
|
||||
final NodeSpatialCache _cache = NodeSpatialCache();
|
||||
Timer? _debounceTimer;
|
||||
|
||||
/// Get cached nodes for the given bounds, filtered by enabled profiles
|
||||
List<OsmNode> getCachedNodesForBounds(LatLngBounds bounds) {
|
||||
final allNodes = _cache.getNodesFor(bounds);
|
||||
// Use the same cache instance as NodeDataManager
|
||||
final allNodes = NodeSpatialCache().getNodesFor(bounds);
|
||||
final enabledProfiles = AppState.instance.enabledProfiles;
|
||||
|
||||
// If no profiles are enabled, show no nodes
|
||||
@@ -68,7 +68,6 @@ class NodeProviderWithCache extends ChangeNotifier {
|
||||
|
||||
/// Clear the cache and repopulate with pending nodes from upload queue
|
||||
void clearCache() {
|
||||
_cache.clear();
|
||||
_nodeDataManager.clearCache();
|
||||
// Repopulate with pending nodes from upload queue if available
|
||||
_repopulatePendingNodesAfterClear();
|
||||
|
||||
Reference in New Issue
Block a user