Implement in-memory filtering rules per role
epic-in-app-notification-centre-services-task-003 — Implement the in-memory filtering pass that runs after the database query returns results. Apply coordinator-specific rules that include peer mentor notifications they are responsible for acting on, and org-admin rules that surface escalations. Ensure filtering logic is pure (no side effects) and testable in isolation without a live Supabase connection.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Define a `NotificationFilterService` class with static methods `filterForRole(UserRole role, List
Document the contract: this filter runs after DB results are returned and before state emission — it is never a replacement for RLS.
Testing Requirements
Unit tests (flutter_test) with no Supabase dependency. Test each role's filter with: (1) a mixed list containing notifications for self, other users, and escalations, (2) empty list, (3) list with notifications containing null fields. Verify coordinator filter correctly includes only mentee escalations for their assigned mentees, not all escalations in the org. Verify org_admin filter surfaces escalation types.
Use test fixtures (factory constructors or helper functions) to generate NotificationModel instances rather than manual JSON parsing. Aim for 100% branch coverage on filter functions. Add a regression test ensuring the filter is pure (call twice on same input, assert identical output and no mutation of original list).
A Realtime INSERT event arriving during an in-flight mark-all-read operation can cause the new notification to be incorrectly marked read in the optimistic state update, silently hiding it from the user.
Mitigation & Contingency
Mitigation: Process Realtime events sequentially in the BLoC event queue using bloc_concurrency's sequential transformer. The mark-all-read event should only affect notifications whose IDs were fetched before the operation started.
Contingency: Add a reconciliation step after mark-all-read that re-fetches the unread count from the repository and corrects the BLoC state if it diverges from the server value.
A coordinator assigned to many peer mentors may trigger a query returning hundreds or thousands of notifications. Without pagination and query optimisation, the initial load will be slow and memory-heavy.
Mitigation & Contingency
Mitigation: Enforce server-side pagination (50 items per page) in the Role-Aware Filter's query predicates. Add a composite index on (org_id, user_id, created_at DESC) and profile query plans before shipping.
Contingency: If query performance is insufficient for large coordinator scopes, introduce a server-side RPC function that pre-aggregates visible notification IDs and returns only the first page, deferring full scope resolution to lazy-load.
If the read-state optimistic update rolls back frequently due to intermittent connectivity, users will observe notifications toggling between read and unread, creating confusion and distrust of the feature.
Mitigation & Contingency
Mitigation: Queue failed mutations in a local retry store and re-attempt on next connectivity event using a connectivity-aware retry service. Show a non-intrusive banner if offline rather than applying optimistic updates.
Contingency: Disable optimistic updates for mark-as-read in low-connectivity scenarios detected by the connectivity provider, instead showing a loading indicator until server confirmation.