Skip to content

feat(app): image upload — camera capture + limited-access permission fix - #1712

Open
dohertykariann-code wants to merge 9 commits into
slopus:mainfrom
dohertykariann-code:feat/image-upload-camera
Open

dohertykariann-code wants to merge 9 commits into
slopus:mainfrom
dohertykariann-code:feat/image-upload-camera

Conversation

@dohertykariann-code

Copy link
Copy Markdown

Summary

This PR ships the image upload feature to iOS users by fixing two bugs and adding camera capture:

  • Permission fix: requestLibraryPermissionsAsync() can return 'limited' on iOS 14+ (PHPickerViewController user-selected access). The old check status !== 'granted' blocked these users even though the picker works fine with limited access. Exported isPermissionAccepted(status) helper handles 'granted' | 'limited'.

  • Camera capture: Added openCamera() using launchCameraAsync() + requestCameraPermissionsAsync(). On iOS/Android, pickImages() now shows a native action sheet ("Add Photo" → Photo Library / Take Photo / Cancel) instead of jumping straight to the library. On web, behavior is unchanged.

  • i18n: Added 5 new keys to all 11 language files (sourceTitle, sourceLibrary, sourceCamera, cameraPermissionTitle, cameraPermissionMessage). Camera permission description is already declared via the expo-camera plugin in app.config.js — no binary rebuild required.

Test plan

  • Toggle Settings → Experimental → Image Upload on
  • Tap the paperclip in a chat — should show action sheet with "Photo Library" and "Take Photo" options
  • "Photo Library" → opens picker; works with both full and limited (iOS 14+ user-selected) access
  • "Take Photo" → requests camera permission on first use, opens camera
  • Web: paperclip opens library picker directly (no action sheet)

Generated with Claude Code
via Happy

dohertykariann-code and others added 7 commits September 10, 2026 11:55
Extends the existing agentInputEnterToSend setting (already working on
web) to native iOS. Uses submitBehavior="submit" + onSubmitEditing on
the TextInput so Enter triggers send and suppresses the newline. Guards
against double-firing when autocomplete suggestions are visible.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
advisory review only, no hook enforcement.
com.slopus.happy.preview is registered under the upstream Happy team's
Apple Developer account; bundle IDs are globally unique across accounts,
not just within a team, so it can't be claimed under a different one.
…tablet detection

Five bugs found and fixed in one triage session:

- agentDefaults.ts: Claude's code-level default model was hardcoded to
  claude-opus-5; changed to claude-sonnet-5.
- MarkdownView.tsx: the code-block copy button was gated entirely on
  onMouseEnter/onMouseLeave (web-only events), so it never showed on
  iOS/Android. Now shown unconditionally on native.
- agentInputComposerBehavior.ts (new) + AgentInput.tsx: a rebase had
  reverted the returnKeyType computed from submitBehavior, so the
  enter-to-send return key never showed as "send" on external keyboards.
  Restored as a small testable pure function.
- deviceCalculations.ts: determineDeviceType's 9-inch tablet threshold
  misclassified iPad Air/Pro 11" (computes ~8.9in with the existing
  163pt/in constant) as a phone, which is why chat screens showed a
  broken glass header/back button on full-screen iPad while Settings
  (which doesn't hit the same tablet-conditional code path) was fine.
  Lowered the threshold to 8.6in, which correctly separates iPad Mini
  (~8.3in) from iPad Air/Pro 11" (~8.9in) using the same constant.
- apiSession.ts: chat titles depended entirely on the agent voluntarily
  calling the change_title tool, with no fallback. Added a deterministic
  fallback that derives a title from the first user message if no real
  title has arrived by the agent's first response, at no added cost.

All five verified with tests: 43/43 (opus default + copy button areas
combined with existing suites), 7/7 new composer-behavior tests, 26/26
device-calculation tests (full file, not just new cases), 35/35
apiSession tests. Full happy-app suite (130 files/1376 tests) and full
happy-cli suite also re-run clean after each change.

BUGS.md tracks status of the two remaining open items (push
notifications addressed separately in happy-cos; header/black-circle
back button expected to resolve via the device-detection fix above,
pending on-device confirmation).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant