Merge remote-tracking branch 'origin/staging' into staging

# Conflicts:
#	.codex/skills/release-mobile/SKILL.md
#	apps/mobile/package.json
#	docs/releasing.md
This commit is contained in:
jjbrunton committed 2026-08-31 11:34:33 +01:00
commit 4973f2c631
103 files changed
+29518 -35343

No files matched your search

@@ -3,8 +3,8 @@ id: 2026-08-30-ios-extension-version-verification
date: 2026-08-30
status: promoted
scope: iOS release artifact verification
evidence: build 45 Xcode archive warning plus direct PlistBuddy inspection of the exported IPA
destination: .codex/skills/release-mobile/SKILL.md
evidence: v1.0.6 exported IPA inspection and reproduced v1.0.7 Xcode build-setting mismatch
destination: apps/mobile/scripts/release-preflight.mjs and .codex/skills/release-mobile/SKILL.md
review_after: 2027-02-28
---
@@ -21,19 +21,23 @@ and every packaged extension `Info.plist` with `PlistBuddy`.
## Cause
The archive warning described an intermediate state and was not authoritative
for the exported package. Direct IPA inspection showed both the app and
`LiveDrawWidget.appex` at version 1.0.6 build 45.
The v1.0.6 archive warning described an intermediate state and was not
authoritative for that exported package. The later v1.0.7 reproduction found a
separate real hazard: `CURRENT_PROJECT_VERSION` in both LiveDrawWidget
configurations can override the widget plist and package an older build number.
## Remediation
For every iOS release, inspect version and build values in the exported app and
all packaged extensions. Reject or rebuild only when the final IPA values differ;
do not accept or reject a release from archive warnings alone.
Before every iOS release, require public-version parity across app config, both
plists, and Xcode marketing settings. Require the widget Debug and Release
`CURRENT_PROJECT_VERSION` values to equal both plists. Then inspect the exported
app and all packaged extensions. Reject or rebuild only when the final IPA values
differ; do not accept or reject a release from archive warnings alone.
## Evidence
`PlistBuddy` read `CFBundleShortVersionString` 1.0.6 and `CFBundleVersion` 45 from
both packaged plists. `codesign --verify --deep --strict` passed for the app, and
the verified IPA SHA-256 was
`9d4779bddac4647911a3a38e4ee37cf794c2b8691e0746a041b629c62b308e7f`.
both v1.0.6 packaged plists. The v1.0.7 build later proved a stale widget Xcode
setting can survive plist-only review. `release-preflight.mjs` and its node test
now reject that mismatch before EAS allocation; exported IPA inspection remains
the final authority.
@@ -0,0 +1,49 @@
---
id: 2026-08-31-mobile-release-preflight-and-attempts
date: 2026-08-31
status: promoted
scope: mobile production release preparation and local EAS builds
evidence: v1.0.7 Android and iOS local EAS release investigation and verified artifacts
destination: apps/mobile/scripts/release-preflight.mjs, apps/mobile/scripts/release-preflight.test.mjs, docs/releasing.md, and release skills
review_after: 2027-02-28
---
## Symptom
The v1.0.7 release spent repeated attempts on archive, plugin startup, package
installation, disk-space, signing, and iOS extension-version failures before
valid Android and iOS artifacts were accepted.
## Reproduction
Run a local production EAS build with an ignored public OTA certificate, a
package-directory plugin path, insufficient disk space, or divergent widget
`CURRENT_PROJECT_VERSION`. Each condition fails or produces an invalid input
before a valid final artifact can be accepted.
## Cause
Pre-compilation setup failures were treated as rebuild failures. Source parity,
archive inclusion, local EAS plugin shape, disk capacity, and effective iOS widget
build settings were not checked together before EAS allocation.
## Remediation
Run the deterministic mobile release preflight before every production build.
Classify attempts before retrying: archive, plugin, npm, and ENOSPC failures do
not consume a native build; signing, profile, and capability failures require
provider read-back before one replacement build. Retain work directories and logs
at stable ignored paths. Accept only independently inspected artifacts and keep
rejected artifacts out of the upload path.
The Android native input failure was a Gradle task ordering issue: Prefab inputs
must be available before dependent native tasks. Preserve the explicit ordering
fix rather than removing Firebase or changing dependency versions without proof.
## Evidence
The preflight node tests reject an ignored OTA certificate and an effective widget
build-setting mismatch. The checked-in source invariants pass the preflight. The
maintained release guide and skills record local EAS path requirements, provider
read-back, phase-specific status language, artifact inspection, store read-back,
and scoped upload authorization boundaries.
+19 -4
View File
@@ -43,6 +43,8 @@ Require all of the following:
- consistent public and OTA runtime versions across `app.json`, explicit
`app.config.js` runtime, Android, and every iOS target
(`scripts/set_version.mjs --check`);
- `npm run release:preflight -w @sauci/mobile`, which also checks the iOS widget
effective build settings and OTA certificate archive inclusion;
- Node 20, JDK 17 for Android, and Xcode/CocoaPods for iOS;
- the configured production API/Auth/RevenueCat environment, without printing
secret values.
@@ -84,6 +86,15 @@ Build from the clean prepared source using the package scripts in
versioned artifact directory. When building both platforms, build Android first,
then iOS.
For local EAS, retain the working directory and logs under a stable ignored path,
set `EAS_LOCAL_BUILD_ARTIFACTS_DIR`, and preflight available disk space before
starting. Set `EAS_LOCAL_BUILD_PLUGIN_PATH` only to the matching plugin
executable `bin/run`, never a package directory or a hardcoded cache location.
Classify plugin, archive, npm, and ENOSPC failures as pre-compilation failures;
repair and preflight them without spending another build. Make at most one
meaningful build for each newly proven input state. After a signing, profile, or
capability failure, read back and prove the provider correction before rebuilding.
After each build:
1. Inspect the worktree before doing anything else. Accept only the documented
@@ -92,15 +103,19 @@ After each build:
dependency, or native-project regeneration as a failed build.
2. Independently inspect the binary for package/bundle identifier, public
version, platform build number, production endpoint configuration where
inspectable, and signing identity/fingerprint. Require the embedded public
version to equal the requested version.
inspectable, signing identity/fingerprint, OTA runtime/channel/certificate,
and SHA-256. Require the embedded public version to equal the requested
version. For Android, also inspect all required ABIs, Worklets/Reanimated/Skia
libraries, and the release permissions. For iOS, inspect every packaged
extension after export.
3. Record the verified platform build number in the new release-note entry.
4. Commit only the expected metadata and release-note update as
`chore(<platform>): record v<version> build <number>`. The worktree must be
clean before starting the next platform build.
Do not accept a successful EAS exit code as proof of a valid artifact. If either
requested platform fails, do not tag a partial release.
Do not accept a successful EAS exit code as proof of a valid artifact. Quarantine
or remove rejected artifacts so only accepted paths can be offered for upload. If
either requested platform fails, do not tag a partial release.
## Tag and prove the result
+48
View File
@@ -52,6 +52,25 @@ verified EAS version metadata. Sauci tracks its native iOS and Android projects,
so routine release builds must package those checked-in sources. Do not run
`expo prebuild` first; native regeneration is a separate reviewed change.
Run `npm run release:preflight -w @sauci/mobile` before allocating an EAS build.
It rejects inconsistent public/runtime versions, an iOS widget build-setting
override, and a public OTA certificate that would be omitted from the archive.
For a local EAS build, also run `npm run release:preflight:local-eas -w
@sauci/mobile` after setting `EAS_LOCAL_BUILD_PLUGIN_PATH` to the matching
plugin's executable `bin/run`. Do not point it at the package directory or
hardcode a machine-local cache path.
Classify a failure before rebuilding. Archive, plugin-spawn, package-install, and
disk-space failures happen before native compilation and must be repaired and
preflighted without consuming another build. After a signing, profile, or
capability failure, read back the provider state and prove only the affected
input changed before rebuilding. Retain the EAS work directory, command log, and
stable artifact path for every meaningful attempt. Never retry an ambiguous
upload.
Report provider progress by phase: packaging, native compilation, submission
queue, or store processing. Do not describe every failure as a rebuild.
Use the package scripts in `apps/mobile/package.json` to build artifacts and the
Fastlane lanes in `apps/mobile/fastlane/Fastfile` to upload, submit, and roll
out.
@@ -83,6 +102,11 @@ submission, and rollout without requesting further confirmation.
when used, pass explicit `--platform`, `--profile production`, `--path`, and
`--wait` — never `--latest`.
App Store Connect API group association does not establish the automatic-
distribution switch. Verify that switch in an authenticated App Store Connect UI
session before upload; if that UI verification is unavailable, stop rather
than inferring the distribution effect from API data.
If an upload or submission command returns an ambiguous result, inspect EAS and
the store before doing anything else; do not retry blind. After submission, read
back the store state (Play Console track status / App Store Connect version
@@ -97,3 +121,27 @@ warnings as diagnostic evidence, not the final artifact state: decide whether to
reject or rebuild only after reading the exported IPA itself. Report the
artifact, version/build number, upload and submission results, rollout state,
and store read-back state.
For Android, verify the accepted AAB has all four required ABIs, the Worklets,
Reanimated, and Skia native libraries, no broad `READ_MEDIA_IMAGES` or
`READ_MEDIA_VIDEO` permissions, and the production OTA runtime, channel, and
public certificate. For iOS, verify the exported IPA, not archive warnings, and
check both the containing app and every extension.
For the Android native dependency ordering already tracked in Gradle, preserve
the explicit task dependency that makes Prefab inputs available before dependent
native tasks. Fix task validation with ordering, not Firebase removal or
dependency-version roulette.
After Android upload, EAS Submit `FINISHED`, the exact internal/DRAFT/no-rollout
submit configuration, and the retained artifact digest are the available
authoritative read-back when a direct Play track read would require creating an
edit. Never create an edit merely to claim read-only verification.
For a checked-in iOS entitlement, inspect the real Apple App ID capability state
before a signing rebuild. The capability sync bug tracked in
https://github.com/expo/eas-cli/issues/3986 can report success while Apple
rejects a malformed update. Read back Associated Domains, regenerate only the
affected profile, and decode that profile to prove the entitlement before
rebuilding. Noninteractive managed App Store Connect key auth may require
`EXPO_APPLE_TEAM_ID`; preserve certificates and unrelated target profiles.
+15
View File
@@ -2,6 +2,21 @@
module.exports = {
extends: 'expo',
ignorePatterns: ['/dist/*'],
rules: {
// eslint-config-expo (SDK 56) pulls eslint-plugin-react-hooks v7, whose
// recommended config adds React Compiler era rules as errors. Existing
// patterns this codebase relies on deliberately (the "latest value" ref
// pattern, effect-driven setState, dynamically created inline
// components, and manual memoization) trip these new rules throughout
// the app. Adopting the stricter rules is a deliberate follow-up
// refactor, not an SDK upgrade side effect, so they are downgraded to
// warnings here.
'react-hooks/refs': 'warn',
'react-hooks/set-state-in-effect': 'warn',
'react-hooks/immutability': 'warn',
'react-hooks/preserve-manual-memoization': 'warn',
'react-hooks/static-components': 'warn',
},
overrides: [
{
files: ['plugins/**/*.js'],
+3
View File
@@ -12,5 +12,8 @@ local.properties
*.hprof
.cxx/
# generated inline modules
app/src/main/java/inline/
# Bundle artifacts
*.jsbundle
+4 -53
View File
@@ -11,7 +11,7 @@ def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
react {
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
hermesCommand = new File(["node", "--print", "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/hermesc/%OS-BIN%/hermesc"
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean()
@@ -64,7 +64,7 @@ react {
}
/**
* Set this to true in release builds to optimize the app using R8.
* Set this to true in release builds to optimize the app using [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization).
*/
def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBuilds') ?: false).toBoolean()
@@ -94,6 +94,7 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 49
versionName "1.0.7"
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
}
signingConfigs {
@@ -181,54 +182,4 @@ dependencies {
}
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
// Reanimated 4.1.7 imports Worklets from its pre-AGP-8 CMake output path,
// while AGP 8.14 writes release outputs below intermediates/cxx/RelWithDebInfo.
// Keep the checked-in Android project compatible with the supported SDK 54
// dependency set by materializing that one legacy input after Worklets builds.
def workletsReleaseAbis = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
def syncWorkletsReleaseCmakeInputs = tasks.register("syncWorkletsReleaseCmakeInputs") {
dependsOn(":react-native-worklets:externalNativeBuildRelease")
doLast {
def workletsIntermediates = file("${rootDir}/../../../node_modules/react-native-worklets/android/build/intermediates")
workletsReleaseAbis.each { abi ->
def libraries = fileTree(workletsIntermediates) {
include "cxx/RelWithDebInfo/**/obj/${abi}/libworklets.so"
}.files
if (libraries.size() != 1) {
throw new GradleException("Expected exactly one Worklets release library for ${abi}, found ${libraries.size()}")
}
def source = libraries.iterator().next()
def destination = file("${workletsIntermediates}/cmake/release/obj/${abi}/libworklets.so")
destination.parentFile.mkdirs()
java.nio.file.Files.deleteIfExists(destination.toPath())
source.withInputStream { input ->
destination.withOutputStream { output -> output << input }
}
if (destination.length() != source.length()) {
throw new GradleException("Failed to copy Worklets release library for ${abi}")
}
}
}
}
gradle.projectsEvaluated {
def reanimatedProject = findProject(":react-native-reanimated")
reanimatedProject?.tasks?.configureEach { task ->
if (task.name == "externalNativeBuildRelease" || task.name == "prefabReleasePackage") {
task.dependsOn(syncWorkletsReleaseCmakeInputs)
}
}
tasks.configureEach { task ->
if (task.name.startsWith("configureCMakeRelWithDebInfo")) {
task.dependsOn(
":react-native-worklets:prefabReleasePackage",
":react-native-reanimated:prefabReleasePackage",
":shopify_react-native-skia:prefabReleasePackage",
)
}
}
}
apply plugin: 'com.google.firebase.crashlytics'
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic" />
</manifest>
@@ -1,10 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
@@ -12,7 +12,7 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<queries>
<package android:name="com.instagram.android"/>
@@ -22,7 +22,7 @@
<data android:scheme="https"/>
</intent>
</queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:requestLegacyExternalStorage="true" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules" android:enableOnBackInvokedCallback="false">
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false" android:requestLegacyExternalStorage="true" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules">
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon"/>
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
@@ -30,13 +30,14 @@
<meta-data android:name="expo.modules.updates.CODE_SIGNING_CERTIFICATE" android:value="-----BEGIN CERTIFICATE-----&#xA;MIICzTCCAbWgAwIBAgIJAPrPjxnUOGisMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV&#xA;BAMTBVNhdWNpMB4XDTI2MDgzMDE4MTAzMFoXDTM2MDgzMDE4MTAzMFowEDEOMAwG&#xA;A1UEAxMFU2F1Y2kwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWiUac&#xA;8i9bIbNZjv8iunVr6kqlgb9vea98JHz/A83biyszg0+GpmLYsr4zV2F7A6RitiIx&#xA;SOML+wVedyD0F2quWccBGXinvMAds0TxgIOjQfP+8+VJ/duDjveHvn+OFW4s7XxW&#xA;SY7TK1ufDcPWo/f5XzfqtBveug49zv3/F3MwfmMUbEY/zvH7nayPGJNuSyf3wISn&#xA;JIt0BsHNhWykRXCzjaF2fy3nAebOyezbtbapCjUlxPENVtBCzBQ7C9kaQXCBw5mV&#xA;hxhJlOxHWXpHJgDLHpev8A/CWQRh67tsevjKi6R6SDlf0R0xSbDJTElx8goWOOQW&#xA;VO9aDqI+oH0KpWfnAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E&#xA;DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAyUc20bIRCxoBTJg84mPl&#xA;2nFDWSaAfzgo0K0b6x2T1suuq4LAOUgzBqJISrQAALd0besK1qUZSAMhjQu1wPZT&#xA;MHuWRMxQHfOkyHMYUfOoPMS4bE+4K0WjzfoVt3y1A+oxhcIAQUUGyNgNmebfEN07&#xA;JlX0B8UI8aIMjyfpsIDgZx1piHKwittGy/V4MPOLbmT2BBwS9BELcmWE4CwWbfeo&#xA;JM4lXY0hk3M/GTstRYaUKiwBWEU+z1PdAmWAgrtxpVnXN+oaToWh0WbVkbF1K6r5&#xA;3oBGFTL7kT+Wogg9GK04XoAREw0jZJ/k77oJx68lGVBNiB+Ue2KqX0H/0cf2CslY&#xA;Ug==&#xA;-----END CERTIFICATE-----&#xA;"/>
<meta-data android:name="expo.modules.updates.CODE_SIGNING_METADATA" android:value="{&quot;keyid&quot;:&quot;main&quot;,&quot;alg&quot;:&quot;rsa-v1_5-sha256&quot;}"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.ENABLE_BSDIFF_PATCH_SUPPORT" android:value="true"/>
<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="5000"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://ota.apps.jbrunton.co.uk/manifest"/>
<meta-data android:name="expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY" android:value="{&quot;expo-channel-name&quot;:&quot;production&quot;,&quot;expo-app-id&quot;:&quot;9a66f3c2-fda7-416f-87e2-6db833b32e9d&quot;,&quot;xprem-branch&quot;:&quot;&quot;}"/>
<service android:name="com.reactnativeandroidwidget.RNWidgetCollectionService" android:permission="android.permission.BIND_REMOTEVIEWS"/>
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize|assetsPaths" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -46,7 +47,6 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="app.sauci"/>
<data android:scheme="com.sauci.app"/>
<data android:scheme="exp+sauci"/>
</intent-filter>
<intent-filter android:autoVerify="true" data-generated="true">
@@ -56,7 +56,7 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<receiver android:name=".widget.LiveDraw" android:exported="true" android:label="Live Draw">
<receiver android:name=".widget.LiveDraw" android:exported="false" android:label="Live Draw">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.sauci.app.WIDGET_CLICK"/>
@@ -1,4 +1,5 @@
package com.sauci.app
import expo.modules.splashscreen.SplashScreenManager
import android.os.Build
import android.os.Bundle
@@ -15,7 +16,10 @@ class MainActivity : ReactActivity() {
// Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme);
// setTheme(R.style.AppTheme);
// @generated begin expo-splashscreen - expo prebuild (DO NOT MODIFY) sync-f3ff59a738c56c9a6119210cb55f0b613eb8b6af
SplashScreenManager.registerOnActivity(this)
// @generated end expo-splashscreen
super.onCreate(null)
}
@@ -5,40 +5,27 @@ import android.content.res.Configuration
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.ReactPackage
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.common.ReleaseLevel
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
import expo.modules.ExpoReactHostFactory
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
this,
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> {
val packages = PackageList(this).packages
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages
}
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
)
override val reactHost: ReactHost
get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
override val reactHost: ReactHost by lazy {
ExpoReactHostFactory.getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}
)
}
override fun onCreate() {
super.onCreate()
@@ -1,84 +1,6 @@
package com.sauci.app.widget;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.Base64;
import android.util.TypedValue;
import android.widget.RemoteViews;
import com.reactnativeandroidwidget.RNWidgetProvider;
import com.sauci.app.R;
public class LiveDraw extends AppWidgetProvider {
private static final String PREFS_NAME_SUFFIX = ".widgetdata";
private static final String KEY_IMAGE = "LiveDrawImage";
private static final int BG_COLOR = 0xFF1A1A2E;
private static final int CORNER_RADIUS_DP = 16;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int widgetId : appWidgetIds) {
updateWidget(context, appWidgetManager, widgetId);
}
}
private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_live_draw);
String base64 = getStoredImage(context);
if (base64 != null && !base64.isEmpty()) {
try {
byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
Bitmap decoded = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (decoded != null) {
Bitmap rounded = addRoundedBackground(context, decoded);
views.setImageViewBitmap(R.id.widget_image, rounded);
decoded.recycle();
}
} catch (Exception e) {
setPlaceholder(views);
}
} else {
setPlaceholder(views);
}
appWidgetManager.updateAppWidget(widgetId, views);
}
private String getStoredImage(Context context) {
SharedPreferences prefs = context.getSharedPreferences(
context.getPackageName() + PREFS_NAME_SUFFIX, Context.MODE_PRIVATE);
return prefs.getString(KEY_IMAGE, null);
}
private void setPlaceholder(RemoteViews views) {
views.setImageViewResource(R.id.widget_image, R.drawable.widget_placeholder);
}
private Bitmap addRoundedBackground(Context context, Bitmap drawing) {
float radius = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS_DP,
context.getResources().getDisplayMetrics());
int w = drawing.getWidth();
int h = drawing.getHeight();
Bitmap output = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgPaint.setColor(BG_COLOR);
canvas.drawRoundRect(new RectF(0, 0, w, h), radius, radius, bgPaint);
canvas.drawBitmap(drawing, 0, 0, null);
return output;
}
public class LiveDraw extends RNWidgetProvider {
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 24 KiB

@@ -2,6 +2,5 @@
<color name="splashscreen_background">#1a1a2e</color>
<color name="iconBackground">#1a1a2e</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#1a1a2e</color>
<color name="notification_icon_color">#e94560</color>
</resources>
@@ -1,7 +1,7 @@
<resources>
<string name="app_name">Sauci</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
<string name="widget_livedraw_description" translatable="false">See your shared drawing with your partner.</string>
<string name="expo_system_ui_user_interface_style" translatable="false">automatic</string>
<string name="expo_runtime_version">1.0.7</string>
<string name="widget_livedraw_description" translatable="false">See your shared drawing with your partner.</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
</resources>
@@ -1,18 +1,14 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:enforceNavigationBarContrast" tools:targetApi="29">true</item>
<item name="android:textColor">@android:color/black</item>
<item name="android:editTextStyle">@style/ResetEditText</item>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="android:statusBarColor">#1a1a2e</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="ResetEditText" parent="@android:style/Widget.EditText">
<item name="android:padding">0dp</item>
<item name="android:textColorHint">#c8c8c8</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Theme.App.SplashScreen" parent="AppTheme">
<item name="android:windowBackground">@drawable/ic_launcher_background</item>
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
<item name="android:windowSplashScreenBehavior">icon_preferred</item>
</style>
</resources>
@@ -4,10 +4,18 @@
android:minHeight="110dp"
android:targetCellWidth="4"
android:targetCellHeight="2"
android:resizeMode="none"
android:description="@string/widget_livedraw_description"
android:initialLayout="@layout/widget_live_draw"
android:initialLayout="@layout/rn_widget"
android:previewImage="@drawable/livedraw_preview"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen">
</appwidget-provider>
+18 -18
View File
@@ -1,26 +1,26 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6'
classpath 'com.google.gms:google-services:4.4.1'
classpath('com.android.tools.build:gradle')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
}
classpath 'com.google.gms:google-services:4.4.4'
classpath('com.android.tools.build:gradle')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
}
apply plugin: "expo-root-project"
apply plugin: "com.facebook.react.rootproject"
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
}
+9 -13
View File
@@ -15,7 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
@@ -41,6 +41,11 @@ newArchEnabled=true
# If set to false, you will be using JSC instead.
hermesEnabled=true
# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=true
# Enable GIF support in React Native images (~200 B increase)
expo.gif.enabled=true
# Enable webp support in React Native images (~85 KB increase)
@@ -55,16 +60,7 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
# Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false
android.targetSdkVersion=36
android.extraMavenRepos=[]
# Specifies whether the app is configured to use edge-to-edge via the app config or plugin
# WARNING: This property has been deprecated and will be removed in Expo SDK 55. Use `edgeToEdgeEnabled` or `react.edgeToEdgeEnabled` to determine whether the project is using edge-to-edge.
expo.edgeToEdgeEnabled=true
# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=true
expo.inlineModules.watchedDirectories=[]
android.compileSdkVersion=36
android.buildToolsVersion=36.0.0
android.targetSdkVersion=36
android.buildToolsVersion=36.0.0
Binary file not shown.
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+4 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -173,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -206,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
+6 -2
View File
@@ -1,3 +1,8 @@
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
@REM
@REM This source code is licensed under the MIT license found in the
@REM LICENSE file in the root directory of this source tree.
@rem
@rem Copyright 2015 the original author or authors.
@rem
@@ -70,11 +75,10 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
+2 -2
View File
@@ -6,7 +6,7 @@ pluginManagement {
}.standardOutput.asText.get().trim()
).getParentFile().absolutePath
includeBuild(reactNativeGradlePlugin)
def expoPluginsPath = new File(
providers.exec {
workingDir(rootDir)
@@ -29,10 +29,10 @@ extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
}
}
expoAutolinking.useExpoModules()
rootProject.name = 'Sauci'
expoAutolinking.useExpoModules()
expoAutolinking.useExpoVersionCatalog()
include ':app'
+12 -7
View File
@@ -7,12 +7,6 @@
"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",
@@ -223,6 +217,14 @@
},
"plugins": [
"./plugins/withFixHoisting.js",
[
"expo-splash-screen",
{
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#1a1a2e"
}
],
"@react-native-firebase/app",
[
"expo-build-properties",
@@ -320,7 +322,10 @@
"origin": false
},
"eas": {
"projectId": "807ca0af-e9bd-40e8-a658-f3382072c724"
"projectId": "807ca0af-e9bd-40e8-a658-f3382072c724",
"observe": {
"endpointUrl": "https://ota.apps.jbrunton.co.uk/observe/9a66f3c2-fda7-416f-87e2-6db833b32e9d"
}
}
},
"owner": "jjbrunton-apps"
+1 -1
View File
@@ -389,7 +389,7 @@ const styles = StyleSheet.create({
zIndex: 100,
},
navBarBackground: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
borderBottomWidth: 1,
borderBottomColor: colors.border,
backgroundColor: colors.background,
+20 -14
View File
@@ -5,6 +5,11 @@ import "../src/polyfills/web";
import { initCrashlytics, setUserContext, clearUserContext } from "../src/lib/crashlytics";
initCrashlytics();
// Install the Observe global JS error handler after Crashlytics so it wraps
// (and always calls) whatever handler is already registered.
import { installObserveGlobalErrorHandler, logAppStarted } from "../src/lib/observe";
installObserveGlobalErrorHandler();
// Analytics imports (initialization deferred until app mounts)
import { initAnalytics, setUserId, clearUserId, logScreenView, Events } from "../src/lib/analytics";
@@ -73,6 +78,7 @@ export default function RootLayout() {
analyticsInitialized.current = true;
initAnalytics();
Events.appOpened("cold");
logAppStarted();
}
}, []);
@@ -115,15 +121,15 @@ export default function RootLayout() {
// Fetch user on mount
fetchUser();
// Handle initial URL if app was opened via deep link
const handleDeepLink = async (url: string | null) => {
if (!url) return;
// Handle initial URL if app was opened via deep link
const handleDeepLink = async (url: string | null) => {
if (!url) return;
// Invite links (https://sauci.app/join/{code} or app.sauci://join?code={code})
// are handled separately from the Supabase auth deep link flow below.
const inviteLink = parseInviteLinkCode(url);
if (inviteLink) {
Events.inviteLinkOpened(inviteLink.source);
const inviteLink = parseInviteLinkCode(url);
if (inviteLink) {
Events.inviteLinkOpened(inviteLink.source);
await stashPendingInviteCode(inviteLink.code);
const { isAuthenticated, user } = useAuthStore.getState();
@@ -200,15 +206,15 @@ export default function RootLayout() {
}
};
// Get initial URL
Linking.getInitialURL().then((url) => {
handleDeepLink(url);
});
// Get initial URL
Linking.getInitialURL().then((url) => {
handleDeepLink(url);
});
// Listen for new deep links while app is open
const linkingSubscription = Linking.addEventListener("url", (event) => {
handleDeepLink(event.url);
});
// Listen for new deep links while app is open
const linkingSubscription = Linking.addEventListener("url", (event) => {
handleDeepLink(event.url);
});
// Set up push notification response listener (for taps while app is running)
const notificationSubscription = setupNotificationResponseListener();
+1 -1
View File
@@ -425,7 +425,7 @@ const styles = StyleSheet.create({
zIndex: 100,
},
navBarBackground: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
borderBottomWidth: 1,
borderBottomColor: colors.border,
overflow: "hidden",
+3
View File
@@ -28,3 +28,6 @@ project.xcworkspace
# CocoaPods
/Pods/
# @bacons/apple-targets generated entitlements cache
/.targets/
+21 -33
View File
@@ -1,15 +1,28 @@
# Set by expo-router. This enables Fabric-only features from react-native-screens
ENV['RNS_GAMMA_ENABLED'] ||= '1'
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require 'json'
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
def ccache_enabled?(podfile_properties)
# Environment variable takes precedence
return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE']
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
install! 'cocoapods',
:deterministic_uuids => false
# Fall back to Podfile properties
podfile_properties['apple.ccacheEnabled'] == 'true'
end
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
ENV['RCT_USE_RN_DEP'] ||= podfile_properties['ios.buildReactNativeFromSource'] == 'true' ? '0' : '1'
ENV['RCT_USE_PREBUILT_RNCORE'] ||= podfile_properties['ios.buildReactNativeFromSource'] == 'true' ? '0' : '1'
ENV['RCT_HERMES_V1_ENABLED'] ||= '0' if podfile_properties['expo.useHermesV1'] == 'false'
# An explicit `ios.usePrecompiledModules: false` must win even when the env var is already set
# (e.g. EXPO_USE_PRECOMPILED_MODULES exported by EAS Build), so force-disable here before the default.
ENV['EXPO_USE_PRECOMPILED_MODULES'] = '0' if podfile_properties['EXPO_USE_PRECOMPILED_MODULES'] == 'false'
ENV['EXPO_USE_PRECOMPILED_MODULES'] ||= '1'
platform :ios, podfile_properties['ios.deploymentTarget'] || '16.4'
prepare_react_native_project!
@@ -23,7 +36,8 @@ target 'Sauci' do
'node',
'--no-warnings',
'--eval',
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
'require(\'expo/bin/autolinking\')',
'expo-modules-autolinking',
'react-native-config',
'--json',
'--platform',
@@ -49,34 +63,8 @@ target 'Sauci' do
installer,
config[:reactNativePath],
:mac_catalyst_enabled => false,
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
:ccache_enabled => ccache_enabled?(podfile_properties),
)
# The fmt version bundled with React Native 0.76 enables consteval for
# Apple Clang, but Apple Clang 21 (Xcode 26.4+) rejects fmt's FMT_STRING
# call sites. Patch the generated pod until React Native supplies a newer
# fmt release. Keeping this here makes clean pod installs reproducible.
fmt_base_header = File.join(__dir__, 'Pods', 'fmt', 'include', 'fmt', 'base.h')
if File.exist?(fmt_base_header)
fmt_base_source = File.read(fmt_base_header)
fmt_base_source.sub!(
/#elif defined\(__apple_build_version__\) && __apple_build_version__ < 14000029L/,
'#elif defined(__apple_build_version__)'
)
File.chmod(0644, fmt_base_header)
File.write(fmt_base_header, fmt_base_source)
end
# This is necessary for Xcode 14, because it signs resource bundles by default
# when building for devices.
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
end
+1193 -1714
View File
File diff suppressed because it is too large. Load diff
+6 -5
View File
@@ -1,9 +1,10 @@
{
"expo.jsEngine": "hermes",
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
"newArchEnabled": "true",
"ios.useFrameworks": "static",
"apple.extraPods": "[]",
"apple.ccacheEnabled": "false",
"apple.privacyManifestAggregationEnabled": "true"
"expo.inlineModules.watchedDirectories": "[]",
"expo.inlineModules.xcodeProjectTargets": "{\"mainTarget\":\"Sauci\",\"targets\":[]}",
"ios.forceStaticLinking": "[]",
"apple.privacyManifestAggregationEnabled": "true",
"EXPO_USE_PRECOMPILED_MODULES": "true",
"ios.useFrameworks": "static"
}
+225 -143
View File
@@ -3,19 +3,18 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
5254BDADE70741A0B2504FB1 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = F744EAD4ABA54509966475D6 /* noop-file.swift */; };
7B71EEB2AF682FA7AFF556D5 /* Pods_Sauci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
3F202C2A859975F48A83ADD7 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */; };
45941E25D336B6152BFCEFB9 /* Pods_Sauci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */; };
804CD03F6B584D08B7803C63 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
CEF2F278ABBC5FBFD470A0F7 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */; };
D60EEEF252214D45B67A0063 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */; };
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
FDFABE1A1A3547B1B22DD2DD /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */; };
XX12B55A1EDABF083C3B51XX /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */; };
XX1A6800511A685AB346D2XX /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXA12889081EB894481655XX /* AppIntents.framework */; };
XX3C562DEF346B45594832XX /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */; };
@@ -24,11 +23,11 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
XXA7C7D5580082A497FD95XX /* PBXContainerItemProxy */ = {
XX77D941E1DB38490B8F4EXX /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = XX540FB4ABC75CE91A6036XX;
remoteGlobalIDString = XXB3405CC208C2ECA4797FXX;
remoteInfo = LiveDrawWidget;
};
/* End PBXContainerItemProxy section */
@@ -51,18 +50,17 @@
13B07F961A680F5B00A75B9A /* Sauci.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; fileEncoding = 4; includeInIndex = 0; path = Sauci.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Sauci/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sauci/Info.plist; sourceTree = "<group>"; };
6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.debug.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.debug.xcconfig"; sourceTree = "<group>"; };
7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.release.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.release.xcconfig"; sourceTree = "<group>"; };
A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Sauci/GoogleService-Info.plist"; sourceTree = "<group>"; };
69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = PrivacyInfo.xcprivacy; path = Sauci/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Sauci/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.release.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.release.xcconfig"; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Sauci/SplashScreen.storyboard; sourceTree = "<group>"; };
B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sauci.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = undefined; name = PrivacyInfo.xcprivacy; path = Sauci/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
DA536CAC88B349A59DFC1F1E /* Sauci-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Sauci-Bridging-Header.h"; path = "Sauci/Sauci-Bridging-Header.h"; sourceTree = "<group>"; };
DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; fileEncoding = 4; includeInIndex = undefined; path = Pods_Sauci.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Sauci/GoogleService-Info.plist"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Sauci/AppDelegate.swift; sourceTree = "<group>"; };
F744EAD4ABA54509966475D6 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Sauci/noop-file.swift"; sourceTree = "<group>"; };
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Sauci/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
F11748442D0722820044C1D9 /* Sauci-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Sauci-Bridging-Header.h"; path = "Sauci/Sauci-Bridging-Header.h"; sourceTree = "<group>"; };
FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.debug.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.debug.xcconfig"; sourceTree = "<group>"; };
XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
XX4DFF38D47332D6BF0183XX /* SwiftUI.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
XXA12889081EB894481655XX /* AppIntents.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = AppIntents.framework; path = System/Library/Frameworks/AppIntents.framework; sourceTree = SDKROOT; };
@@ -71,21 +69,21 @@
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
XX020388E3BB9C764E774FXX /* Exceptions for "widget" folder in "LiveDrawWidget" target */ = {
XX6FB9E6678087E3AAC658XX /* Exceptions for "widget" folder in "LiveDrawWidget" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
"expo-target.config.js",
);
target = XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */;
target = XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
XX683E29AD7AD8279E17B2XX /* widget */ = {
XXBB44BF07EB68666150ABXX /* widget */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
XX020388E3BB9C764E774FXX /* Exceptions for "widget" folder in "LiveDrawWidget" target */,
XX6FB9E6678087E3AAC658XX /* Exceptions for "widget" folder in "LiveDrawWidget" target */,
);
explicitFileTypes = {
};
@@ -101,7 +99,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7B71EEB2AF682FA7AFF556D5 /* Pods_Sauci.framework in Frameworks */,
45941E25D336B6152BFCEFB9 /* Pods_Sauci.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -122,15 +120,32 @@
13B07FAE1A68108700A75B9A /* Sauci */ = {
isa = PBXGroup;
children = (
BB2F792B24A3F905000567C9 /* Supporting */,
F11748412D0307B40044C1D9 /* AppDelegate.swift */,
F11748442D0722820044C1D9 /* Sauci-Bridging-Header.h */,
BB2F792B24A3F905000567C9 /* Supporting */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */,
F744EAD4ABA54509966475D6 /* noop-file.swift */,
DA536CAC88B349A59DFC1F1E /* Sauci-Bridging-Header.h */,
D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */,
69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */,
D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */,
);
name = Sauci;
sourceTree = "<group>";
};
1957149DB83AAF8603C60859 /* Pods */ = {
isa = PBXGroup;
children = (
FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */,
A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
2BFCD64E7EEB594CC2D9E261 /* Sauci */ = {
isa = PBXGroup;
children = (
779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */,
);
name = Sauci;
sourceTree = "<group>";
@@ -141,9 +156,9 @@
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */,
XX4DFF38D47332D6BF0183XX /* SwiftUI.framework */,
DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */,
XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */,
XXA12889081EB894481655XX /* AppIntents.framework */,
B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -163,8 +178,8 @@
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
D65327D7A22EEC0BE12398D9 /* Pods */,
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
1957149DB83AAF8603C60859 /* Pods */,
CC8FEB5C5178B9E5E71D4F61 /* ExpoModulesProviders */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -180,14 +195,6 @@
name = Products;
sourceTree = "<group>";
};
92DBD88DE9BF7D494EA9DA96 /* Sauci */ = {
isa = PBXGroup;
children = (
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
);
name = Sauci;
sourceTree = "<group>";
};
BB2F792B24A3F905000567C9 /* Supporting */ = {
isa = PBXGroup;
children = (
@@ -197,19 +204,10 @@
path = Sauci/Supporting;
sourceTree = "<group>";
};
D65327D7A22EEC0BE12398D9 /* Pods */ = {
CC8FEB5C5178B9E5E71D4F61 /* ExpoModulesProviders */ = {
isa = PBXGroup;
children = (
6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */,
7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
isa = PBXGroup;
children = (
92DBD88DE9BF7D494EA9DA96 /* Sauci */,
2BFCD64E7EEB594CC2D9E261 /* Sauci */,
);
name = ExpoModulesProviders;
sourceTree = "<group>";
@@ -217,7 +215,7 @@
XX3E41C1850246162C0061XX /* expo:targets */ = {
isa = PBXGroup;
children = (
XX683E29AD7AD8279E17B2XX /* widget */,
XXBB44BF07EB68666150ABXX /* widget */,
);
name = "expo:targets";
path = ../targets;
@@ -231,30 +229,31 @@
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sauci" */;
buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
DC3D179C6B62F16DA5F17C2F /* [Expo] Configure project */,
E6F7433229E3C1448FC9F062 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
D58CF933D4804A0CB49C2891 /* [Expo Dev Launcher] Strip Local Network Keys for Release */,
XX72A8CFBF0FBA13A79902XX /* Embed Foundation Extensions */,
A1248897D24903117793599B /* [CP] Embed Pods Frameworks */,
FB4B019D835055733ACDE109 /* [CP-User] [RNFB] Core Configuration */,
563ACE2651AC39438F3B399B /* [CP-User] [RNFB] Crashlytics Configuration */,
503A0FD3DBA862B4BE649BBB /* [CP] Embed Pods Frameworks */,
8490E10253F91A7BEB80F555 /* [CP-User] [RNFB] Core Configuration */,
0EE1B67C9AAD8BD8562E800E /* [CP-User] [RNFB] Crashlytics Configuration */,
);
buildRules = (
);
dependencies = (
XX97289CEC7994AD5F4142XX /* PBXTargetDependency */,
XX5D3A21868E45E6E5BDC3XX /* PBXTargetDependency */,
);
name = Sauci;
productName = Sauci;
productReference = 13B07F961A680F5B00A75B9A /* Sauci.app */;
productType = "com.apple.product-type.application";
};
XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */ = {
XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */ = {
isa = PBXNativeTarget;
buildConfigurationList = XX63FAB1BD532DA0D8AE18XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */;
buildConfigurationList = XX35D62F4FBF2C9086E8C9XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */;
buildPhases = (
XX074B8811C1CF8E8A3D74XX /* Frameworks */,
XXE45443DC38DCFE1DB622XX /* Sources */,
@@ -265,7 +264,7 @@
dependencies = (
);
fileSystemSynchronizedGroups = (
XX683E29AD7AD8279E17B2XX /* widget */,
XXBB44BF07EB68666150ABXX /* widget */,
);
name = LiveDrawWidget;
productName = LiveDrawWidget;
@@ -283,7 +282,7 @@
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1250;
};
XX540FB4ABC75CE91A6036XX = {
XXB3405CC208C2ECA4797FXX = {
CreatedOnToolsVersion = 14.3;
DevelopmentTeam = BXVPBATDZY;
ProvisioningStyle = Automatic;
@@ -304,7 +303,7 @@
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* Sauci */,
XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */,
XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */,
);
};
/* End PBXProject section */
@@ -317,8 +316,8 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
FDFABE1A1A3547B1B22DD2DD /* GoogleService-Info.plist in Resources */,
CEF2F278ABBC5FBFD470A0F7 /* PrivacyInfo.xcprivacy in Resources */,
804CD03F6B584D08B7803C63 /* PrivacyInfo.xcprivacy in Resources */,
D60EEEF252214D45B67A0063 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -341,6 +340,8 @@
inputFileListPaths = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env",
"$(SRCROOT)/.xcode.env.local",
);
name = "Bundle React Native code and images";
outputFileListPaths = (
@@ -373,7 +374,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
563ACE2651AC39438F3B399B /* [CP-User] [RNFB] Crashlytics Configuration */ = {
0EE1B67C9AAD8BD8562E800E /* [CP-User] [RNFB] Crashlytics Configuration */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -387,6 +388,52 @@
shellPath = /bin/sh;
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n";
};
503A0FD3DBA862B4BE649BBB /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem.framework/ExpoFileSystem",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoFont/ExpoFont.framework/ExpoFont",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/ExpoImage.framework/ExpoImage",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImage.framework/SDWebImage",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImageAVIFCoder.framework/SDWebImageAVIFCoder",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImageSVGCoder.framework/SDWebImageSVGCoder",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImageWebPCoder.framework/SDWebImageWebPCoder",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/libavif.framework/libavif",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary.framework/ExpoMediaLibrary",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoModulesCore/ExpoModulesCore.framework/ExpoModulesCore",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoModulesJSI/ExpoModulesJSI.framework/ExpoModulesJSI",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoModulesWorklets/ExpoModulesWorklets.framework/ExpoModulesWorklets",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/React-Core-prebuilt/React.framework/React",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ReactNativeDependencies/ReactNativeDependencies.framework/ReactNativeDependencies",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoFileSystem.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoFont.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageAVIFCoder.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libavif.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoMediaLibrary.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoModulesCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoModulesJSI.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoModulesWorklets.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactNativeDependencies.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -398,9 +445,10 @@
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoNotifications/ExpoNotifications_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle",
@@ -414,18 +462,16 @@
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PurchasesHybridCommon/PurchasesHybridCommon.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/ReachabilitySwift.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RevenueCat/RevenueCat.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/react-native-view-shot/RNViewShotPrivacyInfo.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
@@ -433,9 +479,10 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle",
@@ -449,43 +496,54 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PurchasesHybridCommon.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReachabilitySwift.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RevenueCat.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNViewShotPrivacyInfo.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-resources.sh\"\n";
showEnvVarsInLog = 0;
};
A1248897D24903117793599B /* [CP] Embed Pods Frameworks */ = {
8490E10253F91A7BEB80F555 /* [CP-User] [RNFB] Core Configuration */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
);
name = "[CP-User] [RNFB] Core Configuration";
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"note: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"note: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"note: -> RNFB build script started\"\necho \"note: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"note: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"note: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"error: python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACLine truncated
};
D58CF933D4804A0CB49C2891 /* [Expo Dev Launcher] Strip Local Network Keys for Release */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo Dev Launcher] Strip Local Network Keys for Release";
outputFileListPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh\"\n";
showEnvVarsInLog = 0;
shellScript = "# Strip dev-launcher-specific local network permission keys from non-Debug builds\n# This only removes _expo._tcp Bonjour services and the dev-launcher usage description.\n# Other Bonjour services and custom descriptions are preserved for production use.\n\nif [ \"$CONFIGURATION\" != \"Debug\" ]; then\n PLIST_PATH=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n if [ -f \"$PLIST_PATH\" ]; then\n # Check if NSBonjourServices exists\n if /usr/libexec/PlistBuddy -c \"Print :NSBonjourServices\" \"$PLIST_PATH\" >/dev/null 2>&1; then\n # Get the count of services\n COUNT=$(/usr/libexec/PlistBuddy -c \"Print :NSBonjourServices\" \"$PLIST_PATH\" 2>/dev/null | grep \"^ \" | wc -l | tr -d ' ')\n\n # Remove _expo._tcp\n for ((i=COUNT-1; i>=0; i--)); do\n SERVICE=$(/usr/libexec/PlistBuddy -c \"Print :NSBonjourServices:$i\" \"$PLIST_PATH\" 2>/dev/null || echo \"\")\n if echo \"$SERVICE\" | grep -q \"_expo._tcp\"; then\n /usr/libexec/PlistBuddy -c \"Delete :NSBonjourServices:$i\" \"$PLIST_PATH\" 2>/dev/null || true\n fi\n done\n\n # If the array is now empty, remove it entirely\n REMAINING=$(/usr/libexec/PlistBuddy -c \"Print :NSBonjourServices\" \"$PLIST_PATH\" 2>/dev/null | grep \"^ \" | wc -l | tr -d ' ')\n if [ \"$REMAINING\" -eq \"0\" ]; then\n /usr/libexec/PlistBuddy -c \"Delete :NSBonjourServices\" \"$PLIST_PATH\" 2>/dev/null || true\n fi\n fi\n\n # Only delete the description if it matches the dev-launcher default text\n DESC=$(/usr/libexec/PlistBuddy -c \"Print :NSLocalNetworkUsageDescription\" \"$PLIST_PATH\" 2>/dev/null || echo \"\")\n if echo \"$DESC\" | grep -q \"Expo Dev Launcher\"; then\n /usr/libexec/PlistBuddy -c \"Delete :NSLocalNetworkUsageDescription\" \"$PLIST_PATH\" 2>/dev/null || true\n fi\n fi\nfi\n";
};
DC3D179C6B62F16DA5F17C2F /* [Expo] Configure project */ = {
E6F7433229E3C1448FC9F062 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
@@ -509,19 +567,6 @@
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Sauci/expo-configure-project.sh\"\n";
};
FB4B019D835055733ACDE109 /* [CP-User] [RNFB] Core Configuration */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
);
name = "[CP-User] [RNFB] Core Configuration";
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"note: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"note: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"note: -> RNFB build script started\"\necho \"note: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"note: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"note: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"error: python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACLine truncated
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -530,8 +575,7 @@
buildActionMask = 2147483647;
files = (
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */,
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
5254BDADE70741A0B2504FB1 /* noop-file.swift in Sources */,
3F202C2A859975F48A83ADD7 /* ExpoModulesProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -545,17 +589,17 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
XX97289CEC7994AD5F4142XX /* PBXTargetDependency */ = {
XX5D3A21868E45E6E5BDC3XX /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */;
targetProxy = XXA7C7D5580082A497FD95XX /* PBXContainerItemProxy */;
target = XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */;
targetProxy = XX77D941E1DB38490B8F4EXX /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */;
baseConfigurationReference = FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@@ -568,8 +612,12 @@
"FB_SONARKIT_ENABLED=1",
);
INFOPLIST_FILE = Sauci/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.4;
MARKETING_VERSION = 1.0.7;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -589,7 +637,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */;
baseConfigurationReference = A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@@ -597,8 +645,12 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = BXVPBATDZY;
INFOPLIST_FILE = Sauci/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.4;
MARKETING_VERSION = 1.0.7;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -664,29 +716,39 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers/platform/ios",
"${PODS_ROOT}/ReactCommon",
"${PODS_ROOT}/ReactCommon/react/nativemodule/core",
"${PODS_ROOT}/React-runtimeexecutor",
"${PODS_ROOT}/React-runtimeexecutor/platform/ios",
"${PODS_ROOT}/ReactCommon-Samples",
"${PODS_ROOT}/ReactCommon-Samples/platform/ios",
"${PODS_ROOT}/React-Fabric/react/renderer/components/view/platform/cxx",
"${PODS_ROOT}/React-NativeModulesApple",
"${PODS_ROOT}/React-graphics",
"${PODS_ROOT}/React-graphics/react/renderer/graphics/platform/ios",
"${PODS_ROOT}/React-featureflags",
"${PODS_ROOT}/React-renderercss",
);
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
);
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
OTHER_CFLAGS = (
"$(inherited)",
" ",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true;
};
name = Debug;
@@ -733,33 +795,43 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers/platform/ios",
"${PODS_ROOT}/ReactCommon",
"${PODS_ROOT}/ReactCommon/react/nativemodule/core",
"${PODS_ROOT}/React-runtimeexecutor",
"${PODS_ROOT}/React-runtimeexecutor/platform/ios",
"${PODS_ROOT}/ReactCommon-Samples",
"${PODS_ROOT}/ReactCommon-Samples/platform/ios",
"${PODS_ROOT}/React-Fabric/react/renderer/components/view/platform/cxx",
"${PODS_ROOT}/React-NativeModulesApple",
"${PODS_ROOT}/React-graphics",
"${PODS_ROOT}/React-graphics/react/renderer/graphics/platform/ios",
"${PODS_ROOT}/React-featureflags",
"${PODS_ROOT}/React-renderercss",
);
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
);
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = (
OTHER_CFLAGS = (
"$(inherited)",
" ",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
XX1230635DC14DD95E28A2XX /* Release */ = {
XX0A0FB078256184BF7EA2XX /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
@@ -769,7 +841,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ../targets/widget/generated.entitlements;
CODE_SIGN_ENTITLEMENTS = .targets/LiveDrawWidget/generated.entitlements;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 46;
@@ -781,12 +853,17 @@
INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.7;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.LiveDrawWidget;
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -796,7 +873,7 @@
};
name = Release;
};
XX710DB962925BCE8D59ACXX /* Debug */ = {
XX42CD38636C47E26CBE73XX /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
@@ -806,7 +883,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ../targets/widget/generated.entitlements;
CODE_SIGN_ENTITLEMENTS = .targets/LiveDrawWidget/generated.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 46;
DEBUG_INFORMATION_FORMAT = dwarf;
@@ -817,13 +894,18 @@
INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.7;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.LiveDrawWidget;
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -854,11 +936,11 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
XX63FAB1BD532DA0D8AE18XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */ = {
XX35D62F4FBF2C9086E8C9XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */ = {
isa = XCConfigurationList;
buildConfigurations = (
XX710DB962925BCE8D59ACXX /* Debug */,
XX1230635DC14DD95E28A2XX /* Release */,
XX42CD38636C47E26CBE73XX /* Debug */,
XX0A0FB078256184BF7EA2XX /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
+16 -18
View File
@@ -1,10 +1,10 @@
import Expo
internal import Expo
import FirebaseCore
import React
import ReactAppDependencyProvider
@UIApplicationMain
public class AppDelegate: ExpoAppDelegate {
@main
class AppDelegate: ExpoAppDelegate {
var window: UIWindow?
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
@@ -20,11 +20,12 @@ public class AppDelegate: ExpoAppDelegate {
reactNativeDelegate = delegate
reactNativeFactory = factory
bindReactNativeFactory(factory)
#if os(iOS) || os(tvOS)
window = UIWindow(frame: UIScreen.main.bounds)
FirebaseApp.configure()
// @generated begin @react-native-firebase/app-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-10e8520570672fd76b2403b7e1e27f5198a6349a
FirebaseApp.configure()
// @generated end @react-native-firebase/app-didFinishLaunchingWithOptions
factory.startReactNative(
withModuleName: "main",
in: window,
@@ -34,42 +35,39 @@ public class AppDelegate: ExpoAppDelegate {
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// Linking API
public override func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
super.application(app, open: url, options: options)
|| RCTLinkingManager.application(app, open: url, options: options)
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
}
// Universal Links
public override func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
let result = RCTLinkingManager.application(
application,
continue: userActivity,
restorationHandler: restorationHandler)
return super.application(
application,
continue: userActivity,
restorationHandler: restorationHandler) || result
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
}
}
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
// Extension point for config-plugins
override func sourceURL(for bridge: RCTBridge) -> URL? {
// needed to return the correct URL for expo-dev-client.
bridge.bundleURL ?? bundleURL()
}
override func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings()
.jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 57 KiB

+116 -115
View File
@@ -1,118 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Sauci</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>app.sauci</string>
<string>com.sauci.app</string>
</array>
</dict>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>exp+sauci</string>
</array>
</dict>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>46</string>
<key>GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED</key>
<false/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>instagram</string>
<string>instagram-stories</string>
</array>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>Sauci uses your camera to take photos and videos that you can share privately with your partner.</string>
<key>NSFaceIDUsageDescription</key>
<string>Sauci uses Face ID to unlock the app and keep your conversations private.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Sauci uses your microphone to record audio when you capture videos to share privately with your partner.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Sauci uses your photo library so you can select photos and videos to share privately with your partner.</string>
<key>NSUserActivityTypes</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array>
<key>RCTNewArchEnabled</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UIRequiresFullScreen</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Automatic</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Sauci</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>app.sauci</string>
<string>com.sauci.app</string>
</array>
</dict>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>exp+sauci</string>
</array>
</dict>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>46</string>
<key>GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED</key>
<false/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>instagram</string>
<string>instagram-stories</string>
</array>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_expo._tcp</string>
</array>
<key>NSCameraUsageDescription</key>
<string>Sauci uses your camera to take photos and videos that you can share privately with your partner.</string>
<key>NSFaceIDUsageDescription</key>
<string>Sauci uses Face ID to unlock the app and keep your conversations private.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Expo Dev Launcher uses the local network to discover and connect to development servers running on your computer.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Sauci uses your microphone to record audio when you capture videos to share privately with your partner.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Sauci uses your photo library so you can select photos and videos to share privately with your partner.</string>
<key>NSUserActivityTypes</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array>
<key>RCTMetroPort</key>
<string>$(RCT_METRO_PORT)</string>
<key>RCTNewArchEnabled</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UIRequiresFullScreen</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Automatic</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
+170 -9
View File
@@ -24,6 +24,14 @@
<string>C56D.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
@@ -33,18 +41,171 @@
<string>85F4.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeName</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeSensitiveInfo</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeUserID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeDeviceID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypePhotosorVideos</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeAudioData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherUserContent</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeProductInteraction</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeCrashData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypePerformanceData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypePurchaseHistory</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
</dict>
</array>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict>
</plist>
@@ -1,3 +1,3 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
// Use this file to import your target's public headers that you would like to expose to Swift.
//
+1 -1
View File
@@ -17,4 +17,4 @@
<string>group.com.sauci.app</string>
</array>
</dict>
</plist>
</plist>
+10 -8
View File
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24053.1"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -17,15 +18,13 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView id="EXPO-SplashScreen" userLabel="SplashScreenLogo" image="SplashScreenLogo" contentMode="scaleAspectFit" clipsSubviews="true" userInteractionEnabled="false" translatesAutoresizingMaskIntoConstraints="false">
<rect key="frame" x="0" y="0" width="414" height="736"/>
<rect key="frame" x="146.5" y="376" width="100" height="100"/>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
<constraints>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="83fcb9b545b870ba44c24f0feeb116490c499c52"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="61d16215e44b98e39d0a2c74fdbfaaa22601b12c"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="trailing" secondItem="EXPO-ContainerView" secondAttribute="trailing" id="f934da460e9ab5acae3ad9987d5b676a108796c1"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="bottom" secondItem="EXPO-ContainerView" secondAttribute="bottom" id="d6a0be88096b36fb132659aa90203d39139deda9"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="centerX" secondItem="EXPO-ContainerView" secondAttribute="centerX" id="cad2ab56f97c5429bf29decf850647a4216861d4"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="centerY" secondItem="EXPO-ContainerView" secondAttribute="centerY" id="1a145271b085b6ce89b1405a310f5b1bb7656595"/>
</constraints>
<color key="backgroundColor" name="SplashScreenBackground"/>
</view>
@@ -36,7 +35,10 @@
</scene>
</scenes>
<resources>
<image name="SplashScreenLogo" width="414" height="736"/>
<image name="SplashScreenLogo" width="100" height="100"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<namedColor name="SplashScreenBackground">
<color alpha="1.000" blue="0.180392156862745" green="0.101960784313725" red="0.101960784313725" customColorSpace="sRGB" colorSpace="custom"/>
</namedColor>
@@ -31,6 +31,8 @@ Ug==
<key>alg</key>
<string>rsa-v1_5-sha256</string>
</dict>
<key>EXUpdatesEnableBsdiffPatchSupport</key>
<true/>
<key>EXUpdatesEnabled</key>
<true/>
<key>EXUpdatesLaunchWaitMs</key>
-4
View File
@@ -1,4 +0,0 @@
//
// @generated
// A blank Swift file must be created for native modules with Swift files to work correctly.
//
+6
View File
@@ -12,6 +12,12 @@ module.exports = {
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
// react-native-worklets ships a JS-only mock for its native module at
// this path (react-native-reanimated depends on it internally and has
// no equivalent of its own). Without this, requiring the real
// react-native-worklets under Jest throws because no native module is
// registered.
'^react-native-worklets$': '<rootDir>/../../node_modules/react-native-worklets/src/mock.ts',
},
testMatch: [
'<rootDir>/src/**/__tests__/**/*.(spec|test).(ts|tsx)',
+47 -44
View File
@@ -22,6 +22,8 @@
"release:android:ship": "bundle exec fastlane android release",
"release:ios:upload": "bundle exec fastlane ios upload",
"release:ios:ship": "bundle exec fastlane ios release",
"release:preflight": "node scripts/release-preflight.mjs",
"release:preflight:local-eas": "node scripts/release-preflight.mjs --local-eas",
"ota:preflight:staging": "node scripts/ota-publish.mjs staging --preflight",
"ota:preflight:production": "node scripts/ota-publish.mjs production --preflight",
"ota:publish:staging": "node scripts/ota-publish.mjs staging",
@@ -32,7 +34,7 @@
"ota:doctor:production": "node scripts/ota-publish.mjs production --doctor"
},
"dependencies": {
"@bacons/apple-targets": "^0.1.6",
"@bacons/apple-targets": "^5.0.0",
"@bittingz/expo-widgets": "^3.0.2",
"@expo/ngrok": "^4.1.0",
"@expo/vector-icons": "^15.0.3",
@@ -42,54 +44,54 @@
"@react-native-firebase/app": "^23.8.2",
"@react-native-firebase/crashlytics": "^23.8.4",
"@react-native-google-signin/google-signin": "^16.1.1",
"@react-navigation/bottom-tabs": "^7.0.0",
"@react-navigation/native": "^7.0.0",
"@react-navigation/native-stack": "^7.0.0",
"@shopify/react-native-skia": "2.2.12",
"@shopify/react-native-skia": "2.6.2",
"@supabase/supabase-js": "^2.39.0",
"@tanstack/react-query": "^5.17.0",
"babel-preset-expo": "~54.0.12",
"babel-preset-expo": "~56.0.0",
"base64-arraybuffer": "^1.0.2",
"expo": "~54.0.0",
"expo-apple-authentication": "~8.0.8",
"expo-asset": "~12.0.13",
"expo": "^56.0.0",
"expo-apple-authentication": "~56.0.4",
"expo-asset": "~56.0.24",
"expo-av": "~16.0.8",
"expo-blur": "~15.0.8",
"expo-build-properties": "~1.0.10",
"expo-clipboard": "~8.0.8",
"expo-constants": "~18.0.14",
"expo-dev-client": "~6.0.21",
"expo-file-system": "~19.0.24",
"expo-font": "~14.0.12",
"expo-haptics": "~15.0.8",
"expo-image": "~3.0.11",
"expo-image-picker": "~17.0.11",
"expo-linear-gradient": "~15.0.8",
"expo-linking": "~8.0.12",
"expo-local-authentication": "~17.0.9",
"expo-media-library": "~18.2.1",
"expo-notifications": "~0.32.17",
"expo-router": "~6.0.24",
"expo-secure-store": "~15.0.8",
"expo-sharing": "~14.0.8",
"expo-status-bar": "~3.0.9",
"expo-updates": "~29.0.20",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.5",
"expo-blur": "~56.0.4",
"expo-build-properties": "~56.0.27",
"expo-clipboard": "~56.0.4",
"expo-constants": "~56.0.25",
"expo-dev-client": "~56.0.26",
"expo-file-system": "~56.0.11",
"expo-font": "~56.0.7",
"expo-haptics": "~56.0.3",
"expo-image": "~56.0.12",
"expo-image-picker": "~56.0.25",
"expo-linear-gradient": "~56.0.4",
"expo-linking": "~56.0.17",
"expo-local-authentication": "~56.0.5",
"expo-media-library": "~56.0.11",
"expo-notifications": "~56.0.25",
"expo-observe": "~56.0.29",
"expo-router": "~56.2.20",
"expo-secure-store": "~56.0.4",
"expo-sharing": "~56.0.26",
"expo-splash-screen": "~56.0.15",
"expo-status-bar": "~56.0.4",
"expo-system-ui": "~56.0.5",
"expo-updates": "~56.0.26",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.85.3",
"react-native-android-widget": "^0.22.1",
"react-native-compressor": "^1.13.0",
"react-native-gesture-handler": "~2.28.0",
"react-native-gesture-handler": "~2.31.1",
"react-native-purchases": "^9.6.12",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-reanimated": "4.3.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.26.0",
"react-native-share": "^12.3.1",
"react-native-svg": "15.12.1",
"react-native-svg": "15.15.4",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "^4.0.3",
"react-native-view-shot": "5.1.0",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.5.1",
"react-native-worklets": "0.8.3",
"zustand": "^4.4.7"
},
"devDependencies": {
@@ -97,15 +99,16 @@
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^13.3.3",
"@types/jest": "29.5.14",
"@types/react": "~19.1.10",
"@types/node": "^20.19.0",
"@types/react": "~19.2.14",
"@types/react-test-renderer": "^19.1.0",
"dotenv-cli": "^11.0.0",
"eoas": "^3.1.3",
"eslint": "^8.57.0",
"eslint-config-expo": "~10.0.0",
"eslint-config-expo": "~56.0.4",
"jest": "^29.7.0",
"jest-expo": "~54.0.18",
"react-test-renderer": "19.1.0",
"typescript": "~5.9.2"
"jest-expo": "~56.0.5",
"react-test-renderer": "19.2.3",
"typescript": "~6.0.3"
}
}
+6 -1
View File
@@ -3,6 +3,7 @@
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process';
const projectDir = resolve(import.meta.dirname, '..');
@@ -140,4 +141,8 @@ export function main(args = process.argv.slice(2), environment = process.env) {
run('npx', ['eoas', 'publish', '--branch', branch, ...parsePublishArgs(rawPublishArgs)], exportEnv);
}
if (process.argv[1] === new URL(import.meta.url).pathname) main();
// Compare real filesystem paths. A URL pathname percent-encodes spaces, so
// comparing it to process.argv[1] silently skips main() under any checkout
// whose absolute path contains a space, and every ota:* script then exits 0
// having done nothing.
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env node
import { existsSync, readFileSync, statSync, statfsSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { resolve, join } from 'node:path';
const projectDir = resolve(import.meta.dirname, '..');
const repoRoot = resolve(projectDir, '../..');
const certificatePath = 'apps/mobile/certs/certificate.pem';
const semverPattern = /^\d+\.\d+\.\d+$/;
const minimumFreeBuildBytes = 20 * 1024 ** 3;
function fail(message) {
throw new Error(`Mobile release preflight: ${message}`);
}
function plistValue(source, key, label) {
const values = [...source.matchAll(new RegExp(`<key>${key}</key>\\s*<string>([^<]+)</string>`, 'g'))].map((match) => match[1]);
if (values.length !== 1) fail(`expected one ${label} ${key}`);
return values[0];
}
function allMatches(source, expression, label) {
const values = [...source.matchAll(expression)].map((match) => match[1].trim());
if (!values.length) fail(`found no ${label}`);
return values;
}
function gitStatus(args, root = repoRoot) {
const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
if (result.error) fail(`could not run git ${args.join(' ')}: ${result.error.message}`);
return result.status;
}
export function validateReleaseSources({ root = repoRoot, isTracked } = {}) {
const file = (relative) => readFileSync(join(root, relative), 'utf8');
const app = JSON.parse(file('apps/mobile/app.json')).expo;
const appConfig = file('apps/mobile/app.config.js');
const androidGradle = file('apps/mobile/android/app/build.gradle');
const androidStrings = file('apps/mobile/android/app/src/main/res/values/strings.xml');
const xcode = file('apps/mobile/ios/Sauci.xcodeproj/project.pbxproj');
const iosInfo = file('apps/mobile/ios/Sauci/Info.plist');
const widgetInfo = file('apps/mobile/targets/widget/Info.plist');
const iosExpo = file('apps/mobile/ios/Sauci/Supporting/Expo.plist');
const publicVersion = app.version;
const appConfigRuntime = allMatches(appConfig, /runtimeVersion:\s*['"]([^'"]+)['"]/g, 'explicit app.config.js runtimeVersion');
const androidVersion = allMatches(androidGradle, /\bversionName\s+["']([^"']+)["']/g, 'Android versionName');
const androidRuntime = allMatches(androidStrings, /<string name="expo_runtime_version">([^<]+)<\/string>/g, 'Android Expo runtime version');
const xcodeMarketing = allMatches(xcode, /\bMARKETING_VERSION\s*=\s*([^;]+);/g, 'iOS MARKETING_VERSION settings');
const widgetBuildSettings = [...xcode.matchAll(/CURRENT_PROJECT_VERSION\s*=\s*([^;]+);[\s\S]{0,900}?PRODUCT_BUNDLE_IDENTIFIER\s*=\s*com\.sauci\.app\.LiveDrawWidget;/g)].map((match) => match[1].trim());
if (widgetBuildSettings.length !== 2) fail('expected Debug and Release LiveDrawWidget CURRENT_PROJECT_VERSION settings');
const versions = [
publicVersion,
...appConfigRuntime,
...androidVersion,
...androidRuntime,
...xcodeMarketing,
plistValue(iosInfo, 'CFBundleShortVersionString', 'iOS app Info.plist'),
plistValue(widgetInfo, 'CFBundleShortVersionString', 'widget Info.plist'),
plistValue(iosExpo, 'EXUpdatesRuntimeVersion', 'iOS Expo.plist'),
];
if (!versions.every((version) => semverPattern.test(version))) fail(`versions must be numeric SemVer: ${versions.join(', ')}`);
if (new Set(versions).size !== 1) fail(`public or OTA runtime versions disagree: ${versions.join(', ')}`);
const appBuild = plistValue(iosInfo, 'CFBundleVersion', 'iOS app Info.plist');
const widgetBuild = plistValue(widgetInfo, 'CFBundleVersion', 'widget Info.plist');
if (!/^\d+$/.test(appBuild) || !/^\d+$/.test(widgetBuild)) fail('iOS build numbers must be positive integers');
if (appBuild !== widgetBuild || widgetBuildSettings.some((build) => build !== appBuild)) {
fail(`iOS app, widget, and effective widget Xcode build numbers disagree: app ${appBuild}; widget plist ${widgetBuild}; widget settings ${widgetBuildSettings.join(', ')}`);
}
const tracked = isTracked ?? ((relative) => gitStatus(['ls-files', '--error-unmatch', relative], root) === 0);
if (!tracked(certificatePath)) fail(`${certificatePath} must be tracked`);
const archiveIgnore = existsSync(join(root, '.easignore')) ? '.easignore' : '.gitignore';
if (!existsSync(join(root, archiveIgnore))) fail(`missing root ${archiveIgnore} archive rules`);
const archiveRules = file(archiveIgnore).split(/\r?\n/).map((line) => line.trim());
const pemRules = archiveRules.map((line, index) => line === '*.pem' ? index : -1).filter((index) => index >= 0);
const certificateAllowlist = archiveRules.lastIndexOf(`!${certificatePath}`);
if (!pemRules.length) fail(`${archiveIgnore} must retain the private-key-safe *.pem archive rule`);
if (certificateAllowlist <= Math.max(...pemRules)) {
fail(`${archiveIgnore} must later allowlist !${certificatePath} for the public OTA certificate`);
}
if (!file(certificatePath).trim()) fail(`${certificatePath} is empty`);
return { publicVersion, iosBuild: appBuild };
}
export function validateLocalEas(environment = process.env) {
const pluginPath = environment.EAS_LOCAL_BUILD_PLUGIN_PATH;
if (!pluginPath) fail('EAS_LOCAL_BUILD_PLUGIN_PATH must name the matching local-build plugin executable bin/run');
if (!pluginPath.endsWith('/bin/run') || !existsSync(pluginPath) || !statSync(pluginPath).isFile()) {
fail('EAS_LOCAL_BUILD_PLUGIN_PATH must point to an existing executable bin/run, not a package directory');
}
if (!statSync(pluginPath).mode.toString(8).match(/[1357][0-7][0-7]$/)) fail('EAS_LOCAL_BUILD_PLUGIN_PATH bin/run is not executable');
}
export function validateFreeDisk(root = repoRoot, readStats = statfsSync) {
const stats = readStats(root);
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
if (freeBytes < minimumFreeBuildBytes) {
fail(`need at least 20 GiB free before a local build; found ${(freeBytes / 1024 ** 3).toFixed(1)} GiB`);
}
}
export function main(args = process.argv.slice(2), environment = process.env) {
if (args.length > 1 || (args.length === 1 && args[0] !== '--local-eas')) {
fail('usage: node scripts/release-preflight.mjs [--local-eas]');
}
const result = validateReleaseSources();
validateFreeDisk();
if (args.includes('--local-eas')) validateLocalEas(environment);
console.log(`Mobile release source preflight passed for v${result.publicVersion}, iOS build ${result.iosBuild}`);
}
if (process.argv[1] === new URL(import.meta.url).pathname) main();
@@ -0,0 +1,97 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { chmodSync, cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { validateFreeDisk, validateLocalEas, validateReleaseSources } from './release-preflight.mjs';
const repoRoot = join(import.meta.dirname, '..', '..', '..');
function fixture() {
const root = mkdtempSync(join(tmpdir(), 'sauci-release-preflight-'));
cpSync(join(repoRoot, 'apps', 'mobile'), join(root, 'apps', 'mobile'), { recursive: true });
cpSync(join(repoRoot, '.gitignore'), join(root, '.gitignore'));
return root;
}
function replace(root, relative, from, to) {
const file = join(root, relative);
writeFileSync(file, readFileSync(file, 'utf8').replace(from, to));
}
function validate(root) {
return validateReleaseSources({ root, isTracked: () => true });
}
test('accepts current checked-in mobile release sources', () => {
const root = fixture();
try {
const result = validate(root);
assert.match(result.publicVersion, /^\d+\.\d+\.\d+$/);
assert.match(result.iosBuild, /^\d+$/);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test('rejects a widget Xcode build setting that overrides its plist', () => {
const root = fixture();
try {
const { iosBuild } = validate(root);
const project = 'apps/mobile/ios/Sauci.xcodeproj/project.pbxproj';
const source = readFileSync(join(root, project), 'utf8');
const expected = `CURRENT_PROJECT_VERSION = ${iosBuild};`;
const replacement = `CURRENT_PROJECT_VERSION = ${Number(iosBuild) + 1};`;
assert.ok(source.includes(expected));
replace(root, project, expected, replacement);
assert.ok(readFileSync(join(root, project), 'utf8').includes(replacement));
assert.throws(() => validate(root), /iOS app, widget, and effective widget Xcode build numbers disagree/);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test('rejects an OTA certificate without its root archive allowlist', () => {
const root = fixture();
try {
const archiveRules = readFileSync(join(root, '.gitignore'), 'utf8');
assert.ok(archiveRules.includes('!apps/mobile/certs/certificate.pem'));
replace(root, '.gitignore', '!apps/mobile/certs/certificate.pem', '');
assert.ok(!readFileSync(join(root, '.gitignore'), 'utf8').includes('!apps/mobile/certs/certificate.pem'));
assert.throws(
() => validate(root),
/must later allowlist !apps\/mobile\/certs\/certificate\.pem/,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test('requires the local EAS plugin executable rather than its package directory', () => {
const root = mkdtempSync(join(tmpdir(), 'sauci-eas-plugin-'));
const executable = join(root, 'bin-run');
try {
writeFileSync(executable, '#!/bin/sh\n');
chmodSync(executable, 0o755);
assert.throws(
() => validateLocalEas({ EAS_LOCAL_BUILD_PLUGIN_PATH: root }),
/executable bin\/run, not a package directory/,
);
const bin = join(root, 'bin');
mkdirSync(bin);
const run = join(bin, 'run');
cpSync(executable, run);
chmodSync(run, 0o755);
validateLocalEas({ EAS_LOCAL_BUILD_PLUGIN_PATH: run });
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test('requires enough disk before an expensive local build', () => {
assert.throws(
() => validateFreeDisk('/fixture', () => ({ bavail: 19, bsize: 1024 ** 3 })),
/need at least 20 GiB free/,
);
validateFreeDisk('/fixture', () => ({ bavail: 20, bsize: 1024 ** 3 }));
});
@@ -0,0 +1,113 @@
jest.mock('expo-observe', () => ({
Observe: {
logEvent: jest.fn(),
},
}));
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { Observe } = require('expo-observe');
const logEventMock = Observe.logEvent as jest.Mock;
type ErrorHandler = (error: unknown, isFatal: boolean) => void;
function withFreshObserveModule<T>(run: (mod: typeof import('@/lib/observe')) => T): T {
let result!: T;
jest.isolateModules(() => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mod = require('@/lib/observe') as typeof import('@/lib/observe');
result = run(mod);
});
return result;
}
function installFakeErrorUtils(previousHandler: jest.Mock) {
let installedHandler: ErrorHandler = () => undefined;
const globalWithErrorUtils = global as unknown as {
ErrorUtils: { getGlobalHandler: () => unknown; setGlobalHandler: (fn: unknown) => void };
};
globalWithErrorUtils.ErrorUtils = {
getGlobalHandler: () => previousHandler,
setGlobalHandler: (fn) => {
installedHandler = fn as ErrorHandler;
},
};
return () => installedHandler;
}
describe('observe', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('logs app_started only once per module instance', () => {
withFreshObserveModule(({ logAppStarted }) => {
logAppStarted();
logAppStarted();
});
expect(logEventMock).toHaveBeenCalledTimes(1);
expect(logEventMock).toHaveBeenCalledWith('app_started');
});
it('does not throw when Observe.logEvent fails', () => {
logEventMock.mockImplementationOnce(() => {
throw new Error('native module unavailable');
});
withFreshObserveModule(({ logAppStarted }) => {
expect(() => logAppStarted()).not.toThrow();
});
});
it('wraps the previous ErrorUtils handler, logging xprem_js_crash and delegating', () => {
const previousHandler = jest.fn();
const getInstalledHandler = installFakeErrorUtils(previousHandler);
withFreshObserveModule(({ installObserveGlobalErrorHandler }) => {
installObserveGlobalErrorHandler();
});
const error = new Error('boom');
getInstalledHandler()(error, true);
expect(logEventMock).toHaveBeenCalledWith(
'xprem_js_crash',
expect.objectContaining({
severity: 'fatal',
body: 'boom',
})
);
expect(previousHandler).toHaveBeenCalledWith(error, true);
});
it('does not log xprem_js_crash for non-fatal errors but still delegates', () => {
const previousHandler = jest.fn();
const getInstalledHandler = installFakeErrorUtils(previousHandler);
withFreshObserveModule(({ installObserveGlobalErrorHandler }) => {
installObserveGlobalErrorHandler();
});
const error = new Error('handled');
getInstalledHandler()(error, false);
expect(logEventMock).not.toHaveBeenCalled();
expect(previousHandler).toHaveBeenCalledWith(error, false);
});
it('still calls the previous handler when Observe.logEvent throws', () => {
const previousHandler = jest.fn();
const getInstalledHandler = installFakeErrorUtils(previousHandler);
logEventMock.mockImplementationOnce(() => {
throw new Error('native module unavailable');
});
withFreshObserveModule(({ installObserveGlobalErrorHandler }) => {
installObserveGlobalErrorHandler();
});
getInstalledHandler()(new Error('boom'), true);
expect(previousHandler).toHaveBeenCalledWith(expect.any(Error), true);
});
});
@@ -310,7 +310,7 @@ const styles = StyleSheet.create({
color: colors.textTertiary,
},
lockOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
+1 -1
View File
@@ -307,7 +307,7 @@ export const MatchConfetti: React.FC<Props> = ({ visible, onAnimationComplete })
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
zIndex: 1000,
},
particle: {
@@ -344,7 +344,7 @@ export const MatchNotificationModal: React.FC<Props> = ({
const styles = StyleSheet.create({
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.75)',
justifyContent: 'center',
alignItems: 'center',
@@ -377,7 +377,7 @@ const styles = StyleSheet.create({
borderColor: colors.border,
},
flatBackground: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: colors.backgroundLight,
},
// Removed blur/gradient styles
+1 -1
View File
@@ -269,7 +269,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end",
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.85)",
},
content: {
@@ -101,7 +101,7 @@ const styles = StyleSheet.create({
position: 'relative',
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg,
pointerEvents: 'none',
@@ -115,7 +115,7 @@ const styles = StyleSheet.create({
fontWeight: '700',
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg,
pointerEvents: 'none',
@@ -110,7 +110,7 @@ const styles = StyleSheet.create({
marginTop: 2,
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg,
pointerEvents: 'none',
@@ -90,7 +90,7 @@ const styles = StyleSheet.create({
marginTop: 2,
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg,
pointerEvents: 'none',
@@ -336,7 +336,7 @@ const styles = StyleSheet.create({
zIndex: 1000,
},
backdrop: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.8)',
},
keyboardView: {
@@ -615,7 +615,7 @@ const styles = StyleSheet.create({
borderColor: colors.premium.goldLight,
},
annualBackground: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: colors.premium.goldLight,
opacity: 0.3,
},
@@ -804,7 +804,7 @@ const styles = StyleSheet.create({
// Guest confirm modal
guestConfirmOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.8)",
justifyContent: "center",
alignItems: "center",
@@ -131,7 +131,7 @@ const styles = StyleSheet.create({
padding: spacing.lg,
},
backdrop: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.7)",
},
modalContainer: {
@@ -629,7 +629,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end",
},
backdrop: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.6)",
},
sheetContainer: {
@@ -86,7 +86,7 @@ const styles = StyleSheet.create({
padding: spacing.lg,
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.1)",
},
header: {
@@ -19,7 +19,7 @@ import Animated, {
} from "react-native-reanimated";
import { Ionicons } from "@expo/vector-icons";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import ViewShot from "react-native-view-shot";
import ViewShot, { type ViewShotRef } from "react-native-view-shot";
import * as Sharing from "expo-sharing";
import { colors, typography, spacing, blur } from "../../theme";
import { shareToInstagramStories } from "../../lib/instagramShare";
@@ -50,7 +50,7 @@ export function SharePreviewModal({
const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH);
const shareViewRef = useRef<ViewShot>(null);
const shareViewRef = useRef<ViewShotRef>(null);
const [capturedUri, setCapturedUri] = useState<string | null>(null);
const [isCapturing, setIsCapturing] = useState(false);
@@ -308,7 +308,7 @@ export default function MatchesTutorial({ onComplete }: Props) {
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
zIndex: 1000,
},
blur: {
@@ -152,7 +152,7 @@ const styles = StyleSheet.create({
padding: spacing.md,
},
fullScreenContainer: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(13, 13, 26, 0.85)',
alignItems: 'center',
justifyContent: 'center',
@@ -118,7 +118,7 @@ const styles = StyleSheet.create({
overflow: 'hidden',
},
glowContainer: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
},
edgeGlowTop: {
position: 'absolute',
+1 -2
View File
@@ -1,7 +1,6 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { View, StyleSheet, KeyboardAvoidingView, Platform, Alert } from "react-native";
import { useLocalSearchParams, useRouter } from "expo-router";
import { useFocusEffect, useIsFocused } from "@react-navigation/native";
import { useLocalSearchParams, useRouter, useFocusEffect, useIsFocused } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { LinearGradient } from "expo-linear-gradient";
import Animated, { FadeInDown } from "react-native-reanimated";
@@ -159,7 +159,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end",
},
backdrop: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.6)",
},
sheetContainer: {
@@ -203,13 +203,13 @@ const styles = StyleSheet.create({
marginTop: spacing.xs,
},
loadingOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.3)',
},
playOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
},
@@ -220,7 +220,7 @@ const styles = StyleSheet.create({
},
// ...
inputFieldBorder: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
borderRadius: radius.xl,
borderWidth: 1,
borderColor: colors.border,
@@ -790,7 +790,7 @@ const styles = StyleSheet.create({
height: '100%',
},
photoLoadingOverlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.3)',
justifyContent: 'center',
alignItems: 'center',
@@ -179,7 +179,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end",
},
backdrop: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.6)",
},
sheetContainer: {
@@ -125,7 +125,7 @@ const styles = StyleSheet.create({
backgroundColor: 'rgba(0, 0, 0, 0.2)',
},
thumbnail: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
},
iconContainer: {
width: 64,
@@ -136,7 +136,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from "react";
import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, View } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useIsFocused } from "@react-navigation/native";
import { useIsFocused } from "expo-router";
import Animated, { FadeIn } from "react-native-reanimated";
import { GradientBackground } from "../../components/ui";
import { Paywall } from "../../components/paywall";
@@ -7,8 +7,7 @@ import {
Alert,
useWindowDimensions,
} from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useIsFocused } from '@react-navigation/native';
import { useLocalSearchParams, useRouter, useIsFocused } from 'expo-router';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import * as Haptics from 'expo-haptics';
@@ -21,7 +21,7 @@ import Animated, {
} from 'react-native-reanimated';
import { Ionicons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import ViewShot from 'react-native-view-shot';
import ViewShot, { type ViewShotRef } from 'react-native-view-shot';
import * as Sharing from 'expo-sharing';
import * as FileSystem from 'expo-file-system/legacy';
import { colors, gradients, radius, typography, spacing, blur } from '../../../theme';
@@ -44,7 +44,7 @@ export function DrawingShareModal({
const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH);
const shareViewRef = useRef<ViewShot>(null);
const shareViewRef = useRef<ViewShotRef>(null);
const [capturedUri, setCapturedUri] = useState<string | null>(null);
const [isCapturing, setIsCapturing] = useState(false);
@@ -239,7 +239,7 @@ const styles = StyleSheet.create({
overflow: 'hidden',
},
proBorderGradient: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
opacity: 0.3,
},
proCardInner: {
@@ -330,7 +330,7 @@ const styles = StyleSheet.create({
overflow: 'hidden',
},
freeBorderGradient: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
},
freeHeader: {
@@ -51,7 +51,7 @@ const styles = StyleSheet.create({
justifyContent: "space-between",
},
overlay: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.08)",
},
content: {
@@ -13,7 +13,7 @@ import { BlurView } from "expo-blur";
import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown } from "react-native-reanimated";
import { Ionicons } from "@expo/vector-icons";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import ViewShot from "react-native-view-shot";
import ViewShot, { type ViewShotRef } from "react-native-view-shot";
import * as Sharing from "expo-sharing";
import { colors, spacing, typography, blur } from "../../../theme";
import { shareToInstagramStories } from "../../../lib/instagramShare";
@@ -32,7 +32,7 @@ export function QuizShareModal({ visible, onClose, scorePercent }: QuizShareModa
const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH);
const shareViewRef = useRef<ViewShot>(null);
const shareViewRef = useRef<ViewShotRef>(null);
const [capturedUri, setCapturedUri] = useState<string | null>(null);
const [isCapturing, setIsCapturing] = useState(false);
@@ -225,7 +225,7 @@ const styles = StyleSheet.create({
overflow: 'hidden',
},
premiumButtonGradient: {
...StyleSheet.absoluteFillObject,
...StyleSheet.absoluteFill,
},
premiumButtonContent: {
flexDirection: 'row',
+69
View File
@@ -0,0 +1,69 @@
// EAS Observe wrapper. Keeps expo-observe usage in one place and fails soft
// so a metrics problem never blocks app startup or crash reporting.
import { Observe } from "expo-observe";
type ErrorHandler = (error: unknown, isFatal: boolean) => void;
interface GlobalWithErrorUtils {
ErrorUtils?: {
getGlobalHandler(): ErrorHandler;
setGlobalHandler(handler: ErrorHandler): void;
};
}
let appStartedLogged = false;
/**
* Logs the `app_started` custom event once per process lifetime. Safe to call
* more than once; only the first call has an effect.
*/
export function logAppStarted() {
if (appStartedLogged) return;
appStartedLogged = true;
try {
Observe.logEvent("app_started");
} catch (error) {
console.warn("[Observe] Failed to log app_started:", error);
}
}
let globalErrorHandlerInstalled = false;
/**
* Installs a global fatal JS error handler that records an `xprem_js_crash`
* Observe event and then always delegates to whatever handler was previously
* registered (RN's default redbox handler, or another handler such as
* Crashlytics's, if one is installed first). Existing crash reporting is
* never replaced, only observed.
*/
export function installObserveGlobalErrorHandler() {
if (globalErrorHandlerInstalled) return;
const errorUtils = (global as GlobalWithErrorUtils).ErrorUtils;
if (!errorUtils) {
console.warn("[Observe] ErrorUtils is unavailable; xprem_js_crash will not be recorded.");
return;
}
globalErrorHandlerInstalled = true;
const previousHandler = errorUtils.getGlobalHandler();
errorUtils.setGlobalHandler((error, isFatal) => {
// Only fatal errors count as a crash; non-fatal handled errors would
// otherwise skew update-health tracking.
if (isFatal) {
try {
const message = error instanceof Error ? error.message : String(error);
Observe.logEvent("xprem_js_crash", {
severity: "fatal",
body: message,
});
} catch (observeError) {
console.warn("[Observe] Failed to log xprem_js_crash:", observeError);
}
}
previousHandler(error, isFatal);
});
}
+5 -2
View File
@@ -50,8 +50,6 @@ jest.mock('@supabase/supabase-js', () => {
};
});
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper', () => ({}), { virtual: true });
jest.mock('react-native-reanimated', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Reanimated = require('react-native-reanimated/mock');
@@ -103,6 +101,11 @@ jest.mock('expo-image-picker', () => ({
},
}));
jest.mock('expo-media-library', () => ({
usePermissions: jest.fn(() => [{ granted: true }, jest.fn(async () => ({ granted: true }))]),
createAssetAsync: jest.fn(async () => ({ id: 'mock-asset-id' })),
}));
jest.mock('expo-file-system/legacy', () => ({
readAsStringAsync: jest.fn(async () => ''),
EncodingType: {
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.sauci.app</string>
</array>
</dict>
</plist>
+4
View File
@@ -16,6 +16,10 @@
"declarationMap": true,
"noEmit": true,
"jsx": "react-native",
"types": [
"node",
"jest"
],
"paths": {
"@/*": [
"./src/*"
+8 -1
View File
@@ -153,10 +153,17 @@ regenerate and review the native diff separately:
```bash
cd apps/mobile
RELEASE_CHANNEL=production npx expo prebuild
RELEASE_CHANNEL=production npx expo prebuild --clean
git diff # retain only reviewed expo-updates native changes
```
`--clean` is required as of `@bacons/apple-targets` 5.x: running `expo prebuild`
without it against an already-generated iOS project fails inside the widget
target's Xcode project update step (`withIosXcodeProjectBeta2BaseMod`). A clean
regeneration from the committed source avoids that failure. Run `pod install`
in `apps/mobile/ios` afterwards to refresh `Podfile.lock` and the `.xcworkspace`
before committing.
A build produced before this native source change will not check for updates.
## Publishing an update
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff. Show more