Skip to content

Mobile caf selfies real backend#3771

Merged
Baalmart merged 9 commits into
stagingfrom
mobile-caf-selfies-real-backend
Jul 8, 2026
Merged

Mobile caf selfies real backend#3771
Baalmart merged 9 commits into
stagingfrom
mobile-caf-selfies-real-backend

Conversation

@Mozart299

@Mozart299 Mozart299 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added a new “Forum filter” sharing flow with camera/gallery photo selection, preview, consent toggle, and support for sharing a branded image.
    • Improved in-app status banners with optional actions and clearer feedback during sharing and submission.
  • Bug Fixes

    • Sharing now handles missing images and submission failures more gracefully with clearer retry messages.
    • Messages can still appear even if the sheet is dismissed, improving reliability.
    • Accessibility announcements were added for inline updates.

Mozart299 added 6 commits July 8, 2026 15:00
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.
@Mozart299 Mozart299 self-assigned this Jul 8, 2026
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Mozart299, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad3bc2b3-3345-4752-9d63-69f3eca20a59

📥 Commits

Reviewing files that changed from the base of the PR and between 861d3b6 and 3d9e1fe.

📒 Files selected for processing (4)
  • src/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dart
  • src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart
  • src/mobile/lib/src/app/shared/services/analytics_service.dart
  • src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart
📝 Walkthrough

Walkthrough

This PR refactors the Air Quality share sheet into shared, reusable widgets (share_sheet_widgets.dart), extracts a new CleanAirForumFilterTab widget handling selfie capture and wall submission, reworks CleanAirForumSubmissionService with typed exceptions and a new API endpoint/auth flow, and adds a testing seam to AnalyticsService.

Changes

Share sheet refactor and forum filter feature

Layer / File(s) Summary
Shared share-sheet UI helpers
src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart
Adds captureShareBoundary, ShareSheetMessenger typedef, InlineMessageBanner, ShareTabChip, ShareActionButton, and CheckerboardBackground.
Submission service typed failures and API rework
src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart
Adds SelfieSubmissionFailure/SelfieSubmissionException, injects TokenRefresher, switches endpoint and JWT auth, changes submitSelfie to return Future<String?>, and reworks Cloudinary upload error handling.
AnalyticsService testing seam
src/mobile/lib/src/app/shared/services/analytics_service.dart
Makes the singleton mutable and adds forTesting() constructor plus a @visibleForTesting instance setter.
CleanAirForumFilterTab widget
src/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dart
New widget managing selfie capture (camera/gallery), preview rendering, share/capture flow, background wall submission with retry, consent toggle, and error messaging.
AirQualityShareSheet integration
src/mobile/lib/src/app/dashboard/widgets/air_quality_share_sheet.dart
Refactors imports, constructor (adds submissionService), messaging (_showMessage with actions/accessibility/fallback SnackBar), share/copy flows using captureShareBoundary, and swaps in shared widgets and CleanAirForumFilterTab.

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
Loading

Possibly related PRs

Suggested reviewers: Baalmart

Poem

A rabbit hopped through share sheets bright,
Swapped old chips for tabs polished tight.
Selfies now snap, submit, retry,
Checkerboards shimmer beneath the sky.
Tokens refreshed, exceptions typed clean —
Hop, hop, hooray for this tidy scene! 🐰📸

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: mobile Clean Air Forum selfies now use the real backend.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mobile-caf-selfies-real-backend

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Clean testing seam — consider adding a reset helper for tearDown.

The mutable singleton with forTesting() and the instance setter is the idiomatic Flutter test-seam pattern. One small gap: tests that override instance must manually save and restore the original in tearDown, 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

📥 Commits

Reviewing files that changed from the base of the PR and between f2321fa and 861d3b6.

📒 Files selected for processing (5)
  • src/mobile/lib/src/app/dashboard/widgets/air_quality_share_sheet.dart
  • src/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dart
  • src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart
  • src/mobile/lib/src/app/shared/services/analytics_service.dart
  • src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart

Comment thread src/mobile/lib/src/app/dashboard/widgets/clean_air_forum_filter_tab.dart Outdated
Comment thread src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart Outdated
Comment thread src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart Outdated
Comment thread src/mobile/lib/src/app/shared/services/clean_air_forum_submission_service.dart Outdated
- 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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CleanAirForumFilterTab and 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.

Comment thread src/mobile/lib/src/app/dashboard/widgets/share_sheet_widgets.dart Outdated
Comment thread src/mobile/lib/src/app/shared/services/analytics_service.dart Outdated
Mozart299 and others added 2 commits July 8, 2026 16:33
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>
@Baalmart
Baalmart merged commit ebe0577 into staging Jul 8, 2026
22 of 24 checks passed
@Baalmart
Baalmart deleted the mobile-caf-selfies-real-backend branch July 8, 2026 14:32
@Baalmart Baalmart mentioned this pull request Jul 8, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants