feat(app): native app features — haptics, app-icon shortcuts, share-into-Freegle (Android + iOS)#766
Open
edwh wants to merge 7 commits into
Open
feat(app): native app features — haptics, app-icon shortcuts, share-into-Freegle (Android + iOS)#766edwh wants to merge 7 commits into
edwh wants to merge 7 commits into
Conversation
✅ Deploy Preview for golden-caramel-d2c3a7 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This was referenced Jun 16, 2026
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
2f73742 to
e692a66
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
@capacitor/haptics+ auseHapticscomposable, wired intostores/mobile.js(e.g. a success haptic on chat reply, best-effort / in-app only).shortcuts.xml+ manifest<meta-data>) and iOS (UIApplicationShortcutItemsinInfo.plist+AppDelegatehandling).ACTION_SEND/ACTION_SEND_MULTIPLEimage/*intent-filters handled natively inMainActivity, surfaced into the give-photos flow.ShareViewController, entitlements, app group), Fastlane target wiring (add_share_extension_target.rb), and the matching give-photos flow.Notes
Info.plistcarry both the shortcuts and share additions (merged; verified well-formed). Also rebased over master's fix(modtools): drop restricted media permissions to pass Google Play review #760 media-permissions cleanup with no regression.master.🤖 Generated with Claude Code