Files
Sauci/apps/mobile/app.json
jjbruntonandClaude Fable 5 d48b44baa8 feat(invite-funnel): add web join page and mobile deep-link hand-off
Adds a shareable https://sauci.app/join/{code} link as an alternative to
manually typing an 8-character invite code:

- apps/web: /join/[code] page that validates the code shape client-side,
  attempts the app.sauci:// scheme link, and falls back to a large code
  display with copy button plus App Store/Play Store links; PostHog events
  for page view, code copy, and store-button clicks.
- apps/web: apple-app-site-association and assetlinks.json under
  /.well-known so iOS Universal Links and Android App Links can verify
  sauci.app for /join/*. The Android release cert SHA-256 fingerprint is a
  placeholder pending the real value.
- apps/mobile: associated domains (iOS) and an intent filter (Android) for
  https://sauci.app/join/*, alongside the existing app.sauci scheme.
- apps/mobile: parse incoming join links, pre-fill and highlight the code on
  the pairing screen, stash the code for signed-out/mid-onboarding users and
  apply it once they reach pairing, and offer a once-per-session clipboard
  hand-off that is never auto-applied.
- apps/mobile: share message now leads with the join link, keeping the raw
  code visible as a fallback.
- Analytics: invite_link_opened, pairing_code_prefilled (mobile);
  join_page_viewed, join_page_code_copied, join_page_store_button_clicked
  (web).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 17:58:43 +01:00

317 lines
11 KiB
JSON

{
"expo": {
"name": "Sauci",
"slug": "sauci",
"version": "1.0.6",
"orientation": "portrait",
"icon": "./assets/icon.png",
"scheme": "app.sauci",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#1a1a2e"
},
"ios": {
"supportsTablet": false,
"bundleIdentifier": "com.sauci.app",
"associatedDomains": [
"applinks:sauci.app"
],
"config": {
"usesNonExemptEncryption": false
},
"entitlements": {
"com.apple.security.application-groups": [
"group.com.sauci.app"
]
},
"buildNumber": "45",
"usesAppleSignIn": true,
"googleServicesFile": "./GoogleService-Info.plist",
"infoPlist": {
"GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED": false,
"UIBackgroundModes": [
"remote-notification"
],
"NSFaceIDUsageDescription": "Sauci uses Face ID to unlock the app and keep your conversations private.",
"NSCameraUsageDescription": "Sauci uses your camera to take photos and videos that you can share privately with your partner.",
"NSPhotoLibraryUsageDescription": "Sauci uses your photo library so you can select photos and videos to share privately with your partner.",
"NSPhotoLibraryAddUsageDescription": "Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.",
"NSMicrophoneUsageDescription": "Sauci uses your microphone to record audio when you capture videos to share privately with your partner."
},
"privacyManifests": {
"NSPrivacyTracking": false,
"NSPrivacyCollectedDataTypes": [
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeEmailAddress",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeName",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeSensitiveInfo",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality",
"NSPrivacyCollectedDataTypePurposeProductPersonalization"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeUserID",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality",
"NSPrivacyCollectedDataTypePurposeAnalytics"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeDeviceID",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypePhotosorVideos",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeAudioData",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeOtherUserContent",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeProductInteraction",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAnalytics"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeCrashData",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAnalytics"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypePerformanceData",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAnalytics"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeOtherDiagnosticData",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAnalytics"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypePurchaseHistory",
"NSPrivacyCollectedDataTypeLinked": true,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
}
],
"NSPrivacyAccessedAPITypes": [
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryFileTimestamp",
"NSPrivacyAccessedAPITypeReasons": [
"C617.1",
"0A2A.1",
"3B52.1"
]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
"NSPrivacyAccessedAPITypeReasons": [
"CA92.1",
"C56D.1"
]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryDiskSpace",
"NSPrivacyAccessedAPITypeReasons": [
"E174.1",
"85F4.1"
]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategorySystemBootTime",
"NSPrivacyAccessedAPITypeReasons": [
"35F9.1"
]
}
]
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon-foreground.png",
"backgroundColor": "#1a1a2e"
},
"package": "com.sauci.app",
"versionCode": 48,
"googleServicesFile": "./google-services.json",
"permissions": [
"RECEIVE_BOOT_COMPLETED",
"VIBRATE",
"android.permission.USE_BIOMETRIC",
"android.permission.USE_FINGERPRINT",
"com.android.vending.BILLING"
],
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "sauci.app",
"pathPrefix": "/join"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
}
]
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/favicon.png"
},
"plugins": [
"./plugins/withFixHoisting.js",
"@react-native-firebase/app",
[
"expo-build-properties",
{
"android": {
"buildToolsVersion": "36.0.0",
"compileSdkVersion": 36,
"targetSdkVersion": 36,
"blockedPermissions": [
"android.permission.READ_MEDIA_IMAGES",
"android.permission.READ_MEDIA_VIDEO"
],
"extraProguardRules": "-keep class com.facebook.hermes.unicode.** { *; }",
"useLegacyPackaging": false,
"manifestPlaceholders": {
"firebaseAnalyticsWithoutAdId": "true"
}
},
"ios": {
"useFrameworks": "static"
}
}
],
"expo-router",
"expo-apple-authentication",
[
"expo-notifications",
{
"icon": "./assets/notification-icon.png",
"color": "#e94560"
}
],
"expo-secure-store",
[
"expo-local-authentication",
{
"faceIDPermission": "Sauci uses Face ID to unlock the app and keep your conversations private."
}
],
"@react-native-firebase/crashlytics",
"expo-font",
"expo-asset",
[
"@react-native-google-signin/google-signin",
{
"iosUrlScheme": "com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237"
}
],
"./plugins/withKotlinVersion.js",
[
"@bacons/apple-targets",
{
"appleTeamId": "BXVPBATDZY"
}
],
[
"react-native-android-widget",
{
"widgets": [
{
"name": "LiveDraw",
"label": "Live Draw",
"minWidth": "250dp",
"minHeight": "110dp",
"targetCellWidth": 4,
"targetCellHeight": 2,
"description": "See your shared drawing with your partner.",
"previewImage": "./assets/widget-preview/live-draw.png",
"updatePeriodMillis": 1800000,
"nativeProvider": true
}
]
}
],
"./plugins/withWidgetExported.js",
"./plugins/withNativeWidget.js"
],
"experiments": {
"typedRoutes": true
},
"extra": {
"router": {
"origin": false
},
"eas": {
"projectId": "807ca0af-e9bd-40e8-a658-f3382072c724"
}
},
"owner": "jjbrunton-apps"
}
}