high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

ActivityHistoryList widget renders a scrollable list of activity items from the paginated stream without layout overflows on any screen width from 320pt to 428pt
Each list item displays: typeLabel (bold), formatted date (e.g., '12 Mar 2025'), duration in human-readable format (e.g., '45 min'), and chapterAttribution as secondary text when non-null
Empty state: when the stream emits an empty first page, a centred illustration and localised copy ('No activities recorded yet') are shown instead of an empty list
Loading state for first page: a shimmer or skeleton list of 5 placeholder items is shown while ActivityHistoryLoading is emitted
Loading state for subsequent pages: a centred CircularProgressIndicator is appended below the last item while the next page loads
Infinite scroll: ScrollController listener fires fetchNextPage() when scroll position reaches ≥80% of maxScrollExtent; no duplicate calls if already loading
When hasNextPage is false, no further fetch is triggered regardless of scroll position
Error state: a retry button and error message are shown inline if the first page fails; for subsequent page failures, a snackbar is shown and previously loaded items remain visible
Widget uses design token values (colours, spacing, typography) from the app's token system — no hard-coded hex values or pixel sizes
Widget is stateless at the top level and reads state from Riverpod provider passed via constructor or ref.watch()

Technical Requirements

frameworks
Flutter
Riverpod
data models
ActivityHistoryItem
ActivityHistoryState
performance requirements
ListView uses `itemExtent` or `prototypeItem` to enable O(1) scroll position calculation for large lists
Widget rebuild is scoped — only the loading indicator re-renders on page fetch, not the entire list
Scroll listener uses `addListener` with a named callback to allow proper disposal and prevent memory leaks
security requirements
All text content from ActivityHistoryItem is treated as user-supplied data — no HTML rendering or dynamic code execution
Chapter attribution text is truncated to max 60 chars with ellipsis to prevent layout overflow from unusually long strings
ui components
ActivityHistoryItem tile widget (extract as separate widget for testability)
ActivityHistoryEmptyState widget
ActivityHistoryLoadingShimmer widget (5 placeholder tiles)
Inline error/retry widget for first-page failure

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Extract `_ActivityHistoryTile` as a private stateless widget that takes a single `ActivityHistoryItem` — this makes widget testing isolated and keeps the main list widget lean. For the 80% scroll threshold, use: `if (controller.position.pixels >= controller.position.maxScrollExtent * 0.8 && !state.isLoading && state.hasNextPage) { ref.read(provider.notifier).fetchNextPage(); }`. Dispose the ScrollController in the StatefulWidget's dispose() if the widget owns it, or use a `useScrollController()` hook if flutter_hooks is available. For date formatting, use the `intl` package's `DateFormat.yMMMd(locale)` to respect device locale — do not hard-code 'en' locale.

Duration formatting: prefer '1 h 15 min' over '75 min' for durations ≥60 minutes. The shimmer loading state can use a simple opacity-animated Container with the design token surface-secondary colour rather than adding a shimmer package dependency.

Testing Requirements

Widget tests using flutter_test: (1) renders correct item count for a 3-item fixture state, (2) shows empty state widget when items list is empty and isLoading is false, (3) shows shimmer when isLoading is true and items is empty, (4) shows inline loading indicator when isLoading is true and items is non-empty, (5) scroll to 85% triggers fetchNextPage call on the mock notifier, (6) scroll at 100% with hasNextPage=false does NOT call fetchNextPage. Use pumpWidget with a ProviderScope override. Run `flutter test --update-goldens` to generate golden images for the item tile widget for regression protection.

Component
Activity History List
ui low
Epic Risks (3)
medium impact medium prob scope

The encrypted-field-display confirmation dialog adds interaction steps that may frustrate coordinators who access sensitive fields frequently, leading to requests to bypass the flow or skip read-receipt logging, which would violate Blindeforbundet's compliance requirements.

Mitigation & Contingency

Mitigation: Design the confirmation dialog to be as minimal as possible (one clear sentence, single confirm action) and ensure it does not reappear for the same field within a single screen session. Validate the UX with Blindeforbundet coordinators during the TestFlight pilot before finalising.

Contingency: If coordinators raise strong objections, escalate to Blindeforbundet's data protection officer to determine whether a lighter confirmation pattern (e.g., biometric confirmation instead of dialog) satisfies their compliance obligation.

medium impact medium prob technical

The activity-history-list infinite scroll requires paginated Supabase queries. Contacts with hundreds of activities (e.g., an HLF peer mentor with 380 annual registrations) could cause slow page loads or memory pressure on older devices if pagination boundaries are set too large.

Mitigation & Contingency

Mitigation: Use a page size of 20 records with cursor-based pagination. Implement list item recycling using Flutter's ListView.builder. Benchmark memory usage with 400+ item simulation on a low-end test device before TestFlight release.

Contingency: If performance degrades on older devices, reduce page size to 10 and add a time-window filter (last 30 days, last 6 months, all) so the default view loads a manageable record count for most coordinators.

low impact high prob scope

The cognitive load rule engine (from the Cognitive Accessibility feature) mandates no more than 7 fields per screen section. If a contact model has more than 7 editable fields, the edit-contact-screen layout must be split into sections, adding complexity not accounted for in the initial scope.

Mitigation & Contingency

Mitigation: Audit the full contact field list from all four organisations before implementation. Group fields into logical sections (personal info, contact details, affiliation) so no single section exceeds 7 fields. Use the cognitive-load-rule-engine component if it is already delivered by the Cognitive Accessibility feature.

Contingency: If the rule-engine component is not yet available, implement a simple manual section layout with accordion-style expansion for less-frequently edited fields to stay within the 7-field guideline without blocking delivery.