Commit Graph

13 Commits

Author SHA1 Message Date
Doug Borg
91e5177056 Detect config drift in cached tile providers and replace stale instances
When a user edits a tile type's URL template, max zoom, or API key
without changing IDs, the cached DeflockTileProvider would keep the old
frozen config. Now _getOrCreateProvider() computes a config fingerprint
and replaces the provider when drift is detected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:34:01 -07:00
Doug Borg
f3f40f36ef Allow OSM offline downloads, disable button for restricted providers
Allow offline area downloads for OSM tile server. Move the "downloads
not permitted" check from inside the download dialog to the download
button itself — the button is now disabled (greyed out) when the
current tile type doesn't support offline downloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:34:01 -07:00
Doug Borg
2d92214bed Add offline-first tile system with per-provider caching and error retry
- Add ServicePolicy framework with OSM-specific rate limiting and TTL
- Add per-provider disk tile cache (ProviderTileCacheStore) with O(1)
  lookup, oldest-modified eviction, and ETag/304 revalidation
- Rewrite DeflockTileProvider with two paths: common (NetworkTileProvider)
  and offline-first (disk cache -> local tiles -> network with caching)
- Add zoom-aware offline routing so tiles outside offline area zoom ranges
  use the efficient common path instead of the overhead-heavy offline path
- Fix HTTP client lifecycle: dispose() is now a no-op for flutter_map
  widget recycling; shutdown() handles permanent teardown
- Add TileLayerManager with exponential backoff retry (2s->60s cap),
  provider switch detection, and backoff reset
- Guard null provider/tileType in download dialog with localized error
- Fix Nominatim cache key to use normalized viewbox values
- Comprehensive test coverage (1800+ lines across 6 test files)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:34:01 -07:00
Doug Borg
8983939b05 Delegate network tile fetching to NetworkTileProvider
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>
2026-02-24 20:07:56 -07:00
Doug Borg
0137fd66aa Add consistent User-Agent header to all HTTP clients
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>
2026-02-24 19:31:27 -07:00
Doug Borg
5df0170344 Use out skel for Overpass way/relation pass and add service tests
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>
2026-02-14 13:26:22 -07:00
Doug Borg
6607e30038 Add tests for routing service and node profile serialization
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>
2026-02-09 14:32:34 -07:00
Doug Borg
037165653c Fix lint warnings and cleanup unused code after RadioGroup migration
Remove unused imports, fields, variables, and dead code introduced
during the RadioGroup widget migration and prior changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:36:18 -07:00
Doug Borg
61a2a99bbc Replace deprecated localization APIs and add test coverage
Use AssetManifest.loadFromAssetBundle instead of manually parsing the
deprecated AssetManifest.json. Fix a broken localization key reference
(queue.cameraWithIndex → queue.itemWithIndex).

Replace the standalone scripts/validate_localizations.dart with proper
flutter tests (11 tests across two groups): file integrity checks
(directory exists, en.json present, valid JSON structure, language code
file names, deep key-completeness across all locales) and t() lookup
tests (nested resolution, missing-key fallback, parameter substitution,
partial-path fallback).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 22:31:48 -07:00
Doug Borg
73160c32de Add mocktail dev dependency and fix test state leak
Address PR review comments:
- Add mocktail and flutter_test to dev_dependencies in pubspec.yaml
- Add tearDown to reset AppState.instance between tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 11:35:28 -07:00
Doug Borg
d6f7e99941 Fix pre-existing test failures in tile provider tests
- tile_provider_test: Fix stale package:flock_map_app import (now
  deflockapp), correct test assertion for Mapbox requiring API key
- deflock_tile_provider_test: Fix relative imports, replace invalid
  const TileLayer with final, mock AppState.instance for getImage test
- Remove widget_test.dart (default flutter create scaffold, references
  nonexistent MyApp counter widget)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 11:27:39 -07:00
stopflock
83d7814fb6 Network status indicator should only respect the latest / current request. Others finish in background. Replace stupid bools with an enum to track state. Be smarter about split requests. 2026-01-31 17:21:31 -06:00
stopflock
45f1635e10 Get rid of double cache layer, remove tiles from network status indicator, fix status callbacks from split fetches, use tileprovider instead of http catching. 2025-11-24 18:28:36 -06:00