mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-08-17 16:37:31 +02:00
use real node id for pending uploads, more camera -> node
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import '../models/osm_camera_node.dart';
|
||||
import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
|
||||
|
||||
class CameraCache {
|
||||
// Singleton instance
|
||||
static final CameraCache instance = CameraCache._internal();
|
||||
factory CameraCache() => instance;
|
||||
CameraCache._internal();
|
||||
|
||||
final Map<int, OsmCameraNode> _nodes = {};
|
||||
|
||||
/// Add or update a batch of camera nodes in the cache.
|
||||
void addOrUpdate(List<OsmCameraNode> nodes) {
|
||||
for (var node in nodes) {
|
||||
final existing = _nodes[node.id];
|
||||
if (existing != null) {
|
||||
// Preserve any tags starting with underscore when updating existing nodes
|
||||
final mergedTags = Map<String, String>.from(node.tags);
|
||||
for (final entry in existing.tags.entries) {
|
||||
if (entry.key.startsWith('_')) {
|
||||
mergedTags[entry.key] = entry.value;
|
||||
}
|
||||
}
|
||||
_nodes[node.id] = OsmCameraNode(
|
||||
id: node.id,
|
||||
coord: node.coord,
|
||||
tags: mergedTags,
|
||||
);
|
||||
} else {
|
||||
_nodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query for all cached cameras currently within the given LatLngBounds.
|
||||
List<OsmCameraNode> queryByBounds(LatLngBounds bounds) {
|
||||
return _nodes.values
|
||||
.where((node) => _inBounds(node.coord, bounds))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Retrieve all cached cameras.
|
||||
List<OsmCameraNode> getAll() => _nodes.values.toList();
|
||||
|
||||
/// Optionally clear the cache (rarely needed)
|
||||
void clear() => _nodes.clear();
|
||||
|
||||
/// Utility: point-in-bounds for coordinates
|
||||
bool _inBounds(LatLng coord, LatLngBounds bounds) {
|
||||
return coord.latitude >= bounds.southWest.latitude &&
|
||||
coord.latitude <= bounds.northEast.latitude &&
|
||||
coord.longitude >= bounds.southWest.longitude &&
|
||||
coord.longitude <= bounds.northEast.longitude;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:flutter_map/flutter_map.dart';
|
||||
|
||||
import '../../models/node_profile.dart';
|
||||
import '../../models/osm_camera_node.dart';
|
||||
import '../../models/pending_upload.dart';
|
||||
import '../../app_state.dart';
|
||||
import '../network_status.dart';
|
||||
|
||||
@@ -47,7 +48,7 @@ Future<List<OsmCameraNode>> fetchOverpassNodes({
|
||||
|
||||
NetworkStatus.instance.reportOverpassSuccess();
|
||||
|
||||
return elements.whereType<Map<String, dynamic>>().map((element) {
|
||||
final nodes = elements.whereType<Map<String, dynamic>>().map((element) {
|
||||
return OsmCameraNode(
|
||||
id: element['id'],
|
||||
coord: LatLng(element['lat'], element['lon']),
|
||||
@@ -55,6 +56,11 @@ Future<List<OsmCameraNode>> fetchOverpassNodes({
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// Clean up any pending uploads that now appear in Overpass results
|
||||
_cleanupCompletedUploads(nodes);
|
||||
|
||||
return nodes;
|
||||
|
||||
} catch (e) {
|
||||
debugPrint('[fetchOverpassNodes] Exception: $e');
|
||||
|
||||
@@ -92,4 +98,40 @@ String _buildOverpassQuery(LatLngBounds bounds, List<NodeProfile> profiles, int
|
||||
);
|
||||
$outputClause
|
||||
''';
|
||||
}
|
||||
|
||||
/// Clean up pending uploads that now appear in Overpass results
|
||||
void _cleanupCompletedUploads(List<OsmCameraNode> overpassNodes) {
|
||||
try {
|
||||
final appState = AppState.instance;
|
||||
final pendingUploads = appState.pendingUploads;
|
||||
|
||||
if (pendingUploads.isEmpty) return;
|
||||
|
||||
final overpassNodeIds = overpassNodes.map((n) => n.id).toSet();
|
||||
|
||||
// Find pending uploads whose submitted node IDs now appear in Overpass results
|
||||
final uploadsToRemove = <PendingUpload>[];
|
||||
|
||||
for (final upload in pendingUploads) {
|
||||
if (upload.submittedNodeId != null &&
|
||||
overpassNodeIds.contains(upload.submittedNodeId!)) {
|
||||
uploadsToRemove.add(upload);
|
||||
debugPrint('[OverpassCleanup] Found submitted node ${upload.submittedNodeId} in Overpass results, removing from pending queue');
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the completed uploads from the queue
|
||||
for (final upload in uploadsToRemove) {
|
||||
appState.removeFromQueue(upload);
|
||||
}
|
||||
|
||||
if (uploadsToRemove.isNotEmpty) {
|
||||
debugPrint('[OverpassCleanup] Cleaned up ${uploadsToRemove.length} completed uploads');
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
debugPrint('[OverpassCleanup] Error during cleanup: $e');
|
||||
// Don't let cleanup errors break the main functionality
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import '../models/osm_camera_node.dart';
|
||||
import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
|
||||
|
||||
class NodeCache {
|
||||
// Singleton instance
|
||||
static final NodeCache instance = NodeCache._internal();
|
||||
factory NodeCache() => instance;
|
||||
NodeCache._internal();
|
||||
|
||||
final Map<int, OsmCameraNode> _nodes = {};
|
||||
|
||||
/// Add or update a batch of nodes in the cache.
|
||||
void addOrUpdate(List<OsmCameraNode> nodes) {
|
||||
for (var node in nodes) {
|
||||
final existing = _nodes[node.id];
|
||||
if (existing != null) {
|
||||
// Preserve any tags starting with underscore when updating existing nodes
|
||||
final mergedTags = Map<String, String>.from(node.tags);
|
||||
for (final entry in existing.tags.entries) {
|
||||
if (entry.key.startsWith('_')) {
|
||||
mergedTags[entry.key] = entry.value;
|
||||
}
|
||||
}
|
||||
_nodes[node.id] = OsmCameraNode(
|
||||
id: node.id,
|
||||
coord: node.coord,
|
||||
tags: mergedTags,
|
||||
);
|
||||
} else {
|
||||
_nodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query for all cached nodes currently within the given LatLngBounds.
|
||||
List<OsmCameraNode> queryByBounds(LatLngBounds bounds) {
|
||||
return _nodes.values
|
||||
.where((node) => _inBounds(node.coord, bounds))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Retrieve all cached nodes.
|
||||
List<OsmCameraNode> getAll() => _nodes.values.toList();
|
||||
|
||||
/// Optionally clear the cache (rarely needed)
|
||||
void clear() => _nodes.clear();
|
||||
|
||||
/// Remove the _pending_edit marker from a specific node
|
||||
void removePendingEditMarker(int nodeId) {
|
||||
final node = _nodes[nodeId];
|
||||
if (node != null && node.tags.containsKey('_pending_edit')) {
|
||||
final cleanTags = Map<String, String>.from(node.tags);
|
||||
cleanTags.remove('_pending_edit');
|
||||
|
||||
_nodes[nodeId] = OsmCameraNode(
|
||||
id: node.id,
|
||||
coord: node.coord,
|
||||
tags: cleanTags,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove temporary nodes (negative IDs) with _pending_upload marker at the given coordinate
|
||||
/// This is used when a real node ID is assigned to clean up temp placeholders
|
||||
void removeTempNodesByCoordinate(LatLng coord, {double tolerance = 0.00001}) {
|
||||
final nodesToRemove = <int>[];
|
||||
|
||||
for (final entry in _nodes.entries) {
|
||||
final nodeId = entry.key;
|
||||
final node = entry.value;
|
||||
|
||||
// Only consider temp nodes (negative IDs) with pending upload marker
|
||||
if (nodeId < 0 &&
|
||||
node.tags.containsKey('_pending_upload') &&
|
||||
_coordsMatch(node.coord, coord, tolerance)) {
|
||||
nodesToRemove.add(nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
for (final nodeId in nodesToRemove) {
|
||||
_nodes.remove(nodeId);
|
||||
}
|
||||
|
||||
if (nodesToRemove.isNotEmpty) {
|
||||
print('[NodeCache] Removed ${nodesToRemove.length} temp nodes at coordinate ${coord.latitude}, ${coord.longitude}');
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if two coordinates match within tolerance
|
||||
bool _coordsMatch(LatLng coord1, LatLng coord2, double tolerance) {
|
||||
return (coord1.latitude - coord2.latitude).abs() < tolerance &&
|
||||
(coord1.longitude - coord2.longitude).abs() < tolerance;
|
||||
}
|
||||
|
||||
/// Utility: point-in-bounds for coordinates
|
||||
bool _inBounds(LatLng coord, LatLngBounds bounds) {
|
||||
return coord.latitude >= bounds.southWest.latitude &&
|
||||
coord.latitude <= bounds.northEast.latitude &&
|
||||
coord.longitude >= bounds.southWest.longitude &&
|
||||
coord.longitude <= bounds.northEast.longitude;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ class Uploader {
|
||||
Uploader(this.accessToken, this.onSuccess, {this.uploadMode = UploadMode.production});
|
||||
|
||||
final String accessToken;
|
||||
final void Function() onSuccess;
|
||||
final void Function(int nodeId) onSuccess;
|
||||
final UploadMode uploadMode;
|
||||
|
||||
Future<bool> upload(PendingUpload p) async {
|
||||
@@ -99,7 +99,8 @@ class Uploader {
|
||||
print('Uploader: Close response: ${closeResp.statusCode}');
|
||||
|
||||
print('Uploader: Upload successful!');
|
||||
onSuccess();
|
||||
final nodeIdInt = int.parse(nodeId);
|
||||
onSuccess(nodeIdInt);
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Uploader: Upload failed with error: $e');
|
||||
|
||||
Reference in New Issue
Block a user