Implement NoAccessConfigRepository with Supabase remote config
epic-no-access-screen-foundation-task-002 — Build the NoAccessConfigRepository class that fetches the admin portal URL and blocked-role list from Supabase remote config. Implement a timed in-memory cache (e.g., 5-minute TTL) to prevent redundant network calls. Fall back to local constants defined in task-001 on network error or timeout.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement the TTL cache using a private `_cachedConfig` field and a `_cacheExpiry` `DateTime?` field. Check `DateTime.now().isBefore(_cacheExpiry!)` on each `getConfig()` call. Inject a `SupabaseClient` (or an abstract `ISupabaseClient`) through the constructor for testability — avoid accessing `Supabase.instance.client` directly inside the class. The Supabase remote config source could be a dedicated `app_config` table with a single row, or a Postgres function — coordinate with the backend team on the exact table/RPC name.
Wrap the entire fetch in a `try-catch` catching both `PostgrestException` and `TimeoutException`. Use `.timeout(const Duration(seconds: 5))` on the Supabase future.
Testing Requirements
Unit tests using `flutter_test` and `mocktail`. Test scenarios: (1) successful Supabase response maps to correct `NoAccessConfig` fields; (2) second call within TTL returns cached value without invoking Supabase client; (3) Supabase throws `SocketException` → fallback constants returned; (4) Supabase returns malformed JSON → fallback constants returned; (5) after TTL expires (advance fake clock), next call re-fetches from Supabase. Use `fake_async` or manual `DateTime` injection to control the TTL clock in tests.
Supabase remote config may be unavailable at app startup (network error, cold start), causing the repository to return no blocked-role list. If the fallback is empty, blocked users could access the app.
Mitigation & Contingency
Mitigation: Define a local constants fallback list of blocked roles compiled into the app binary. Remote config enriches or overrides this list when available.
Contingency: If remote config repeatedly fails in production, pin the blocked-role list to local constants only and disable remote override until the Supabase config endpoint is stabilised.
url_launcher package behaviour differs between iOS and Android (e.g. canLaunchUrl returning false on some Android configurations), leading to silent failures when the admin portal link is tapped.
Mitigation & Contingency
Mitigation: Run canLaunchUrl check before every launch attempt and surface a descriptive inline error message (e.g. 'Could not open link — visit admin.example.org manually') when the check fails.
Contingency: If canLaunchUrl is consistently unreliable on a target platform, replace the tap-to-open pattern with a copyable text field showing the URL as a fallback.