Build NotificationEmptyState widget
epic-in-app-notification-centre-foundation-task-008 — Implement the NotificationEmptyState as a stateless Flutter widget displayed when the notification list is empty or a filtered view yields no results. Include an accessible illustration placeholder, a localized headline ('No notifications'), a contextual sub-message that adapts based on whether a filter is active, and an optional CTA button (e.g., 'Clear filter'). Apply design token typography and spacing. Add semantic labels for screen reader announcement of empty state.
Acceptance Criteria
Technical Requirements
Implementation Notes
Keep the widget strictly stateless: accept `bool isFilterActive` and `VoidCallback? onClearFilter` as constructor parameters. Use the app's existing design token system for all spacing and typography — do not introduce hardcoded values. The illustration placeholder should be a fixed-size Container or SvgPicture with ExcludeSemantics wrapping unless a meaningful description can be provided.
Wrap the headline in a `Text` widget inside a `Semantics` node with `header: true` so screen readers announce it as a section heading. Place the widget in `lib/features/notifications/widgets/notification_empty_state.dart`. Since this widget has no dependencies on other tasks, it can be implemented and tested in isolation immediately.
Testing Requirements
Widget tests using flutter_test and WidgetTester. Test scenarios: (1) unfiltered empty state — verify headline text, sub-message text, illustration presence, CTA button absence; (2) filtered empty state — verify contextual sub-message, CTA button presence; (3) CTA tap — verify onClearFilter callback fires exactly once; (4) semantics tree — verify Semantics labels on illustration, headline, sub-message, and CTA button using SemanticsController; (5) const construction — verify widget can be instantiated as const when no callbacks provided. Target 100% branch coverage for this low-complexity widget.
Supabase Realtime channels on mobile networks can drop silently. If reconnection logic is flawed, users miss notifications without knowing it, undermining the audit-trail guarantee.
Mitigation & Contingency
Mitigation: Implement exponential-backoff reconnection with a maximum of 5 retries; expose a channel-status stream to the BLoC so it can trigger a full-fetch fallback when the channel reconnects after a gap.
Contingency: If Realtime reliability proves insufficient in production, fall back to polling the repository every 60 seconds as a background supplement to the Realtime channel.
Coordinator and org-admin RLS expansions require joining user_roles and org_memberships tables. An incorrect policy could expose notifications to wrong users or block legitimate access entirely.
Mitigation & Contingency
Mitigation: Write dedicated RLS integration tests for each role (peer mentor, coordinator, org admin) using separate Supabase test projects. Review policies with the security checklist before merging.
Contingency: If an RLS defect is discovered post-deployment, disable the expanded-scope policy and revert to user-scoped-only access while a corrected migration is prepared and tested.
JSONB payload structure may vary across notification types created by different Edge Functions (reminder, expiry, scenario, pause). Missing or renamed fields will cause runtime parse failures.
Mitigation & Contingency
Mitigation: Define a canonical NotificationPayload union type in a shared schema document. Each Edge Function must validate its payload against this schema before inserting. Add fallback parsing with default values in the domain model.
Contingency: Wrap all payload parsing in try/catch and log malformed payloads to a monitoring channel; render a generic notification item rather than crashing when the payload cannot be parsed.