test mode to disable uploads, queue view/edit/clear

This commit is contained in:
stopflock
2025-07-19 19:58:26 -05:00
parent 2e1b504419
commit e290e11c5b
2 changed files with 151 additions and 7 deletions
+46 -4
View File
@@ -28,6 +28,15 @@ class AppState extends ChangeNotifier {
late final List<CameraProfile> _profiles = [CameraProfile.alpr()];
final Set<CameraProfile> _enabled = {};
// Test mode - prevents actual uploads to OSM
bool _testMode = false;
bool get testMode => _testMode;
void setTestMode(bool enabled) {
_testMode = enabled;
print('AppState: Test mode ${enabled ? 'enabled' : 'disabled'}');
notifyListeners();
}
AddCameraSession? _session;
AddCameraSession? get session => _session;
@@ -216,13 +225,30 @@ class AppState extends ChangeNotifier {
if (access == null) return; // not logged in
final item = _queue.first;
final up = Uploader(access, () {
bool ok;
if (_testMode) {
// Test mode - simulate successful upload without actually calling OSM API
print('AppState: Test mode - simulating upload for ${item.coord}');
await Future.delayed(const Duration(seconds: 1)); // Simulate network delay
ok = true;
print('AppState: Test mode - simulated upload successful');
} else {
// Real upload
final up = Uploader(access, () {
_queue.remove(item);
_saveQueue();
notifyListeners();
});
ok = await up.upload(item);
}
if (ok && _testMode) {
// In test mode, manually remove from queue since Uploader callback won't be called
_queue.remove(item);
_saveQueue();
notifyListeners();
});
final ok = await up.upload(item);
}
if (!ok) {
item.attempts++;
if (item.attempts >= 3) {
@@ -237,5 +263,21 @@ class AppState extends ChangeNotifier {
// ---------- Exposed getters ----------
int get pendingCount => _queue.length;
List<PendingUpload> get pendingUploads => List.unmodifiable(_queue);
// ---------- Queue management ----------
void clearQueue() {
print('AppState: Clearing upload queue (${_queue.length} items)');
_queue.clear();
_saveQueue();
notifyListeners();
}
void removeFromQueue(PendingUpload upload) {
print('AppState: Removing upload from queue: ${upload.coord}');
_queue.remove(upload);
_saveQueue();
notifyListeners();
}
}
+105 -3
View File
@@ -88,10 +88,112 @@ class SettingsScreen extends StatelessWidget {
),
),
const Divider(),
ListTile(
leading: const Icon(Icons.sync),
title: Text('Pending uploads: ${appState.pendingCount}'),
// Test mode toggle
SwitchListTile(
secondary: const Icon(Icons.bug_report),
title: const Text('Test Mode'),
subtitle: const Text('Simulate uploads without sending to OSM'),
value: appState.testMode,
onChanged: (value) => appState.setTestMode(value),
),
const Divider(),
// Queue management
ListTile(
leading: const Icon(Icons.queue),
title: Text('Pending uploads: ${appState.pendingCount}'),
subtitle: appState.testMode
? const Text('Test mode enabled - uploads simulated')
: const Text('Tap to view queue'),
onTap: appState.pendingCount > 0 ? () {
_showQueueDialog(context, appState);
} : null,
),
if (appState.pendingCount > 0)
ListTile(
leading: const Icon(Icons.clear_all),
title: const Text('Clear Upload Queue'),
subtitle: Text('Remove all ${appState.pendingCount} pending uploads'),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Clear Queue'),
content: Text('Remove all ${appState.pendingCount} pending uploads?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
appState.clearQueue();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Queue cleared')),
);
},
child: const Text('Clear'),
),
],
),
);
},
),
],
),
);
}
void _showQueueDialog(BuildContext context, AppState appState) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Upload Queue (${appState.pendingCount} items)'),
content: SizedBox(
width: double.maxFinite,
height: 300,
child: ListView.builder(
itemCount: appState.pendingUploads.length,
itemBuilder: (context, index) {
final upload = appState.pendingUploads[index];
return ListTile(
leading: const Icon(Icons.camera_alt),
title: Text('Camera ${index + 1}'),
subtitle: Text(
'Lat: ${upload.coord.latitude.toStringAsFixed(6)}\n'
'Lon: ${upload.coord.longitude.toStringAsFixed(6)}\n'
'Direction: ${upload.direction.round()}°\n'
'Attempts: ${upload.attempts}'
),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
appState.removeFromQueue(upload);
if (appState.pendingCount == 0) {
Navigator.pop(context);
}
},
),
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
if (appState.pendingCount > 1)
TextButton(
onPressed: () {
appState.clearQueue();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Queue cleared')),
);
},
child: const Text('Clear All'),
),
],
),
);