high priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

Tapping any thumbnail cell triggers a signed URL fetch from AttachmentSignedUrlService before navigating to the preview modal
Navigation to the preview modal passes the resolved signed URL and MIME type as arguments
A loading indicator is displayed on the tapped cell while the signed URL is being fetched
If signed URL fetch fails, a snackbar or inline error message is shown and navigation is cancelled
A delete icon button is shown on each thumbnail cell only when the current user is authorised to delete it (coordinator: all cells; peer mentor: only own uploads; other roles: no cells)
Delete button has a minimum touch target of 44x44 logical pixels per WCAG 2.2 AA
Tapping delete triggers a confirmation dialog before calling the delete action
After successful deletion, the cell is removed from the grid without requiring a full page reload
After failed deletion, the cell remains and an error snackbar is shown
Role evaluation is derived from the authenticated user's role and uploadedBy field on the attachment — no server-side dependency for the render decision
Delete button and tap gesture each have accessible semantic labels

Technical Requirements

frameworks
Flutter
Riverpod or BLoC
go_router or Navigator 2.0 for modal navigation
apis
AttachmentSignedUrlService.getSignedUrl(attachmentId)
ActivityAttachmentRepository.deleteAttachment(attachmentId)
Auth state provider for current user role and userId
data models
ActivityAttachment
AttachmentMetadata
UserRole
SignedUrlResult
performance requirements
Signed URL fetch must not block UI thread — use async/await with loading state on the individual cell
Delete optimistic update: remove cell from local state immediately, revert on failure
security requirements
Role check must compare attachment.uploadedById == currentUser.id for peer mentor — never trust client-supplied role override
Delete must be confirmed server-side via RLS policy; client-side role check is a UX guard only
ui components
GestureDetector or InkWell wrapping thumbnail cell for tap
IconButton with Icons.delete_outline for delete action
CircularProgressIndicator overlay on tapped cell during URL fetch
AlertDialog for delete confirmation
SnackBar for error feedback

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Wrap the thumbnail cell's existing GestureDetector (or add one) with an onTap that dispatches a 'fetchSignedUrlAndNavigate' event/action. Store per-cell loading state in a local ValueNotifier or a small Riverpod StateProvider keyed by attachmentId to avoid rebuilding the entire grid. For role-gating, read the auth provider to get currentUser.id and currentUser.role; render the delete button conditionally using a helper function canDeleteAttachment(attachment, currentUser) → bool. Position the delete IconButton as an Align(alignment: Alignment.topRight) overlay within the thumbnail Stack to avoid layout disruption.

Use showDialog for the confirmation prompt and await its bool result before calling the delete action. Handle optimistic removal by updating the local attachment list state and reverting on error.

Testing Requirements

Widget tests: verify tap on thumbnail calls AttachmentSignedUrlService.getSignedUrl with correct attachmentId; verify navigation is triggered with correct arguments after URL resolution; verify delete button is rendered for coordinator role on all cells; verify delete button is rendered only on own attachment for peer mentor role; verify delete button is absent for viewer/read-only roles; verify confirmation dialog appears on delete tap; verify cell is removed after confirmed delete. Mock all service and repository calls with Riverpod overrides or BLoC fakes.

Component
Attachment Thumbnail Grid
ui low
Epic Risks (4)
medium impact medium prob dependency

Flutter does not include a first-party PDF renderer. Third-party packages (e.g., flutter_pdfview, syncfusion_flutter_pdf) have inconsistent accessibility support, may not honour dynamic type scaling, and can introduce large binary size increases. Choosing the wrong package late in development risks rework or an inaccessible PDF experience for Blindeforbundet users.

Mitigation & Contingency

Mitigation: Evaluate and spike two PDF rendering packages (flutter_pdfview and pdfx) in the first task of this epic before committing to implementation. Criteria: VoiceOver compatibility, dynamic type support, APK/IPA size delta, and licence. Document the decision in an ADR.

Contingency: If no package meets accessibility requirements, fall back to opening PDFs in the system browser via url_launcher, which inherits the OS's accessible PDF viewer. Display a clear 'Opening in external viewer' message to set user expectation.

medium impact medium prob technical

Flutter's Semantics API for live region announcements (analogous to aria-live) has known gaps on Android for dynamic content updates. Upload progress announcements required by the accessibility user story may not fire reliably on Android devices used by HLF and NHF members.

Mitigation & Contingency

Mitigation: Use SemanticsService.announce() for imperative announcements at each upload state transition rather than relying on declarative Semantics widget tree updates. Test on a physical Android device with TalkBack enabled during development, not only on iOS with VoiceOver.

Contingency: If SemanticsService.announce() proves unreliable, implement a persistent accessible status banner at the top of the screen that reflects the current upload state as plain text, satisfying the WCAG success criterion through a visual + programmatic mechanism.

low impact high prob technical

iOS and Android file picker behaviour diverges significantly: MIME type filtering works reliably on Android but is advisory on iOS (users can still navigate to and select non-compliant files via the Files app). This could allow unsupported file types to reach the upload service, causing validation failures with a confusing user experience.

Mitigation & Contingency

Mitigation: Rely on the AttachmentUploadService (Epic 2) as the authoritative MIME validation gate, regardless of platform. In the UI, re-validate the picked file's extension/MIME after selection and show an inline plain-language error ('Only PDF, JPEG, and PNG files are supported') before even calling the service.

Contingency: If users consistently hit the error due to iOS file picker limitations, add a user-facing help tooltip on the picker sheet explaining the supported file types, reducing support volume while the underlying OS limitation persists.

high impact medium prob scope

WCAG 2.2 AA compliance for three new interactive components (picker, grid, modal) with file system integration, role-gated actions, and live regions is a significant accessibility surface area. An incomplete audit before release could result in the feature being unusable for Blindeforbundet screen reader users at launch.

Mitigation & Contingency

Mitigation: Allocate a dedicated accessibility audit task at the end of the epic using the accessibility-test-harness (619) and wcag-compliance-checker (620) components already in the project. Include at least one manual test session on a physical device with VoiceOver enabled. Fail the epic's definition of done if any WCAG 2.2 AA violation remains open.

Contingency: If critical accessibility issues are found late, gate the feature behind the org-level attachments_enabled feature flag and only enable it for NHF (the requesting org) after a targeted fix cycle, rather than delaying the release for all organisations.