Apple requires all iOS/iPadOS apps to be built with the iOS 26 SDK
(Xcode 26+) starting April 28, 2026. Switch the build-ios and
upload-to-stores jobs from macos-latest (macOS 15 / Xcode 16) to
macos-26 (macOS 26 / Xcode 26).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump Dart SDK constraint from >=3.8.0 to >=3.10.3 to match resolved dependency floor
- Upgrade desugar_jdk_libs from 2.0.4 to 2.1.5 (adds Stream.toList(), better locale support)
- Migrate deprecated kotlinOptions { jvmTarget } to kotlin { compilerOptions { jvmTarget } }
- Remove stale comments and non-breaking space characters
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run `flutter pub upgrade` to pull in 42 dependency updates within
existing ^constraints. No pubspec.yaml changes needed.
Notable updates: flutter_map 8.2.1→8.2.2, flutter_svg 2.2.0→2.2.3,
http 1.5.0-beta.2→1.6.0, provider 6.1.5→6.1.5+1,
shared_preferences 2.5.3→2.5.4, uuid 4.5.1→4.5.2, xml 6.5.0→6.6.1,
flutter_native_splash 2.4.6→2.4.7, plus many transitive deps.
Closes#78
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace our custom tile pipeline (fetchRemoteTile / _SimpleSemaphore /
exponential backoff) with flutter_map's built-in NetworkTileProvider,
gaining persistent disk cache, ETag revalidation, RetryClient, and
obsolete request aborting for free.
DeflockTileProvider now extends NetworkTileProvider and overrides
getTileUrl() to route through TileType.getTileUrl() (quadkey,
subdomains, API keys). getImageWithCancelLoadingSupport() routes
between two paths at runtime: the common network path (super) when
no offline areas exist, and a DeflockOfflineTileImageProvider for
offline-first when they do.
- Delete tiles_from_remote.dart (semaphore, retry loop, spatial helpers)
- Simplify MapDataProvider._fetchRemoteTileFromCurrentProvider to plain
http.get (only used by offline area downloader now)
- Remove dead clearTileQueue/clearTileQueueSelective from MapDataProvider
- Remove 7 tile fetch constants from dev_config.dart
- TileLayerManager now disposes provider on cache clear and uses actual
urlTemplate for cache key generation
- 9 new tests covering URL delegation, routing, and equality
Closes#87 Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create UserAgentClient (http.BaseClient wrapper) that injects a
User-Agent header into every request, reading app name and version
from VersionService and contact/homepage from dev_config.dart.
Format follows OSM tile usage policy:
DeFlock/<version> (+https://deflock.org; contact: admin@stopflock.com)
Replaces 4 inconsistent hardcoded UA strings and adds UA to the 9
call sites that previously sent none.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cold Gradle builds were taking ~8.5 min for the APK job. Add
caching for Flutter/pub (via flutter-action), Gradle deps, and
CocoaPods to speed up subsequent runs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Let reviewers download and test PR changes on a device without building
locally. A sticky PR comment links directly to the artifacts page.
Closes#52
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch the second Overpass pass (ways/relations) from out meta to out skel,
dropping unused tags/version/changeset fields from the response. The app only
reads structural references (node lists, relation members) from these elements.
Also inject http.Client into OverpassService for testability (matching
RoutingService pattern) and add close() for client lifecycle management.
14 tests covering query building, constraint detection, and error handling.
Fixes#108
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cancel in-progress follow-me animations on pointer-down and suppress
new ones while any pointer is on the map. Without this, GPS position
updates trigger 600ms animateTo() calls that fight with the user's
stationary finger, causing visible wiggle — especially at low zoom
where small geographic shifts cover more pixels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 'main' of github.com:FoggedLens/deflock-app:
Remove simulation tests that don't exercise production code
Guard onFieldSubmitted on non-empty text to prevent cleared values reappearing
Add tests demonstrating RawAutocomplete onSubmitted bug
no longer lose operator profile selection when making other changes to a node
bump version
Move README roadmap items to GitHub Issues
Address PR review: truncate error response logs and close http client
Fix route calculation HTTP 400 by filtering empty profile tags
Add tests for routing service and node profile serialization
Make suggestion limit configurable and remove redundant .take(10) from widget
Materialize options iterable to list in optionsViewBuilder
Fix dropdown dismiss by replacing manual overlay with RawAutocomplete
Address Copilot review feedback on PR #46
Bump Dart SDK constraint to >=3.8.0 and document Flutter 3.35+ requirement
Rewrite dev setup docs with tested, copy-pasteable instructions
The RawAutocomplete tests proved the pattern and fix but never
instantiated NSITagValueField itself — no actual coverage gained.
PR #36 replaces the widget entirely, so investing in testability
here isn't worthwhile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a user clears a tag value and presses Done, RawAutocomplete's
onFieldSubmitted auto-selects the first option from the suggestions
list. Since optionsBuilder returns all suggestions for empty text,
this causes the cleared value to reappear. Guarding the call on
non-empty text prevents the auto-selection while preserving
autocomplete behavior when the user has typed a partial match.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RawAutocomplete.onFieldSubmitted auto-selects the first option when
called, which means pressing "Done" on an empty tag value field
re-populates it with the first NSI suggestion. These tests prove the
bug exists (unguarded path) and verify the fix (guarded path).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the inline roadmap section with a link to GitHub Issues.
Each roadmap item has been created as a proper issue (#55-#76) for
better tracking, discussion, and prioritization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Gate full error response body logging behind kDebugMode; truncate to
500 chars in release builds to avoid log noise and data exposure
- Add RoutingService.close() and call from NavigationState.dispose()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Built-in profiles (Flock, Motorola, etc.) include placeholder empty
values like camera:mount: '' for user refinement. When these get
serialized into the routing request body, the alprwatch API rejects
them with HTTP 400.
Fix: strip empty-valued tags from enabled_profiles before sending
the routing request. Also refactor RoutingService to accept an
injectable http.Client for testability, and log error response
bodies for easier debugging of future API issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route calculation to alprwatch API fails with HTTP 400 because
built-in profiles include empty tag values (e.g. camera:mount: '')
that get serialized into the request body and rejected by the API.
Add routing_service_test.dart with 5 tests:
- Empty tags filtered from request (reproduces the bug)
- Successful route parsing
- HTTP error handling
- Network error wrapping
- API-level error surfacing
Add node_profile_test.dart with 4 tests:
- toJson/fromJson round-trip
- getDefaults returns expected profiles
- Empty tag values exist in defaults (documents bug origin)
- Equality based on id
Tests require RoutingService to accept an injectable http.Client,
which will be added in the next commit along with the fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move hardcoded suggestion limit to kNSIMaxSuggestions in dev_config, and remove
the redundant .take(10) from optionsBuilder since the fetch stage already caps
results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoids repeated iteration of the lazy .where().take() iterable on each
call to .length and .elementAt() in ListView.builder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NSITagValueField used raw OverlayEntry + CompositedTransformFollower
with no tap-outside dismiss mechanism, causing suggestion dropdowns to
stay visible when tapping elsewhere. Replace with Flutter's
RawAutocomplete which handles dismiss, keyboard navigation, and
accessibility out of the box.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- build.gradle.kts: use maxOf(flutter.minSdkVersion, 23) to preserve
the floor required by oauth2_client/flutter_web_auth_2
- DEVELOPER.md: replace hardcoded /opt/homebrew paths with
$(brew --prefix) for Intel Mac compatibility, use $HOME instead
of /Users/$USER for --sdk_root
- README.md: label quick-start as macOS-specific, add cross-platform
pointer to DEVELOPER.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The RadioGroup widget (merged via PR #35) requires Flutter 3.35+ /
Dart 3.8+. The old constraint (>=3.5.0) allowed older SDKs that don't
have RadioGroup, causing cryptic build errors instead of a clear
version mismatch from pub get.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the vague "Latest stable version" prerequisites in DEVELOPER.md
with concrete commands tested on a fresh macOS machine. Adds Android SDK
setup without Android Studio, documents the gen_icons_splashes.sh
requirement, and fixes the OAuth2 config to reference build_keys.conf
instead of the removed keys.dart.example. Also includes Flutter SDK
auto-migrations (iOS 13.0 min, gradle minSdk).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>