Mobile caf selfies real backend#3771
Conversation
Swap the temporary website mock API for the new POST /api/v2/users/selfies endpoint. Drops the interim shared-secret header and attaches the user's JWT when a valid one is available so their real name shows on the wall; anonymous submissions continue to work unchanged.
Mirrors the BaseRepository DIP pattern so tests can swap the refresher, and tightens the eventId doc to state the wall-matching constraint.
Omit optional fields instead of sending explicit nulls, and default the event ID to clean-air-forum-2026 to match the wall's current event.
Lets widget tests exercise the consent-to-wall flow with a fake service instead of real HTTP; production behavior is unchanged (defaults to the shared instance).
- AnalyticsService: the singleton behind the factory is now swappable via a visibleForTesting setter, giving every existing AnalyticsService() call site a test seam without changing the call convention. - Extract the forum filter tab (selfie acquisition, consent, wall submission) into clean_air_forum_filter_tab.dart; selfie and consent state stay lifted in the sheet so they survive tab switches. - Move shared presentational widgets and the RepaintBoundary capture helper into share_sheet_widgets.dart. No behavior changes; air_quality_share_sheet.dart drops from 916 to 420 lines.
- Typed SelfieSubmissionException (offline/timeout/server) so failures get
short, accurate messages and analytics record a real category.
- Permission denials link to Settings instead of saying 'try again'.
- Wall submission shows an in-flight status row, offers Retry on failure,
and reports the display name the wall used ('You're on the wall as X!').
- If the sheet is closed before the background submission finishes, the
result falls back to a root SnackBar instead of being dropped.
- Inline banner: error styling, optional action button, duration scaled to
message length, and a screen-reader announcement.
- All messages shortened.
|
Warning Review limit reached
Next review available in: 24 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR refactors the Air Quality share sheet into shared, reusable widgets ( ChangesShare sheet refactor and forum filter feature
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant CleanAirForumFilterTab
participant captureShareBoundary
participant AirQualityShareService
participant CleanAirForumSubmissionService
User->>CleanAirForumFilterTab: tap Share
CleanAirForumFilterTab->>captureShareBoundary: capture preview bytes
captureShareBoundary-->>CleanAirForumFilterTab: PNG bytes
CleanAirForumFilterTab->>AirQualityShareService: shareCleanAirForumFilter(bytes)
CleanAirForumFilterTab->>CleanAirForumSubmissionService: submitSelfie(bytes, measurement) [if consent]
CleanAirForumSubmissionService-->>CleanAirForumFilterTab: wall display name or SelfieSubmissionException
CleanAirForumFilterTab-->>User: show success/failure message with retry
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Git: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
src/mobile/lib/src/app/shared/services/analytics_service.dart (1)
20-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winClean testing seam — consider adding a reset helper for tearDown.
The mutable singleton with
forTesting()and theinstancesetter is the idiomatic Flutter test-seam pattern. One small gap: tests that overrideinstancemust manually save and restore the original intearDown, or risk polluting other tests. A tiny helper would make cleanup foolproof.♻️ Optional: add a reset helper for test teardown
`@visibleForTesting` static set instance(AnalyticsService value) => _instance = value; + + /// Restore the default instance — call in tearDown to avoid test pollution. + `@visibleForTesting` + static void resetInstance() => _instance = AnalyticsService._internal();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mobile/lib/src/app/shared/services/analytics_service.dart` around lines 20 - 35, Add a test-only reset helper for the AnalyticsService singleton so overridden instances can be restored automatically in tearDown. The current AnalyticsService.instance setter and AnalyticsService.forTesting() create a shared mutable test seam, but tests still need a safe way to return to the default _instance after swapping in fakes. Introduce a small `@visibleForTesting` reset method on AnalyticsService that resets the shared singleton back to the original internal instance, and use it from tests instead of manually saving/restoring state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/mobile/lib/src/app/dashboard/widgets/air_quality_share_sheet.dart`:
- Around line 186-188: The Duration construction in the share sheet widget is
passing a num from clamp(...) into milliseconds, so update the duration
calculation in the air quality share sheet logic to convert the clamped value to
an int before creating the Duration. Use the existing duration assignment near
the message.length-based timeout and ensure the final milliseconds argument is
an int so the constructor type matches.
In `@src/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dart`:
- Around line 252-253: Guard _submitToConferenceWall against concurrent
invocations by checking the existing in-flight state before starting a new
request. Use the _isSendingToWall flag in CleanAirForumFilterTabState to return
early if a wall submission is already running, and only set it true once per
accepted submission; make sure it is cleared in the completion path so Retry or
repeated share taps cannot enqueue duplicate wall submissions.
In `@src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart`:
- Around line 27-30: Dispose the captured ui.Image after encoding in the share
capture flow. In the method that calls RenderRepaintBoundary.toImage() and then
toByteData() in share_sheet_widgets.dart, wrap the image usage so
image.dispose() is always called in a finally block after encoding completes or
fails, while keeping the byteData return behavior unchanged.
In
`@src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart`:
- Line 124: The info log in CleanAirForumSubmissionService is leaking a
sensitive uploaded selfie URL, so remove the direct use of imageUrl from the
submission log. Update the logging in the submission flow to either omit the URL
entirely or replace it with a non-sensitive identifier, keeping the change
localized to the Clean Air Forum submission path and the loggy.info call.
- Around line 182-188: The Cloudinary response parsing in
clean_air_forum_submission_service.dart is throwing raw jsonDecode/cast errors
instead of the expected SelfieSubmissionException. Update the response handling
in the upload flow so the parsing around response.body, jsonDecode, and
secure_url extraction is wrapped in the typed failure model. Catch malformed or
non-map 200 responses and rethrow them as SelfieSubmissionException with
SelfieSubmissionFailure.server, keeping the UI/analytics contract intact.
---
Nitpick comments:
In `@src/mobile/lib/src/app/shared/services/analytics_service.dart`:
- Around line 20-35: Add a test-only reset helper for the AnalyticsService
singleton so overridden instances can be restored automatically in tearDown. The
current AnalyticsService.instance setter and AnalyticsService.forTesting()
create a shared mutable test seam, but tests still need a safe way to return to
the default _instance after swapping in fakes. Introduce a small
`@visibleForTesting` reset method on AnalyticsService that resets the shared
singleton back to the original internal instance, and use it from tests instead
of manually saving/restoring state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 980a1b75-f12e-4744-80be-264808e6ba30
📒 Files selected for processing (5)
src/mobile/lib/src/app/dashboard/widgets/air_quality_share_sheet.dartsrc/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dartsrc/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dartsrc/mobile/lib/src/app/shared/services/analytics_service.dartsrc/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart
- guard _submitToConferenceWall against concurrent Retry/share taps - dispose the captured ui.Image after PNG encoding - stop logging the uploaded selfie URL - surface malformed Cloudinary 200 responses as SelfieSubmissionException
There was a problem hiding this comment.
Pull request overview
This PR updates the mobile share sheet’s “Forum filter” flow to use the AirQo backend for selfie submissions, adds a dedicated filter tab widget with improved UX around photo picking/sharing/consent, and introduces a reusable set of share-sheet UI utilities.
Changes:
- Switch Clean Air Forum selfie submission from the temporary wall API to
POST /api/v2/users/selfies, with structured failure types and optional JWT attachment. - Refactor the share sheet by extracting the forum-filter UI into
CleanAirForumFilterTaband centralizing common UI helpers (capture, tabs, banner, buttons). - Improve inline status messaging (error styling, optional action, accessibility announcement) and add a test seam for analytics.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart | Routes selfie submission to the AirQo backend and introduces typed failure handling + display-name parsing. |
| src/mobile/lib/src/app/shared/services/analytics_service.dart | Adds a test seam to swap the singleton analytics instance. |
| src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart | Introduces reusable share-sheet helpers (capture, banner, tabs, buttons, checkerboard). |
| src/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dart | New forum-filter share tab: selfie source selection, preview, consent, share, and wall submission messaging. |
| src/mobile/lib/src/app/dashboard/widgets/air_quality_share_sheet.dart | Refactors share sheet to use the new tab widget and improved inline messaging behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Summary by CodeRabbit
New Features
Bug Fixes