high priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

Swiping the card horizontally (left or right) triggers the dismiss action — a Dismissible widget wraps ReminderNotificationCard with a visible dismiss background
An explicit 'Mark as read' action button or icon is present on the card and triggers the mark-read action independently of swipe
On dismiss or mark-read action, an optimistic UI update immediately removes or visually marks the card without waiting for the backend response
If the backend call fails, the card is restored to its previous state (rollback) and an error snackbar/toast is shown to the user
The BLoC event (or Riverpod notifier method) for dismiss is distinct from mark-as-read — they map to separate state transitions
The in-app notification repository method is called with the correct notification ID and action type on each interaction
After a successful dismiss, the card is removed from the notifications list; after a successful mark-as-read, the card is visually de-emphasised (e.g., reduced opacity or 'read' indicator) but remains in the list
Swipe gesture meets accessibility requirements: an equivalent non-gesture action (button) is always available for users who cannot swipe
The dismiss background shows a meaningful icon and colour (e.g., trash icon on red background) that communicate the action before release
Widget tests confirm: (1) swipe triggers dismiss event, (2) button tap triggers mark-read event, (3) optimistic removal occurs before async completion, (4) state is rolled back when repository throws

Technical Requirements

frameworks
Flutter
Dart
BLoC
Riverpod
apis
In-app notification repository API (dismiss, markAsRead)
data models
InAppNotification (id, type, isRead, isDismissed)
NotificationState
performance requirements
Optimistic update must render within a single frame of the gesture completion
Dismissible animation must complete within Flutter's default 300 ms — do not block the animation on async calls
security requirements
Dismiss and mark-read actions must be scoped to the authenticated peer mentor's own notifications — never accept notification IDs from untrusted sources
ui components
Dismissible (Flutter built-in) wrapping the card
Dismiss background container with icon and colour from design tokens
Mark-as-read IconButton or TextButton using design token styles
Error SnackBar / Toast (project shared component)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use Flutter's `Dismissible` widget for swipe-to-dismiss — set `dismissThresholds` to 0.4 to prevent accidental dismissals. For optimistic updates with rollback, dispatch an optimistic state change immediately in the BLoC/notifier, then call the repository; on error, dispatch a revert event. Keep the rollback logic inside the BLoC/notifier, not in the widget — the widget only dispatches events and renders state. Ensure the `Dismissible` key is the notification ID (not an index) to prevent incorrect widget reuse during list updates.

The mark-as-read button should be accessible via `Semantics(label: 'Mark reminder as read', button: true)`. Test the rollback path explicitly — it is the most likely source of subtle bugs in optimistic UI patterns.

Testing Requirements

Widget tests using flutter_test with a mocked notification repository (mocktail): (1) simulate swipe-to-dismiss and verify the BLoC/notifier receives a DismissNotification event with the correct ID; (2) tap the mark-read button and verify a MarkNotificationRead event is dispatched; (3) stub the repository to throw an exception and verify the card reappears in the list and an error snackbar is displayed; (4) verify the dismiss animation completes without errors. Use `WidgetTester.drag` for swipe simulation. All tests must pass without a live Supabase connection.

Component
Reminder Notification Card
ui low
Epic Risks (2)
medium impact medium prob integration

The deep-link from the notification card to the assignment detail screen depends on the assignment detail route being stable and accepting an assignment ID parameter. If the routing contract is undocumented or changes during parallel development, the CTA will silently navigate to a fallback screen.

Mitigation & Contingency

Mitigation: Confirm the assignment detail route path and parameter contract with the team building or maintaining that screen before implementing the CTA. Add an integration test that asserts navigating from a mock notification card with a known assignment ID lands on the correct route.

Contingency: If the route is unstable, implement the deep-link as a late-bound string resolved from a central route registry, allowing the target route to be updated without changing the notification card.

medium impact medium prob technical

Visually distinguishing escalation cards from standard reminder cards using colour alone fails WCAG 1.4.1 (use of colour). Blindeforbundet users relying on screen readers must receive equivalent contextual information through semantics, not just visual styling.

Mitigation & Contingency

Mitigation: Use both colour and an icon/label difference to distinguish card types. Add explicit Semantics widgets with descriptive labels ('Escalation alert: peer mentor has not responded') so screen readers announce the type without visual context.

Contingency: If accessibility review flags the distinction, add a text badge ('Escalation') alongside the visual treatment as a code-change-only fix with no schema or service impact.