0a2c7376b9
Adds the reverse direction: share FROM Photos/Files/Safari INTO a Biz Connect conversation. An app can only appear in the iOS share sheet as an app-extension target, so this is real native work, not a web change. Pieces: - mobile/ios-share/ShareViewController.swift: a UI-less Share Extension. It stages the shared items into the App Group container and opens bizconnect://share. It deliberately does NOT reimplement the chat picker — that lives in the app, which already has the chat list, search and upload progress. Appends to the manifest (never overwrites), so sharing twice before opening the app loses nothing. - mobile/scripts/add-share-extension.rb: injects the extension target into the Capacitor-generated Xcode project on every CI build (Codemagic checks out fresh), using the xcodeproj gem that ships with CocoaPods. Embeds it, sets the bundle id <app>.share, and MERGES the App Group into the app's entitlements rather than clobbering them (push's aps-environment must survive). Idempotent. - mobile/plugins/share-inbox: getPending()/clear() to read that manifest — the App Group container isn't one of Filesystem's known directories, so it needs a bridge. - home.html: on bizconnect://share (and every resume, and cold-launch), read the inbox and show a "Send to…" picker over the chat list; chosen files run the SAME upload + /api/messages send as an in-app attachment. Reuses convertFileSrc to read the staged bytes with no base64 marshalling. - ios-patch.sh registers the bizconnect URL scheme; codemagic.yaml fetches a profile for the .share bundle id too. One-time manual gate (CI cannot toggle App capabilities): the App Group group.com.bizgaze.connect must be created and enabled on both App IDs in the Apple portal — documented in mobile/IOS_SETUP.md. Without it the two processes can't see each other's files and sharing silently no-ops; everything else still works. Validated cross-file: pod-name/jsName/method wiring for all three plugins, App Group id identical in all 4 files, URL scheme consistent across extension/plist/web, entitlement-merge preserves push. Needs a new iOS build (new targets + plugins). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
4.8 KiB
Bash
77 lines
4.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Patch the freshly-generated Capacitor iOS project (mobile/ios/App) for App Store submission.
|
|
# Runs on the Codemagic macOS instance after `npx cap add ios`. Idempotent — safe to re-run.
|
|
set -euo pipefail
|
|
|
|
PLIST="mobile/ios/App/App/Info.plist"
|
|
PB=/usr/libexec/PlistBuddy
|
|
|
|
set_str() { # set_str <key> <value> — add the key if missing, else overwrite
|
|
"$PB" -c "Add :$1 string $2" "$PLIST" 2>/dev/null || "$PB" -c "Set :$1 $2" "$PLIST"
|
|
}
|
|
|
|
echo "Patching $PLIST"
|
|
|
|
# ── Privacy usage strings (Apple REJECTS the build if a used capability has no purpose string) ──
|
|
set_str NSCameraUsageDescription "Biz Connect uses the camera for video calls and to share photos and your screen."
|
|
set_str NSMicrophoneUsageDescription "Biz Connect uses the microphone for voice and video calls."
|
|
set_str NSPhotoLibraryUsageDescription "Biz Connect needs photo access so you can send images in chat."
|
|
set_str NSPhotoLibraryAddUsageDescription "Biz Connect saves images and recordings you download to your photos."
|
|
|
|
# Human-readable display name on the home screen.
|
|
set_str CFBundleDisplayName "Biz Connect"
|
|
|
|
# CFBundleVersion (build number) MUST be unique AND higher than every previous App Store Connect upload,
|
|
# or publishing fails with "The bundle version must be higher than the previously uploaded version".
|
|
# Capacitor ships "1" by default, so every build collided. Use Codemagic's monotonically-increasing
|
|
# BUILD_NUMBER (this is build index 6+, already > the "1" that's on TestFlight). Fall back to an epoch
|
|
# timestamp if it's somehow unset, which is also strictly increasing.
|
|
BUILD_NO="${BUILD_NUMBER:-$(date +%s)}"
|
|
echo "Setting CFBundleVersion = $BUILD_NO"
|
|
set_str CFBundleVersion "$BUILD_NO"
|
|
|
|
# ── Make the app's Documents folder visible in the Files app ───────────────────────────────────────
|
|
# Downloads are saved to Documents/{Images,Videos,Files}. Without these two keys that folder is private
|
|
# and the user has no way to reach what they saved. With them, Files shows
|
|
# Files → Browse → On My iPhone → Biz Connect → Images / Videos / Files
|
|
# UIFileSharingEnabled exposes the folder; LSSupportsOpeningDocumentsInPlace lets other apps open those
|
|
# files in place rather than silently working on a copy.
|
|
set_bool() { "$PB" -c "Add :$1 bool $2" "$PLIST" 2>/dev/null || "$PB" -c "Set :$1 $2" "$PLIST"; }
|
|
set_bool UIFileSharingEnabled true
|
|
set_bool LSSupportsOpeningDocumentsInPlace true
|
|
|
|
# ── Custom URL scheme so the Share Extension can bounce the user back into the app ──────────────────
|
|
# The extension stages the shared files into the App Group, then opens bizconnect://share; the app reads
|
|
# the staged files and shows "Send to…". Registering the scheme is what makes that openURL succeed.
|
|
if ! "$PB" -c "Print :CFBundleURLTypes" "$PLIST" >/dev/null 2>&1; then
|
|
"$PB" -c "Add :CFBundleURLTypes array" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0 dict" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0:CFBundleURLName string com.bizgaze.connect" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes array" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string bizconnect" "$PLIST"
|
|
fi
|
|
|
|
# We use only standard encryption (HTTPS/TLS), which is exempt — declaring this up front stops App Store
|
|
# Connect from asking the "export compliance" question on every single build/TestFlight upload.
|
|
"$PB" -c "Add :ITSAppUsesNonExemptEncryption bool false" "$PLIST" 2>/dev/null \
|
|
|| "$PB" -c "Set :ITSAppUsesNonExemptEncryption false" "$PLIST"
|
|
|
|
# Allow the webview to load our HTTPS origin (we do NOT enable arbitrary cleartext).
|
|
"$PB" -c "Delete :NSAppTransportSecurity" "$PLIST" 2>/dev/null || true
|
|
|
|
# ── Default in-call audio to the LOUDSPEAKER (AVAudioSession) ──────────────────────────────────────
|
|
# Without this, iOS routes WebRTC call audio to the quiet EARPIECE. A Node helper (Node 20 is already set
|
|
# up for this build) injects an AVAudioSession category into the generated AppDelegate so calls default to
|
|
# the speaker (headphones/Bluetooth still win when connected). The helper is tolerant and exits 0 even if
|
|
# the template differs, so it NEVER fails the build. First-pass fix — if WebRTC re-grabs the session
|
|
# mid-call on device, we follow up with a plugin that re-asserts .overrideOutputAudioPort(.speaker).
|
|
AD="mobile/ios/App/App/AppDelegate.swift"
|
|
if [ -f "$AD" ]; then
|
|
echo "Patching AppDelegate audio session"
|
|
node "$(dirname "$0")/inject-audio.js" "$AD" || echo " (AVAudioSession patch skipped — non-fatal)"
|
|
fi
|
|
|
|
echo "Info.plist patched:"
|
|
"$PB" -c "Print :NSCameraUsageDescription" "$PLIST"
|
|
"$PB" -c "Print :NSMicrophoneUsageDescription" "$PLIST"
|