Implement Badge Configuration Service
epic-achievement-badges-ui-task-008 — Build the badge-configuration-service that provides a typed, validated view of all active badge definitions by wrapping badge-definition-repository. Expose methods for listing badges by category, looking up a badge by ID, and resolving display metadata. Include in-memory caching with TTL-based invalidation.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement the TTL cache as a private _CacheEntry class holding the data and a DateTime expiry. Check DateTime.now().isAfter(expiry) at the start of each public method. Inject a DateTime Function() clock parameter defaulting to DateTime.now for easy test overriding. Use a single Map
Populate both structures in a single _refreshCache() private method. The service should be a singleton in the DI container since badge definitions change infrequently. Consider pre-warming the cache on app start as part of the app initialization sequence.
Testing Requirements
Unit tests: (1) first call fetches from repository and populates cache; (2) second call within TTL uses cache — verify repository called exactly once via mock; (3) call after TTL expiry re-fetches from repository; (4) getBadgeById with unknown ID throws BadgeDefinitionNotFoundException; (5) malformed record in repository response is skipped with warning log. Use fake clock (injectable DateTime provider) for TTL tests to avoid real time delays. Minimum 90% branch coverage.
The badge-earned-celebration overlay must appear within 2 seconds of the triggering activity being saved, but badge evaluation runs server-side in an edge function triggered by a database webhook. Network latency, edge function cold start, and Supabase Realtime delivery delays could cause the overlay to appear late or not at all, breaking the motivational loop.
Mitigation & Contingency
Mitigation: Implement an optimistic UI path: after activity save, badge-bloc immediately checks whether any badge thresholds are crossed client-side using cached stats and badge definitions, showing the overlay speculatively before server confirmation. The server result then reconciles. Subscribe to Supabase Realtime on the earned_badges table for authoritative confirmation.
Contingency: If Realtime delivery is unreliable in production, add a polling fallback: badge-bloc polls for new earned badges 3 seconds after an activity save and shows the overlay if a new record is detected, accepting up to 5-second latency as a fallback SLA.
The celebration overlay uses animation for positive reinforcement, but motion sensitivity (prefers-reduced-motion) and screen reader users require a non-animated or text-only alternative. Failing to handle this risks excluding Blindeforbundet users or triggering vestibular discomfort for motion-sensitive volunteers.
Mitigation & Contingency
Mitigation: Check MediaQuery.disableAnimations in badge-earned-celebration-overlay and skip animation entirely when true, showing a static card instead. Add an ExcludeSemantics wrapper around the decorative animation widget and a separate Semantics node with a live region announcement of the badge name and congratulatory message.
Contingency: If accessibility issues are identified in TestFlight testing with Blindeforbundet's test group, fast-track a patch that defaults to the static card path and gates the animation behind a user preference setting in notification preferences.