Skip to content

feat(app): native app features — haptics, app-icon shortcuts, share-into-Freegle (Android + iOS)#766

Open
edwh wants to merge 7 commits into
masterfrom
feat/app-native-features
Open

feat(app): native app features — haptics, app-icon shortcuts, share-into-Freegle (Android + iOS)#766
edwh wants to merge 7 commits into
masterfrom
feat/app-native-features

Conversation

@edwh

@edwh edwh commented Jun 16, 2026

Copy link
Copy Markdown
Member

Consolidates four native-app feature PRs into one — they're all small, independent app capabilities and don't need separate review/merge. Replaces #756, #757, #758, #759, rebased cleanly onto current master.

What's included

Notes

🤖 Generated with Claude Code

@netlify

netlify Bot commented Jun 16, 2026

Copy link
Copy Markdown

Deploy Preview for golden-caramel-d2c3a7 ready!

Name Link
🔨 Latest commit e692a66
🔍 Latest deploy log https://app.netlify.com/projects/golden-caramel-d2c3a7/deploys/6a4a95b0c440130008502c4e
😎 Deploy Preview https://deploy-preview-766--golden-caramel-d2c3a7.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

edwh and others added 7 commits July 5, 2026 18:34
Adds @capacitor/haptics and a useHaptics() composable — a no-op on web,
lazily importing the native plugin in-app, exposing light/medium/heavy
impacts and success/warning/error notification feedback (all best-effort).
First wiring: a success haptic when a chat reply is sent from a push
notification (mobile store handleReplyAction). Reusable for posting,
sending, swipe actions etc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds static Android app shortcuts: long-press the launcher icon → Give,
Ask, or My Chats. Each launches MainActivity with a
https://www.ilovefreegle.org/<path> VIEW intent, which the existing
deep-link intent-filter + the appUrlOpen handler (stores/mobile.js) route
to /give, /find, /chats. No new native code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UIApplicationShortcutItems in Info.plist + a performActionFor handler in
AppDelegate that maps each shortcut to a https://www.ilovefreegle.org/<path>
URL and routes it through the same ApplicationDelegateProxy pipeline as a
deep link, so appUrlOpen (stores/mobile.js) navigates to /give, /find, /chats.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds an Android "Share -> Freegle" target: share a photo from the gallery,
camera or any other app straight into a new OFFER with the image
pre-attached, instead of having to open the app and re-pick the photo.

- AndroidManifest: ACTION_SEND / ACTION_SEND_MULTIPLE image/* intent-filters
  on MainActivity.
- MainActivity: copies the shared image(s) from the intent-scoped content://
  Uri into the app cache and exposes them to the WebView via a small
  window.FreegleShare JS bridge (consume()).
- stores/mobile: pulls shared images at startup (cold share) and on resume
  (warm share), converts the cache paths with Capacitor.convertFileSrc, and
  routes into /give/mobile/photos with them queued.
- PhotoUploader: exposes processPhoto() so a shared photo runs the same
  quality-check + tus-upload path as the camera/gallery flows.
- give/mobile/photos: on mount, attaches any queued shared image(s).

iOS Share Extension to follow (needs an App Group + provisioning changes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iOS half of Share-into-Freegle (Android is in the parent commit). Adds an
iOS share-sheet extension so a photo can be shared straight into a new OFFER
with the image pre-attached.

DARK / FLAG-GATED: everything is a no-op unless ENABLE_SHARE_EXTENSION=true.
By default the Xcode project, the main app's entitlements and signing are
byte-for-byte unchanged, so this cannot affect the weekly App Store release
until deliberately enabled. Enabling first requires (human, Apple-portal):
  1. Register App Group group.org.ilovefreegle.iphone and enable it on both
     App IDs (org.ilovefreegle.iphone and .ShareExtension).
  2. Pre-create the .ShareExtension App ID + App Store distribution profile.
  3. Regenerate the main app profile so it includes the App Group.
  4. Set ENABLE_SHARE_EXTENSION=true in the deploy-apps CircleCI context.
Then on-device verification.

- ios/App/ShareExtension/ShareViewController.swift — no-UI share handler:
  copies shared image(s) into the App Group container and opens the host app
  with freegleshare://shared?p=<path>... (responder-chain openURL).
- ios/App/ShareExtension/Info.plist — share-services extension, image
  activation rule, programmatic principal class (no storyboard).
- ios/App/ShareExtension/ShareExtension.entitlements — App Group.
- ios/App/App/Info.plist — register the freegleshare:// URL scheme.
- fastlane/add_share_extension_target.rb — gated; adds the target + (build-time
  only) the App Group entitlement to the main app. Mirrors add_nse_target.rb.
- fastlane/Fastfile — gated wiring + signing + export map, composed so a
  disabled build is identical to today's app(+NSE) export.
- stores/mobile: appUrlOpen handles freegleshare://shared, queuing the
  image(s) into the same pendingSharedImages give-flow path as Android.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…xclude both from playwright coverage

useHaptics.js and pages/give/mobile/photos.vue were added without tests,
dropping vitest coverage and adding 0% lines to the playwright denominator.

- Add useHaptics.spec.js: exercises all 6 haptic methods, no-op on web
  (isApp=false), correct Haptics.impact/notification calls on native,
  and silent error swallowing.
- Add give/mobile/photos.spec.js: mounts the page, verifies compose-store
  integration (new vs reused Offer message), Next-button visibility, goNext
  navigation, and the onMounted pending-shared-images flow.
- Exclude useHaptics and give/mobile/photos from playwright sourceFilter:
  native-only haptics and the app-only give-flow route are never exercised
  by the browser e2e suite, so they were pure denominator noise (mirrors the
  ChatMobileNavbar exclusion pattern).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vi.stubGlobal('useRouter', ...) does not intercept the auto-imported
useRouter (resolved via the globalThis.__testUseRouter hook in
tests/unit/setup.ts), so router.push was never the spy and
'goNext() navigates to /give/mobile/details' failed (0 calls). Switch to
overriding __testUseRouter, matching the repo convention (see
tests/unit/pages/volunteering/id.spec.js). This failure was masked until
now because npm ci failed on the srvx lock, so vitest never ran.

Verified locally: all 8 photos.spec.js tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EgWPgdxq4drrQ1jjrynVVZ
@edwh edwh force-pushed the feat/app-native-features branch from 2f73742 to e692a66 Compare July 5, 2026 17:34
@FreegleGeeks FreegleGeeks added CI: Running CI build is currently running CI: Passed CI build passed and removed CI: Passed CI build passed CI: Running CI build is currently running labels Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI: Passed CI build passed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants