high priority low complexity infrastructure pending infrastructure specialist Tier 1

Acceptance Criteria

ShareSheetBridge is a Dart class in lib/features/recruitment/data/services/ with a method shareReferralCode({required String referralUrl, required String mentorName, required String orgName, Uint8List? qrImageBytes}) → Future<void>
The composed share message is exactly: 'Hei! Jeg er likeperson i [orgName]. Bli med via denne lenken: [referralUrl]' with orgName and referralUrl interpolated
When qrImageBytes is null, the share sheet is invoked with text and subject only (no attachment)
When qrImageBytes is provided, the QR image is written to a temporary file (e.g., via path_provider) and passed as an XFile to Share.shareXFiles; the temporary file is deleted after the share sheet closes
ShareResultStatus.dismissed is treated as a no-op (no error thrown); ShareResultStatus.unavailable throws ShareSheetUnavailableException
Any PlatformException from share_plus is caught and wrapped in a ShareSheetBridgeException with the original message
shareReferralCode is an async method that awaits the share sheet completion before returning
The service is registered as a Riverpod Provider
No UI code (BuildContext, widgets) is referenced in the service

Technical Requirements

frameworks
Flutter
share_plus (Share.shareXFiles, ShareResultStatus)
path_provider (getTemporaryDirectory)
Riverpod
apis
Native iOS Share Sheet (UIActivityViewController)
Native Android Share Sheet (ACTION_SEND intent)
performance requirements
Temporary file write for QR image must complete in < 50 ms for a 400×400 PNG
Temporary files must be cleaned up after each share operation to avoid accumulating disk usage
security requirements
Temporary QR image files must be written to the app's sandboxed temporary directory only (getTemporaryDirectory), not to publicly accessible storage
The referralUrl must be validated as a non-empty string before composing the share message to prevent blank links being shared

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Introduce a ShareAdapter abstract class with a single shareXFiles method if mocking share_plus in tests proves impractical. The concrete implementation wraps Share.shareXFiles and a stub/mock is used in tests. For the temporary file name, use a fixed name like 'referral_qr.png' (overwritten each time) rather than a UUID to avoid orphaned files. Use path_provider's getTemporaryDirectory and delete the file in a finally block after Share.shareXFiles completes.

The subject line (shown on iOS) should be set to 'Bli likeperson i [orgName]' for clarity in the share sheet preview. Ensure the service handles the case where path_provider throws (e.g., storage unavailable) by surfacing a ShareSheetBridgeException.

Testing Requirements

Unit tests with flutter_test and mocktail mocking the share_plus Share class (or wrap it in an injectable interface for testability): (1) correct message is composed with provided orgName and referralUrl; (2) shareXFiles is called with an XFile when qrImageBytes is non-null; (3) Share.share (text-only) is called when qrImageBytes is null; (4) ShareResultStatus.dismissed does not throw; (5) ShareResultStatus.unavailable throws ShareSheetUnavailableException; (6) PlatformException is wrapped in ShareSheetBridgeException. Integration test on a physical device or simulator verifying the native share sheet appears and temporary files are cleaned up. Note: share_plus is difficult to mock without an abstraction layer — introduce a thin ShareAdapter interface if needed.

Component
Share Sheet Bridge
infrastructure low
Epic Risks (3)
high impact medium prob technical

iOS Universal Links and Android App Links have distinct configuration requirements (apple-app-site-association, assetlinks.json, entitlements). A misconfiguration causes the OS to open the referral URL in a browser instead of the app, completely breaking the onboarding funnel for new members on one platform.

Mitigation & Contingency

Mitigation: Configure both Universal Links and App Links from the start of this epic using the project's existing Supabase-hosted domain. Write an E2E test on both simulators that taps a referral URL and asserts the onboarding screen is reached. Document the required server-side JSON files alongside the migration.

Contingency: If platform deep-link configuration cannot be resolved before the UI epics need the handler, implement a fallback custom-scheme URI (e.g., likeperson://referral?code=XYZ) that works unconditionally, and schedule Universal/App Link fix as a follow-up task.

high impact high prob security

Referral click events must be writable without an authenticated session (a new member who has not yet registered is tapping the link). Standard Supabase RLS cannot grant anonymous inserts without opening a security hole. If this is not solved early it blocks the entire attribution pipeline.

Mitigation & Contingency

Mitigation: Design referral_events writes to go exclusively through a Supabase Edge Function that validates the referral code exists and is active before inserting. The Edge Function uses the service-role key server-side; the client only calls the function endpoint. This is documented in the feature spec.

Contingency: If the Edge Function approach is delayed, temporarily allow anon inserts restricted by a CHECK constraint that event_type = 'click' and new_member_id IS NULL, then tighten to Edge Function writes in a follow-up migration before the feature goes to production.

medium impact low prob dependency

The qr_flutter package version pinned in pubspec may conflict with the current Flutter SDK version or with other packages in the monorepo, causing build failures that block QR code delivery.

Mitigation & Contingency

Mitigation: Verify qr_flutter compatibility against the project's Flutter SDK version as the very first task in this epic. If a conflict exists, resolve it before any other work proceeds.

Contingency: If qr_flutter cannot be made compatible, evaluate mobile_scanner (already likely in pubspec for QR scanning) which also supports generation, or implement QR generation via a lightweight Dart port as a last resort.