Update readme, create dev notes

This commit is contained in:
stopflock
2025-09-29 11:16:35 -05:00
parent 5c525900f1
commit 5c80fdc169
2 changed files with 438 additions and 23 deletions

425
DEVELOPER.md Normal file
View File

@@ -0,0 +1,425 @@
# Developer Documentation
This document provides detailed technical information about the DeFlock app architecture, key design decisions, and development guidelines.
---
## Philosophy: Brutalist Code
Our development approach prioritizes **simplicity over cleverness**:
- **Explicit over implicit**: Clear, readable code that states its intent
- **Few edge cases by design**: Avoid complex branching and special cases
- **Maintainable over efficient**: Choose the approach that's easier to understand and modify
- **Delete before adding**: Remove complexity when possible rather than adding features
**Hierarchy of preferred code:**
1. **Code we don't write** (through thoughtful design and removing edge cases)
2. **Code we can remove** (by seeing problems from a new angle)
3. **Code that sadly must exist** (simple, explicit, maintainable)
---
## Architecture Overview
### State Management
The app uses **Provider pattern** with modular state classes:
```
AppState (main coordinator)
├── AuthState (OAuth2 login/logout)
├── OperatorProfileState (operator tag sets)
├── ProfileState (node profiles & toggles)
├── SessionState (add/edit sessions)
├── SettingsState (preferences & tile providers)
└── UploadQueueState (pending operations)
```
**Why this approach:**
- **Separation of concerns**: Each state handles one domain
- **Testability**: Individual state classes can be unit tested
- **Brutalist**: No complex state orchestration, just simple delegation
### Data Flow Architecture
```
UI Layer (Widgets)
↕️
AppState (Coordinator)
↕️
State Modules (AuthState, ProfileState, etc.)
↕️
Services (MapDataProvider, NodeCache, Uploader)
↕️
External APIs (OSM, Overpass, Tile providers)
```
**Key principles:**
- **Unidirectional data flow**: UI → AppState → Services → APIs
- **No direct service access from UI**: Everything goes through AppState
- **Clean boundaries**: Each layer has a clear responsibility
---
## Core Components
### 1. MapDataProvider
**Purpose**: Unified interface for fetching map tiles and surveillance nodes
**Design decisions:**
- **Pluggable sources**: Local (cached) vs Remote (live API)
- **Offline-first**: Always try local first, graceful degradation
- **Mode-aware**: Different behavior for production vs sandbox
- **Failure handling**: Never crash the UI, always provide fallbacks
**Key methods:**
- `getNodes()`: Smart fetching with local/remote merging
- `getTile()`: Tile fetching with caching
- `_fetchRemoteNodes()`: Handles Overpass → OSM API fallback
**Why unified interface:**
The app needs to seamlessly switch between multiple data sources (local cache, Overpass API, OSM API, offline areas) based on network status, upload mode, and zoom level. A single interface prevents the UI from needing to know about these complexities.
### 2. Node Operations (Create/Edit/Delete)
**Upload Operations Enum:**
```dart
enum UploadOperation { create, modify, delete }
```
**Why explicit enum vs boolean flags:**
- **Brutalist**: Three explicit states instead of nullable booleans
- **Extensible**: Easy to add new operations (like bulk operations)
- **Clear intent**: `operation == UploadOperation.delete` is unambiguous
**Session Pattern:**
- `AddNodeSession`: For creating new nodes
- `EditNodeSession`: For modifying existing nodes
- No "DeleteSession": Deletions are immediate (simpler)
**Why no delete session:**
Deletions don't need position dragging or tag editing - they just need confirmation and queuing. A session would add complexity without benefit.
### 3. Upload Queue System
**Design principles:**
- **Operation-agnostic**: Same queue handles create/modify/delete
- **Offline-capable**: Queue persists between app sessions
- **Visual feedback**: Each operation type has distinct UI state
- **Error recovery**: Retry mechanism with exponential backoff
**Queue workflow:**
1. User action (add/edit/delete) → `PendingUpload` created
2. Immediate visual feedback (cache updated with temp markers)
3. Background uploader processes queue when online
4. Success → cache updated with real data, temp markers removed
5. Failure → error state, retry available
**Why immediate visual feedback:**
Users expect instant response to their actions. By immediately updating the cache with temporary markers (e.g., `_pending_deletion`), the UI stays responsive while the actual API calls happen in background.
### 4. Cache & Visual States
**Node visual states:**
- **Blue ring**: Real nodes from OSM
- **Purple ring**: Pending uploads (new nodes)
- **Grey ring**: Original nodes with pending edits
- **Orange ring**: Node currently being edited
- **Red ring**: Nodes pending deletion
**Cache tags for state tracking:**
```dart
'_pending_upload' // New node waiting to upload
'_pending_edit' // Original node has pending edits
'_pending_deletion' // Node queued for deletion
'_original_node_id' // For drawing connection lines
```
**Why underscore prefix:**
These are internal app tags, not OSM tags. The underscore prefix makes this explicit and prevents accidental upload to OSM.
### 5. Multi-API Data Sources
**Production mode:** Overpass API → OSM API fallback
**Sandbox mode:** OSM API only (Overpass doesn't have sandbox data)
**Zoom level restrictions:**
- **Production (Overpass)**: Zoom ≥ 10 (established limit)
- **Sandbox (OSM API)**: Zoom ≥ 13 (stricter due to bbox limits)
**Why different zoom limits:**
The OSM API returns ALL data types (nodes, ways, relations) in a bounding box and has stricter size limits. Overpass is more efficient for large areas. The zoom restrictions prevent API errors and excessive data transfer.
### 6. Offline vs Online Mode Behavior
**Mode combinations:**
```
Production + Online → Local cache + Overpass API
Production + Offline → Local cache only
Sandbox + Online → OSM API only (no cache mixing)
Sandbox + Offline → No nodes (cache is production data)
```
**Why sandbox + offline = no nodes:**
Local cache contains production data. Showing production nodes in sandbox mode would be confusing and could lead to users trying to edit production nodes with sandbox credentials.
---
## Key Design Decisions & Rationales
### 1. Why Provider Pattern?
**Alternatives considered:**
- BLoC: Too verbose for our needs
- Riverpod: Added complexity without clear benefit
- setState: Doesn't scale beyond single widgets
**Why Provider won:**
- **Familiar**: Most Flutter developers know Provider
- **Simple**: Minimal boilerplate
- **Flexible**: Easy to compose multiple providers
- **Battle-tested**: Mature, stable library
### 2. Why Separate State Classes?
**Alternative**: Single monolithic AppState
**Why modular state:**
- **Single responsibility**: Each state class has one concern
- **Testability**: Easier to unit test individual features
- **Maintainability**: Changes to auth don't affect profile logic
- **Team development**: Different developers can work on different states
### 3. Why Upload Queue vs Direct API Calls?
**Alternative**: Direct API calls from UI actions
**Why queue approach:**
- **Offline capability**: Actions work without internet
- **User experience**: Instant feedback, no waiting for API calls
- **Error recovery**: Failed uploads can be retried
- **Batch processing**: Could optimize multiple operations
- **Visual feedback**: Users can see pending operations
### 4. Why Overpass + OSM API vs Just One?
**Why not just Overpass:**
- Overpass doesn't have sandbox data
- Overpass can be unreliable/slow
- OSM API is canonical source
**Why not just OSM API:**
- OSM API has strict bbox size limits
- OSM API returns all data types (inefficient)
- Overpass is optimized for surveillance device queries
**Result**: Use the best tool for each situation
### 5. Why Zoom Level Restrictions?
**Alternative**: Always fetch, handle errors gracefully
**Why restrictions:**
- **Prevents API abuse**: Large bbox queries can overload servers
- **User experience**: Fetching 10,000 nodes causes UI lag
- **Battery life**: Excessive network requests drain battery
- **Clear feedback**: Users understand why nodes aren't showing
---
## Development Guidelines
### 1. Adding New Features
**Before writing code:**
1. Can we solve this by removing existing code?
2. Can we simplify the problem to avoid edge cases?
3. Does this fit the existing patterns?
**When adding new upload operations:**
1. Add to `UploadOperation` enum
2. Update `PendingUpload` serialization
3. Add visual state (color, icon)
4. Update uploader logic
5. Add cache cleanup handling
### 2. Testing Philosophy
**Priority order:**
1. **Integration tests**: Test complete user workflows
2. **Widget tests**: Test UI components with mock data
3. **Unit tests**: Test individual state classes
**Why integration tests first:**
The most important thing is that user workflows work end-to-end. Unit tests can pass while the app is broken from a user perspective.
### 3. Error Handling
**Principles:**
- **Never crash the UI**: Always provide fallbacks
- **Fail gracefully**: Empty list is better than exception
- **User feedback**: Show meaningful error messages
- **Logging**: Use debugPrint for troubleshooting
**Example pattern:**
```dart
try {
final result = await riskyOperation();
return result;
} catch (e) {
debugPrint('Operation failed: $e');
// Show user-friendly message
showSnackBar('Unable to load data. Please try again.');
return <EmptyResult>[];
}
```
### 4. State Updates
**Always notify listeners:**
```dart
void updateSomething() {
_something = newValue;
notifyListeners(); // Don't forget this!
}
```
**Batch related updates:**
```dart
void updateMultipleThings() {
_thing1 = value1;
_thing2 = value2;
_thing3 = value3;
notifyListeners(); // Single notification for all changes
}
```
---
## Build & Development Setup
### Prerequisites
- **Flutter SDK**: Latest stable version
- **Xcode**: For iOS builds (macOS only)
- **Android Studio**: For Android builds
- **Git**: For version control
### OAuth2 Setup
**Required registrations:**
1. **Production OSM**: https://www.openstreetmap.org/oauth2/applications
2. **Sandbox OSM**: https://master.apis.dev.openstreetmap.org/oauth2/applications
**Configuration:**
```bash
cp lib/keys.dart.example lib/keys.dart
# Edit keys.dart with your OAuth2 client IDs
```
### iOS Setup
```bash
cd ios && pod install
```
### Running
```bash
flutter pub get
flutter run
```
### Testing
```bash
# Run all tests
flutter test
# Run with coverage
flutter test --coverage
```
---
## Code Organization
```
lib/
├── models/ # Data classes
│ ├── osm_camera_node.dart
│ ├── pending_upload.dart
│ └── node_profile.dart
├── services/ # Business logic
│ ├── map_data_provider.dart
│ ├── uploader.dart
│ └── node_cache.dart
├── state/ # State management
│ ├── app_state.dart
│ ├── auth_state.dart
│ └── upload_queue_state.dart
├── widgets/ # UI components
│ ├── map_view.dart
│ ├── edit_node_sheet.dart
│ └── map/ # Map-specific widgets
├── screens/ # Full screens
│ ├── home_screen.dart
│ └── settings_screen.dart
└── localizations/ # i18n strings
├── en.json
├── de.json
├── es.json
└── fr.json
```
**Principles:**
- **Models**: Pure data, no business logic
- **Services**: Stateless business logic
- **State**: Stateful coordination
- **Widgets**: UI only, delegate to AppState
- **Screens**: Compose widgets, handle navigation
---
## Debugging Tips
### Common Issues
**Nodes not appearing:**
- Check zoom level (≥10 production, ≥13 sandbox)
- Check upload mode vs expected data source
- Check network connectivity
- Look for console errors
**Upload failures:**
- Verify OAuth2 credentials
- Check upload mode matches login (production vs sandbox)
- Ensure node has required tags
- Check network connectivity
**Cache issues:**
- Clear app data to reset cache
- Check if offline mode is affecting behavior
- Verify upload mode switches clear cache
### Debug Logging
**Enable verbose logging:**
```dart
debugPrint('[ComponentName] Detailed message: $data');
```
**Key areas to log:**
- Network requests and responses
- Cache operations
- State transitions
- User actions
### Performance
**Monitor:**
- Memory usage during large node fetches
- UI responsiveness during background uploads
- Battery usage during GPS tracking
---
This documentation should be updated as the architecture evolves. When making significant changes, update both the relevant section here and add a brief note explaining the rationale for the change.

View File

@@ -25,11 +25,11 @@ A comprehensive Flutter app for mapping public surveillance infrastructure with
- **Multi-source tiles**: Switch between OpenStreetMap, Google Satellite, Esri imagery, Mapbox, and any custom providers
- **Offline-first design**: Download a region for complete offline operation
- **Smooth UX**: Intuitive controls, follow-me mode with GPS rotation, and gesture-friendly interactions
- **Device visualization**: Color-coded markers showing real devices (blue), pending uploads (purple), new devices (white), edited devices (grey), and devices being edited (orange)
- **Device visualization**: Color-coded markers showing real devices (blue), pending uploads (purple), pending edits (grey), devices being edited (orange), and pending deletions (red)
### Device Management
- **Comprehensive profiles**: Built-in profiles for major manufacturers (Flock Safety, Motorola/Vigilant, Genetec, Leonardo/ELSAG, Neology) plus custom profile creation
- **Editing capabilities**: Update location, direction, and tags of existing devices
- **Full CRUD operations**: Create, edit, and delete surveillance devices
- **Direction visualization**: Interactive field-of-view cones showing camera viewing angles
- **Bulk operations**: Tag multiple devices efficiently with profile-based workflow
@@ -52,7 +52,8 @@ A comprehensive Flutter app for mapping public surveillance infrastructure with
1. **Install** the app on iOS or Android
2. **Enable location** permissions
3. **Log into OpenStreetMap**: Choose upload mode and get OAuth2 credentials
4. **Add your first device**: Tap the "tag node" button, position the pin, set direction, select a profile, and tap submit
4. **Add your first device**: Tap the "New Node" button, position the pin, set direction, select a profile, and tap submit
5. **Edit or delete devices**: Tap any device marker to view details, then use Edit or Delete buttons
**New to OpenStreetMap?** Visit [deflock.me](https://deflock.me) for complete setup instructions and community guidelines.
@@ -60,37 +61,26 @@ A comprehensive Flutter app for mapping public surveillance infrastructure with
## For Developers
### Architecture Highlights
- **Unified data provider**: All map tiles and surveillance device data route through `MapDataProvider` with pluggable remote/local sources
- **Modular settings**: Each settings section is a separate widget for maintainability
- **State management**: Provider pattern with clean separation of concerns
- **Offline-first**: Network calls are optional; app functions fully offline with downloaded data and queues uploads until online
### Build Setup
**Prerequisites**: Flutter SDK, Xcode (iOS), Android Studio
**OAuth Setup**: Register apps at [openstreetmap.org/oauth2](https://www.openstreetmap.org/oauth2/applications) and [OSM Sandbox](https://master.apis.dev.openstreetmap.org/oauth2/applications) to get a client ID
**See [DEVELOPER.md](DEVELOPER.md)** for comprehensive technical documentation including:
- Architecture overview and design decisions
- Development setup and build instructions
- Code organization and contribution guidelines
- Debugging tips and troubleshooting
**Quick setup:**
```shell
# Basic setup
flutter pub get
cp lib/keys.dart.example lib/keys.dart
# Add your OAuth2 client IDs to keys.dart
# iOS additional setup
cd ios && pod install
# Run
flutter run
# Add OAuth2 client IDs, then: flutter run
```
---
## Roadmap
### v1 todo/bug List
### Current Development
- Update offline area nodes while browsing?
- Camera deletions
- Optional custom icons for camera profiles
- Optional custom icons for camera profiles
- Upgrade device marker design (considering nullplate's svg)
### Future Features & Wishlist