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 date: 2026-08-30
status: promoted status: promoted
scope: iOS release artifact verification scope: iOS release artifact verification
evidence: build 45 Xcode archive warning plus direct PlistBuddy inspection of the exported IPA evidence: v1.0.6 exported IPA inspection and reproduced v1.0.7 Xcode build-setting mismatch
destination: .codex/skills/release-mobile/SKILL.md destination: apps/mobile/scripts/release-preflight.mjs and .codex/skills/release-mobile/SKILL.md
review_after: 2027-02-28 review_after: 2027-02-28
--- ---
@@ -21,19 +21,23 @@ and every packaged extension `Info.plist` with `PlistBuddy`.
## Cause ## Cause
The archive warning described an intermediate state and was not authoritative The v1.0.6 archive warning described an intermediate state and was not
for the exported package. Direct IPA inspection showed both the app and authoritative for that exported package. The later v1.0.7 reproduction found a
`LiveDrawWidget.appex` at version 1.0.6 build 45. separate real hazard: `CURRENT_PROJECT_VERSION` in both LiveDrawWidget
configurations can override the widget plist and package an older build number.
## Remediation ## Remediation
For every iOS release, inspect version and build values in the exported app and Before every iOS release, require public-version parity across app config, both
all packaged extensions. Reject or rebuild only when the final IPA values differ; plists, and Xcode marketing settings. Require the widget Debug and Release
do not accept or reject a release from archive warnings alone. `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 ## Evidence
`PlistBuddy` read `CFBundleShortVersionString` 1.0.6 and `CFBundleVersion` 45 from `PlistBuddy` read `CFBundleShortVersionString` 1.0.6 and `CFBundleVersion` 45 from
both packaged plists. `codesign --verify --deep --strict` passed for the app, and both v1.0.6 packaged plists. The v1.0.7 build later proved a stale widget Xcode
the verified IPA SHA-256 was setting can survive plist-only review. `release-preflight.mjs` and its node test
`9d4779bddac4647911a3a38e4ee37cf794c2b8691e0746a041b629c62b308e7f`. 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 - consistent public and OTA runtime versions across `app.json`, explicit
`app.config.js` runtime, Android, and every iOS target `app.config.js` runtime, Android, and every iOS target
(`scripts/set_version.mjs --check`); (`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; - Node 20, JDK 17 for Android, and Xcode/CocoaPods for iOS;
- the configured production API/Auth/RevenueCat environment, without printing - the configured production API/Auth/RevenueCat environment, without printing
secret values. 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, versioned artifact directory. When building both platforms, build Android first,
then iOS. 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: After each build:
1. Inspect the worktree before doing anything else. Accept only the documented 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. dependency, or native-project regeneration as a failed build.
2. Independently inspect the binary for package/bundle identifier, public 2. Independently inspect the binary for package/bundle identifier, public
version, platform build number, production endpoint configuration where version, platform build number, production endpoint configuration where
inspectable, and signing identity/fingerprint. Require the embedded public inspectable, signing identity/fingerprint, OTA runtime/channel/certificate,
version to equal the requested version. 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. 3. Record the verified platform build number in the new release-note entry.
4. Commit only the expected metadata and release-note update as 4. Commit only the expected metadata and release-note update as
`chore(<platform>): record v<version> build <number>`. The worktree must be `chore(<platform>): record v<version> build <number>`. The worktree must be
clean before starting the next platform build. clean before starting the next platform build.
Do not accept a successful EAS exit code as proof of a valid artifact. If either Do not accept a successful EAS exit code as proof of a valid artifact. Quarantine
requested platform fails, do not tag a partial release. 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 ## 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 so routine release builds must package those checked-in sources. Do not run
`expo prebuild` first; native regeneration is a separate reviewed change. `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 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 Fastlane lanes in `apps/mobile/fastlane/Fastfile` to upload, submit, and roll
out. out.
@@ -83,6 +102,11 @@ submission, and rollout without requesting further confirmation.
when used, pass explicit `--platform`, `--profile production`, `--path`, and when used, pass explicit `--platform`, `--profile production`, `--path`, and
`--wait` — never `--latest`. `--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 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 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 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 reject or rebuild only after reading the exported IPA itself. Report the
artifact, version/build number, upload and submission results, rollout state, artifact, version/build number, upload and submission results, rollout state,
and store read-back 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 = { module.exports = {
extends: 'expo', extends: 'expo',
ignorePatterns: ['/dist/*'], 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: [ overrides: [
{ {
files: ['plugins/**/*.js'], files: ['plugins/**/*.js'],
+3
View File
@@ -12,5 +12,8 @@ local.properties
*.hprof *.hprof
.cxx/ .cxx/
# generated inline modules
app/src/main/java/inline/
# Bundle artifacts # Bundle artifacts
*.jsbundle *.jsbundle
+4 -53
View File
@@ -11,7 +11,7 @@ def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
react { react {
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) 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() 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() 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() 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() def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBuilds') ?: false).toBoolean()
@@ -94,6 +94,7 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 49 versionCode 49
versionName "1.0.7" versionName "1.0.7"
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\"" buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
} }
signingConfigs { signingConfigs {
@@ -181,54 +182,4 @@ dependencies {
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics' 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",
)
}
}
}
@@ -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"> <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.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <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_AUDIO"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="remove"/> <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.READ_MEDIA_VISUAL_USER_SELECTED"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <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_BIOMETRIC"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/> <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.VIBRATE"/> <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"/> <uses-permission android:name="com.android.vending.BILLING"/>
<queries> <queries>
<package android:name="com.instagram.android"/> <package android:name="com.instagram.android"/>
@@ -22,7 +22,7 @@
<data android:scheme="https"/> <data android:scheme="https"/>
</intent> </intent>
</queries> </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_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="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"/> <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_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.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.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_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_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_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.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;}"/> <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"/> <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> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
@@ -46,7 +47,6 @@
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="app.sauci"/> <data android:scheme="app.sauci"/>
<data android:scheme="com.sauci.app"/>
<data android:scheme="exp+sauci"/> <data android:scheme="exp+sauci"/>
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true" data-generated="true"> <intent-filter android:autoVerify="true" data-generated="true">
@@ -56,7 +56,7 @@
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
</activity> </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> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.sauci.app.WIDGET_CLICK"/> <action android:name="com.sauci.app.WIDGET_CLICK"/>
@@ -1,4 +1,5 @@
package com.sauci.app package com.sauci.app
import expo.modules.splashscreen.SplashScreenManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -15,7 +16,10 @@ class MainActivity : ReactActivity() {
// Set the theme to AppTheme BEFORE onCreate to support // Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar. // coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen. // 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) super.onCreate(null)
} }
@@ -5,40 +5,27 @@ import android.content.res.Configuration
import com.facebook.react.PackageList import com.facebook.react.PackageList
import com.facebook.react.ReactApplication 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.ReactPackage
import com.facebook.react.ReactHost 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.common.ReleaseLevel
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import expo.modules.ApplicationLifecycleDispatcher import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper import expo.modules.ExpoReactHostFactory
class MainApplication : Application(), ReactApplication { class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( override val reactHost: ReactHost by lazy {
this, ExpoReactHostFactory.getDefaultReactHost(
object : DefaultReactNativeHost(this) { context = applicationContext,
override fun getPackages(): List<ReactPackage> { packageList =
val packages = PackageList(this).packages PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example: // Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage()); // add(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 fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@@ -1,84 +1,6 @@
package com.sauci.app.widget; package com.sauci.app.widget;
import android.appwidget.AppWidgetManager; import com.reactnativeandroidwidget.RNWidgetProvider;
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.sauci.app.R; public class LiveDraw extends RNWidgetProvider {
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;
}
} }
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="splashscreen_background">#1a1a2e</color>
<color name="iconBackground">#1a1a2e</color> <color name="iconBackground">#1a1a2e</color>
<color name="colorPrimary">#023c69</color> <color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#1a1a2e</color>
<color name="notification_icon_color">#e94560</color> <color name="notification_icon_color">#e94560</color>
</resources> </resources>
@@ -1,7 +1,7 @@
<resources> <resources>
<string name="app_name">Sauci</string> <string name="app_name">Sauci</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string> <string name="expo_system_ui_user_interface_style" translatable="false">automatic</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_runtime_version">1.0.7</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> </resources>
@@ -1,18 +1,14 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.DayNight.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>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item> <item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</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>
<style name="ResetEditText" parent="@android:style/Widget.EditText"> <style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
<item name="android:padding">0dp</item> <item name="windowSplashScreenBackground">@color/splashscreen_background</item>
<item name="android:textColorHint">#c8c8c8</item> <item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
<item name="android:textColor">@android:color/black</item> <item name="postSplashScreenTheme">@style/AppTheme</item>
</style> <item name="android:windowSplashScreenBehavior">icon_preferred</item>
<style name="Theme.App.SplashScreen" parent="AppTheme">
<item name="android:windowBackground">@drawable/ic_launcher_background</item>
</style> </style>
</resources> </resources>
@@ -4,10 +4,18 @@
android:minHeight="110dp" android:minHeight="110dp"
android:targetCellWidth="4" android:targetCellWidth="4"
android:targetCellHeight="2" android:targetCellHeight="2"
android:resizeMode="none" android:resizeMode="none"
android:description="@string/widget_livedraw_description" android:description="@string/widget_livedraw_description"
android:initialLayout="@layout/widget_live_draw"
android:initialLayout="@layout/rn_widget"
android:previewImage="@drawable/livedraw_preview" android:previewImage="@drawable/livedraw_preview"
android:updatePeriodMillis="1800000" android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen"> android:widgetCategory="home_screen">
</appwidget-provider> </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. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6' classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6'
classpath 'com.google.gms:google-services:4.4.1' classpath 'com.google.gms:google-services:4.4.4'
classpath('com.android.tools.build:gradle') classpath('com.android.tools.build:gradle')
classpath('com.facebook.react:react-native-gradle-plugin') classpath('com.facebook.react:react-native-gradle-plugin')
classpath('org.jetbrains.kotlin:kotlin-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: "expo-root-project"
apply plugin: "com.facebook.react.rootproject" 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. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # 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 # 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 # 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 # 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. # If set to false, you will be using JSC instead.
hermesEnabled=true 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) # Enable GIF support in React Native images (~200 B increase)
expo.gif.enabled=true expo.gif.enabled=true
# Enable webp support in React Native images (~85 KB increase) # 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. # Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false expo.useLegacyPackaging=false
android.targetSdkVersion=36 expo.inlineModules.watchedDirectories=[]
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
android.compileSdkVersion=36 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+4 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # 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 APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # 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 # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -206,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # 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. # 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 # * 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. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # 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
@rem Copyright 2015 the original author or authors. @rem Copyright 2015 the original author or authors.
@rem @rem
@@ -70,11 +75,10 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @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 :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
+2 -2
View File
@@ -6,7 +6,7 @@ pluginManagement {
}.standardOutput.asText.get().trim() }.standardOutput.asText.get().trim()
).getParentFile().absolutePath ).getParentFile().absolutePath
includeBuild(reactNativeGradlePlugin) includeBuild(reactNativeGradlePlugin)
def expoPluginsPath = new File( def expoPluginsPath = new File(
providers.exec { providers.exec {
workingDir(rootDir) workingDir(rootDir)
@@ -29,10 +29,10 @@ extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand) ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
} }
} }
expoAutolinking.useExpoModules()
rootProject.name = 'Sauci' rootProject.name = 'Sauci'
expoAutolinking.useExpoModules()
expoAutolinking.useExpoVersionCatalog() expoAutolinking.useExpoVersionCatalog()
include ':app' include ':app'
+12 -7
View File
@@ -7,12 +7,6 @@
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
"scheme": "app.sauci", "scheme": "app.sauci",
"userInterfaceStyle": "automatic", "userInterfaceStyle": "automatic",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#1a1a2e"
},
"ios": { "ios": {
"supportsTablet": false, "supportsTablet": false,
"bundleIdentifier": "com.sauci.app", "bundleIdentifier": "com.sauci.app",
@@ -223,6 +217,14 @@
}, },
"plugins": [ "plugins": [
"./plugins/withFixHoisting.js", "./plugins/withFixHoisting.js",
[
"expo-splash-screen",
{
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#1a1a2e"
}
],
"@react-native-firebase/app", "@react-native-firebase/app",
[ [
"expo-build-properties", "expo-build-properties",
@@ -320,7 +322,10 @@
"origin": false "origin": false
}, },
"eas": { "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" "owner": "jjbrunton-apps"
+1 -1
View File
@@ -389,7 +389,7 @@ const styles = StyleSheet.create({
zIndex: 100, zIndex: 100,
}, },
navBarBackground: { navBarBackground: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: colors.border, borderBottomColor: colors.border,
backgroundColor: colors.background, backgroundColor: colors.background,
+20 -14
View File
@@ -5,6 +5,11 @@ import "../src/polyfills/web";
import { initCrashlytics, setUserContext, clearUserContext } from "../src/lib/crashlytics"; import { initCrashlytics, setUserContext, clearUserContext } from "../src/lib/crashlytics";
initCrashlytics(); 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) // Analytics imports (initialization deferred until app mounts)
import { initAnalytics, setUserId, clearUserId, logScreenView, Events } from "../src/lib/analytics"; import { initAnalytics, setUserId, clearUserId, logScreenView, Events } from "../src/lib/analytics";
@@ -73,6 +78,7 @@ export default function RootLayout() {
analyticsInitialized.current = true; analyticsInitialized.current = true;
initAnalytics(); initAnalytics();
Events.appOpened("cold"); Events.appOpened("cold");
logAppStarted();
} }
}, []); }, []);
@@ -115,15 +121,15 @@ export default function RootLayout() {
// Fetch user on mount // Fetch user on mount
fetchUser(); fetchUser();
// Handle initial URL if app was opened via deep link // Handle initial URL if app was opened via deep link
const handleDeepLink = async (url: string | null) => { const handleDeepLink = async (url: string | null) => {
if (!url) return; if (!url) return;
// Invite links (https://sauci.app/join/{code} or app.sauci://join?code={code}) // Invite links (https://sauci.app/join/{code} or app.sauci://join?code={code})
// are handled separately from the Supabase auth deep link flow below. // are handled separately from the Supabase auth deep link flow below.
const inviteLink = parseInviteLinkCode(url); const inviteLink = parseInviteLinkCode(url);
if (inviteLink) { if (inviteLink) {
Events.inviteLinkOpened(inviteLink.source); Events.inviteLinkOpened(inviteLink.source);
await stashPendingInviteCode(inviteLink.code); await stashPendingInviteCode(inviteLink.code);
const { isAuthenticated, user } = useAuthStore.getState(); const { isAuthenticated, user } = useAuthStore.getState();
@@ -200,15 +206,15 @@ export default function RootLayout() {
} }
}; };
// Get initial URL // Get initial URL
Linking.getInitialURL().then((url) => { Linking.getInitialURL().then((url) => {
handleDeepLink(url); handleDeepLink(url);
}); });
// Listen for new deep links while app is open // Listen for new deep links while app is open
const linkingSubscription = Linking.addEventListener("url", (event) => { const linkingSubscription = Linking.addEventListener("url", (event) => {
handleDeepLink(event.url); handleDeepLink(event.url);
}); });
// Set up push notification response listener (for taps while app is running) // Set up push notification response listener (for taps while app is running)
const notificationSubscription = setupNotificationResponseListener(); const notificationSubscription = setupNotificationResponseListener();
+1 -1
View File
@@ -425,7 +425,7 @@ const styles = StyleSheet.create({
zIndex: 100, zIndex: 100,
}, },
navBarBackground: { navBarBackground: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: colors.border, borderBottomColor: colors.border,
overflow: "hidden", overflow: "hidden",
+3
View File
@@ -28,3 +28,6 @@ project.xcworkspace
# CocoaPods # CocoaPods
/Pods/ /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('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require 'json' require 'json'
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' def ccache_enabled?(podfile_properties)
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] # Environment variable takes precedence
return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE']
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' # Fall back to Podfile properties
install! 'cocoapods', podfile_properties['apple.ccacheEnabled'] == 'true'
:deterministic_uuids => false 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! prepare_react_native_project!
@@ -23,7 +36,8 @@ target 'Sauci' do
'node', 'node',
'--no-warnings', '--no-warnings',
'--eval', '--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', 'react-native-config',
'--json', '--json',
'--platform', '--platform',
@@ -49,34 +63,8 @@ target 'Sauci' do
installer, installer,
config[:reactNativePath], config[:reactNativePath],
:mac_catalyst_enabled => false, :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
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", "expo.jsEngine": "hermes",
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
"newArchEnabled": "true", "expo.inlineModules.watchedDirectories": "[]",
"ios.useFrameworks": "static", "expo.inlineModules.xcodeProjectTargets": "{\"mainTarget\":\"Sauci\",\"targets\":[]}",
"apple.extraPods": "[]", "ios.forceStaticLinking": "[]",
"apple.ccacheEnabled": "false", "apple.privacyManifestAggregationEnabled": "true",
"apple.privacyManifestAggregationEnabled": "true" "EXPO_USE_PRECOMPILED_MODULES": "true",
"ios.useFrameworks": "static"
} }
+225 -143
View File
@@ -3,19 +3,18 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 46; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
5254BDADE70741A0B2504FB1 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = F744EAD4ABA54509966475D6 /* noop-file.swift */; }; 3F202C2A859975F48A83ADD7 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */; };
7B71EEB2AF682FA7AFF556D5 /* Pods_Sauci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */; }; 45941E25D336B6152BFCEFB9 /* Pods_Sauci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; 804CD03F6B584D08B7803C63 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; 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 */; }; 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 */; }; XX12B55A1EDABF083C3B51XX /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */; };
XX1A6800511A685AB346D2XX /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXA12889081EB894481655XX /* AppIntents.framework */; }; XX1A6800511A685AB346D2XX /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXA12889081EB894481655XX /* AppIntents.framework */; };
XX3C562DEF346B45594832XX /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */; }; XX3C562DEF346B45594832XX /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */; };
@@ -24,11 +23,11 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
XXA7C7D5580082A497FD95XX /* PBXContainerItemProxy */ = { XX77D941E1DB38490B8F4EXX /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1; proxyType = 1;
remoteGlobalIDString = XX540FB4ABC75CE91A6036XX; remoteGlobalIDString = XXB3405CC208C2ECA4797FXX;
remoteInfo = LiveDrawWidget; remoteInfo = LiveDrawWidget;
}; };
/* End PBXContainerItemProxy section */ /* 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; }; 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>"; }; 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>"; }; 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>"; }; 69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = PrivacyInfo.xcprivacy; path = Sauci/PrivacyInfo.xcprivacy; 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>"; }; 779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Sauci/ExpoModulesProvider.swift"; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; };
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; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 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>"; }; 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>"; }; 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>"; };
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>"; }; 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; }; 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; }; 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; }; 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 */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
XX020388E3BB9C764E774FXX /* Exceptions for "widget" folder in "LiveDrawWidget" target */ = { XX6FB9E6678087E3AAC658XX /* Exceptions for "widget" folder in "LiveDrawWidget" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet; isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = ( membershipExceptions = (
Info.plist, Info.plist,
"expo-target.config.js", "expo-target.config.js",
); );
target = XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */; target = XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */;
}; };
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFileSystemSynchronizedRootGroup section */
XX683E29AD7AD8279E17B2XX /* widget */ = { XXBB44BF07EB68666150ABXX /* widget */ = {
isa = PBXFileSystemSynchronizedRootGroup; isa = PBXFileSystemSynchronizedRootGroup;
exceptions = ( exceptions = (
XX020388E3BB9C764E774FXX /* Exceptions for "widget" folder in "LiveDrawWidget" target */, XX6FB9E6678087E3AAC658XX /* Exceptions for "widget" folder in "LiveDrawWidget" target */,
); );
explicitFileTypes = { explicitFileTypes = {
}; };
@@ -101,7 +99,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7B71EEB2AF682FA7AFF556D5 /* Pods_Sauci.framework in Frameworks */, 45941E25D336B6152BFCEFB9 /* Pods_Sauci.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -122,15 +120,32 @@
13B07FAE1A68108700A75B9A /* Sauci */ = { 13B07FAE1A68108700A75B9A /* Sauci */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BB2F792B24A3F905000567C9 /* Supporting */,
F11748412D0307B40044C1D9 /* AppDelegate.swift */, F11748412D0307B40044C1D9 /* AppDelegate.swift */,
F11748442D0722820044C1D9 /* Sauci-Bridging-Header.h */,
BB2F792B24A3F905000567C9 /* Supporting */,
13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB61A68108700A75B9A /* Info.plist */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */, 69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */,
F744EAD4ABA54509966475D6 /* noop-file.swift */, D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */,
DA536CAC88B349A59DFC1F1E /* Sauci-Bridging-Header.h */, );
D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */, 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; name = Sauci;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -141,9 +156,9 @@
ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */, XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */,
XX4DFF38D47332D6BF0183XX /* SwiftUI.framework */, XX4DFF38D47332D6BF0183XX /* SwiftUI.framework */,
DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */,
XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */, XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */,
XXA12889081EB894481655XX /* AppIntents.framework */, XXA12889081EB894481655XX /* AppIntents.framework */,
B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -163,8 +178,8 @@
832341AE1AAA6A7D00B99B32 /* Libraries */, 832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */, 2D16E6871FA4F8E400B85C8A /* Frameworks */,
D65327D7A22EEC0BE12398D9 /* Pods */, 1957149DB83AAF8603C60859 /* Pods */,
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */, CC8FEB5C5178B9E5E71D4F61 /* ExpoModulesProviders */,
); );
indentWidth = 2; indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -180,14 +195,6 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
92DBD88DE9BF7D494EA9DA96 /* Sauci */ = {
isa = PBXGroup;
children = (
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
);
name = Sauci;
sourceTree = "<group>";
};
BB2F792B24A3F905000567C9 /* Supporting */ = { BB2F792B24A3F905000567C9 /* Supporting */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -197,19 +204,10 @@
path = Sauci/Supporting; path = Sauci/Supporting;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D65327D7A22EEC0BE12398D9 /* Pods */ = { CC8FEB5C5178B9E5E71D4F61 /* ExpoModulesProviders */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */, 2BFCD64E7EEB594CC2D9E261 /* Sauci */,
7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
isa = PBXGroup;
children = (
92DBD88DE9BF7D494EA9DA96 /* Sauci */,
); );
name = ExpoModulesProviders; name = ExpoModulesProviders;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -217,7 +215,7 @@
XX3E41C1850246162C0061XX /* expo:targets */ = { XX3E41C1850246162C0061XX /* expo:targets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
XX683E29AD7AD8279E17B2XX /* widget */, XXBB44BF07EB68666150ABXX /* widget */,
); );
name = "expo:targets"; name = "expo:targets";
path = ../targets; path = ../targets;
@@ -231,30 +229,31 @@
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sauci" */; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sauci" */;
buildPhases = ( buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
DC3D179C6B62F16DA5F17C2F /* [Expo] Configure project */, E6F7433229E3C1448FC9F062 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */, 13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
D58CF933D4804A0CB49C2891 /* [Expo Dev Launcher] Strip Local Network Keys for Release */,
XX72A8CFBF0FBA13A79902XX /* Embed Foundation Extensions */, XX72A8CFBF0FBA13A79902XX /* Embed Foundation Extensions */,
A1248897D24903117793599B /* [CP] Embed Pods Frameworks */, 503A0FD3DBA862B4BE649BBB /* [CP] Embed Pods Frameworks */,
FB4B019D835055733ACDE109 /* [CP-User] [RNFB] Core Configuration */, 8490E10253F91A7BEB80F555 /* [CP-User] [RNFB] Core Configuration */,
563ACE2651AC39438F3B399B /* [CP-User] [RNFB] Crashlytics Configuration */, 0EE1B67C9AAD8BD8562E800E /* [CP-User] [RNFB] Crashlytics Configuration */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
XX97289CEC7994AD5F4142XX /* PBXTargetDependency */, XX5D3A21868E45E6E5BDC3XX /* PBXTargetDependency */,
); );
name = Sauci; name = Sauci;
productName = Sauci; productName = Sauci;
productReference = 13B07F961A680F5B00A75B9A /* Sauci.app */; productReference = 13B07F961A680F5B00A75B9A /* Sauci.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */ = { XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = XX63FAB1BD532DA0D8AE18XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */; buildConfigurationList = XX35D62F4FBF2C9086E8C9XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */;
buildPhases = ( buildPhases = (
XX074B8811C1CF8E8A3D74XX /* Frameworks */, XX074B8811C1CF8E8A3D74XX /* Frameworks */,
XXE45443DC38DCFE1DB622XX /* Sources */, XXE45443DC38DCFE1DB622XX /* Sources */,
@@ -265,7 +264,7 @@
dependencies = ( dependencies = (
); );
fileSystemSynchronizedGroups = ( fileSystemSynchronizedGroups = (
XX683E29AD7AD8279E17B2XX /* widget */, XXBB44BF07EB68666150ABXX /* widget */,
); );
name = LiveDrawWidget; name = LiveDrawWidget;
productName = LiveDrawWidget; productName = LiveDrawWidget;
@@ -283,7 +282,7 @@
13B07F861A680F5B00A75B9A = { 13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1250; LastSwiftMigration = 1250;
}; };
XX540FB4ABC75CE91A6036XX = { XXB3405CC208C2ECA4797FXX = {
CreatedOnToolsVersion = 14.3; CreatedOnToolsVersion = 14.3;
DevelopmentTeam = BXVPBATDZY; DevelopmentTeam = BXVPBATDZY;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
@@ -304,7 +303,7 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
13B07F861A680F5B00A75B9A /* Sauci */, 13B07F861A680F5B00A75B9A /* Sauci */,
XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */, XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -317,8 +316,8 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */, BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
FDFABE1A1A3547B1B22DD2DD /* GoogleService-Info.plist in Resources */, 804CD03F6B584D08B7803C63 /* PrivacyInfo.xcprivacy in Resources */,
CEF2F278ABBC5FBFD470A0F7 /* PrivacyInfo.xcprivacy in Resources */, D60EEEF252214D45B67A0063 /* GoogleService-Info.plist in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -341,6 +340,8 @@
inputFileListPaths = ( inputFileListPaths = (
); );
inputPaths = ( inputPaths = (
"$(SRCROOT)/.xcode.env",
"$(SRCROOT)/.xcode.env.local",
); );
name = "Bundle React Native code and images"; name = "Bundle React Native code and images";
outputFileListPaths = ( 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"; 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; showEnvVarsInLog = 0;
}; };
563ACE2651AC39438F3B399B /* [CP-User] [RNFB] Crashlytics Configuration */ = { 0EE1B67C9AAD8BD8562E800E /* [CP-User] [RNFB] Crashlytics Configuration */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@@ -387,6 +388,52 @@
shellPath = /bin/sh; 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"; 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 */ = { 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -398,9 +445,10 @@
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.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}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_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}/FirebaseCore/FirebaseCore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_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}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PurchasesHybridCommon/PurchasesHybridCommon.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}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.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-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RevenueCat/RevenueCat.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-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.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}/nanopb/nanopb_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/react-native-view-shot/RNViewShotPrivacyInfo.bundle",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Copy Pods Resources";
outputPaths = ( outputPaths = (
@@ -433,9 +479,10 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", "${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}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.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}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_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}/FirebaseCore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_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}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_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}/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}/RNCAsyncStorage_resources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.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-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_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}/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}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.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}/nanopb_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNViewShotPrivacyInfo.bundle",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-resources.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-resources.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
A1248897D24903117793599B /* [CP] Embed Pods Frameworks */ = { 8490E10253F91A7BEB80F555 /* [CP-User] [RNFB] Core Configuration */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh", "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", );
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 = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh\"\n"; 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";
showEnvVarsInLog = 0;
}; };
DC3D179C6B62F16DA5F17C2F /* [Expo] Configure project */ = { E6F7433229E3C1448FC9F062 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -509,19 +567,6 @@
shellPath = /bin/sh; 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"; 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 */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@@ -530,8 +575,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */, F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */,
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */, 3F202C2A859975F48A83ADD7 /* ExpoModulesProvider.swift in Sources */,
5254BDADE70741A0B2504FB1 /* noop-file.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -545,17 +589,17 @@
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
XX97289CEC7994AD5F4142XX /* PBXTargetDependency */ = { XX5D3A21868E45E6E5BDC3XX /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */; target = XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */;
targetProxy = XXA7C7D5580082A497FD95XX /* PBXContainerItemProxy */; targetProxy = XX77D941E1DB38490B8F4EXX /* PBXContainerItemProxy */;
}; };
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */; baseConfigurationReference = FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
@@ -568,8 +612,12 @@
"FB_SONARKIT_ENABLED=1", "FB_SONARKIT_ENABLED=1",
); );
INFOPLIST_FILE = Sauci/Info.plist; INFOPLIST_FILE = Sauci/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1; IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.4;
MARKETING_VERSION = 1.0.7; MARKETING_VERSION = 1.0.7;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
@@ -589,7 +637,7 @@
}; };
13B07F951A680F5B00A75B9A /* Release */ = { 13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */; baseConfigurationReference = A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
@@ -597,8 +645,12 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = BXVPBATDZY; DEVELOPMENT_TEAM = BXVPBATDZY;
INFOPLIST_FILE = Sauci/Info.plist; INFOPLIST_FILE = Sauci/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1; IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.4;
MARKETING_VERSION = 1.0.7; MARKETING_VERSION = 1.0.7;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
@@ -664,29 +716,39 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", "${PODS_ROOT}/ReactCommon",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", "${PODS_ROOT}/ReactCommon/react/nativemodule/core",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", "${PODS_ROOT}/React-runtimeexecutor",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_ROOT}/React-runtimeexecutor/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", "${PODS_ROOT}/ReactCommon-Samples",
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_ROOT}/ReactCommon-Samples/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", "${PODS_ROOT}/React-Fabric/react/renderer/components/view/platform/cxx",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", "${PODS_ROOT}/React-NativeModulesApple",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers", "${PODS_ROOT}/React-graphics",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers/platform/ios", "${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)\""; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = ( OTHER_CFLAGS = (
"$(inherited)", "$(inherited)",
" ", "-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
); );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true; USE_HERMES = true;
}; };
name = Debug; name = Debug;
@@ -733,33 +795,43 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", "${PODS_ROOT}/ReactCommon",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", "${PODS_ROOT}/ReactCommon/react/nativemodule/core",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", "${PODS_ROOT}/React-runtimeexecutor",
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_ROOT}/React-runtimeexecutor/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", "${PODS_ROOT}/ReactCommon-Samples",
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_ROOT}/ReactCommon-Samples/platform/ios",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", "${PODS_ROOT}/React-Fabric/react/renderer/components/view/platform/cxx",
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", "${PODS_ROOT}/React-NativeModulesApple",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers", "${PODS_ROOT}/React-graphics",
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers/platform/ios", "${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)\""; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = ( OTHER_CFLAGS = (
"$(inherited)", "$(inherited)",
" ", "-DRCT_REMOVE_LEGACY_ARCH=1",
);
OTHER_CPLUSPLUSFLAGS = (
"$(inherited)",
"-DRCT_REMOVE_LEGACY_ARCH=1",
); );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
USE_HERMES = true; USE_HERMES = true;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;
}; };
XX1230635DC14DD95E28A2XX /* Release */ = { XX0A0FB078256184BF7EA2XX /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
@@ -769,7 +841,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ../targets/widget/generated.entitlements; CODE_SIGN_ENTITLEMENTS = .targets/LiveDrawWidget/generated.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 46; CURRENT_PROJECT_VERSION = 46;
@@ -781,12 +853,17 @@
INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget; INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.0; 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; MARKETING_VERSION = 1.0.7;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -796,7 +873,7 @@
}; };
name = Release; name = Release;
}; };
XX710DB962925BCE8D59ACXX /* Debug */ = { XX42CD38636C47E26CBE73XX /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
@@ -806,7 +883,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ../targets/widget/generated.entitlements; CODE_SIGN_ENTITLEMENTS = .targets/LiveDrawWidget/generated.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 46; CURRENT_PROJECT_VERSION = 46;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
@@ -817,13 +894,18 @@
INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget; INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.0; 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; MARKETING_VERSION = 1.0.7;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -854,11 +936,11 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
XX63FAB1BD532DA0D8AE18XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */ = { XX35D62F4FBF2C9086E8C9XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
XX710DB962925BCE8D59ACXX /* Debug */, XX42CD38636C47E26CBE73XX /* Debug */,
XX1230635DC14DD95E28A2XX /* Release */, XX0A0FB078256184BF7EA2XX /* Release */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
+16 -18
View File
@@ -1,10 +1,10 @@
import Expo internal import Expo
import FirebaseCore import FirebaseCore
import React import React
import ReactAppDependencyProvider import ReactAppDependencyProvider
@UIApplicationMain @main
public class AppDelegate: ExpoAppDelegate { class AppDelegate: ExpoAppDelegate {
var window: UIWindow? var window: UIWindow?
var reactNativeDelegate: ExpoReactNativeFactoryDelegate? var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
@@ -20,11 +20,12 @@ public class AppDelegate: ExpoAppDelegate {
reactNativeDelegate = delegate reactNativeDelegate = delegate
reactNativeFactory = factory reactNativeFactory = factory
bindReactNativeFactory(factory)
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
window = UIWindow(frame: UIScreen.main.bounds) 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( factory.startReactNative(
withModuleName: "main", withModuleName: "main",
in: window, in: window,
@@ -34,42 +35,39 @@ public class AppDelegate: ExpoAppDelegate {
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }
// Linking API
public override func application( public override func application(
_ app: UIApplication, _ app: UIApplication,
open url: URL, open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:] options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool { ) -> Bool {
super.application(app, open: url, options: options) return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|| RCTLinkingManager.application(app, open: url, options: options)
} }
// Universal Links
public override func application( public override func application(
_ application: UIApplication, _ application: UIApplication,
continue userActivity: NSUserActivity, continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool { ) -> Bool {
let result = RCTLinkingManager.application( let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
application, return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
continue: userActivity,
restorationHandler: restorationHandler)
return super.application(
application,
continue: userActivity,
restorationHandler: restorationHandler) || result
} }
} }
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate { class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
// Extension point for config-plugins
override func sourceURL(for bridge: RCTBridge) -> URL? { override func sourceURL(for bridge: RCTBridge) -> URL? {
// needed to return the correct URL for expo-dev-client.
bridge.bundleURL ?? bundleURL() bridge.bundleURL ?? bundleURL()
} }
override func bundleURL() -> URL? { override func bundleURL() -> URL? {
#if DEBUG #if DEBUG
RCTBundleURLProvider.sharedSettings() return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
.jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
#else #else
Bundle.main.url(forResource: "main", withExtension: "jsbundle") return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif #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"?> <?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"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleAllowMixedLocalizations</key> <key>CFBundleAllowMixedLocalizations</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Sauci</string> <string>Sauci</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string> <string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0.7</string> <string>1.0.7</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict> <dict>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>app.sauci</string> <string>app.sauci</string>
<string>com.sauci.app</string> <string>com.sauci.app</string>
</array> </array>
</dict> </dict>
<dict> <dict>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>exp+sauci</string> <string>exp+sauci</string>
</array> </array>
</dict> </dict>
<dict> <dict>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237</string> <string>com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237</string>
</array> </array>
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>46</string> <string>46</string>
<key>GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED</key> <key>GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED</key>
<false/> <false/>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSApplicationQueriesSchemes</key> <key>LSApplicationQueriesSchemes</key>
<array> <array>
<string>instagram</string> <string>instagram</string>
<string>instagram-stories</string> <string>instagram-stories</string>
</array> </array>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>12.0</string> <string>12.0</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
<dict> <dict>
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
<false/> <false/>
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSCameraUsageDescription</key> <key>NSBonjourServices</key>
<string>Sauci uses your camera to take photos and videos that you can share privately with your partner.</string> <array>
<key>NSFaceIDUsageDescription</key> <string>_expo._tcp</string>
<string>Sauci uses Face ID to unlock the app and keep your conversations private.</string> </array>
<key>NSMicrophoneUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>Sauci uses your microphone to record audio when you capture videos to share privately with your partner.</string> <string>Sauci uses your camera to take photos and videos that you can share privately with your partner.</string>
<key>NSPhotoLibraryAddUsageDescription</key> <key>NSFaceIDUsageDescription</key>
<string>Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.</string> <string>Sauci uses Face ID to unlock the app and keep your conversations private.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSLocalNetworkUsageDescription</key>
<string>Sauci uses your photo library so you can select photos and videos to share privately with your partner.</string> <string>Expo Dev Launcher uses the local network to discover and connect to development servers running on your computer.</string>
<key>NSUserActivityTypes</key> <key>NSMicrophoneUsageDescription</key>
<array> <string>Sauci uses your microphone to record audio when you capture videos to share privately with your partner.</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <key>NSPhotoLibraryAddUsageDescription</key>
</array> <string>Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.</string>
<key>RCTNewArchEnabled</key> <key>NSPhotoLibraryUsageDescription</key>
<true/> <string>Sauci uses your photo library so you can select photos and videos to share privately with your partner.</string>
<key>UIBackgroundModes</key> <key>NSUserActivityTypes</key>
<array> <array>
<string>remote-notification</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array> </array>
<key>UILaunchStoryboardName</key> <key>RCTMetroPort</key>
<string>SplashScreen</string> <string>$(RCT_METRO_PORT)</string>
<key>UIRequiredDeviceCapabilities</key> <key>RCTNewArchEnabled</key>
<array> <true/>
<string>arm64</string> <key>UIBackgroundModes</key>
</array> <array>
<key>UIRequiresFullScreen</key> <string>remote-notification</string>
<false/> </array>
<key>UIStatusBarStyle</key> <key>UILaunchStoryboardName</key>
<string>UIStatusBarStyleDefault</string> <string>SplashScreen</string>
<key>UISupportedInterfaceOrientations</key> <key>UIRequiredDeviceCapabilities</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>arm64</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> </array>
</array> <key>UIRequiresFullScreen</key>
<key>UISupportedInterfaceOrientations~ipad</key> <false/>
<array> <key>UIStatusBarStyle</key>
<string>UIInterfaceOrientationPortrait</string> <string>UIStatusBarStyleDefault</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <key>UISupportedInterfaceOrientations</key>
<string>UIInterfaceOrientationLandscapeLeft</string> <array>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationPortrait</string>
</array> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<key>UIUserInterfaceStyle</key> </array>
<string>Automatic</string> <key>UIUserInterfaceStyle</key>
<key>UIViewControllerBasedStatusBarAppearance</key> <string>Automatic</string>
<false/> <key>UIViewControllerBasedStatusBarAppearance</key>
</dict> <false/>
</plist> </dict>
</plist>
+170 -9
View File
@@ -24,6 +24,14 @@
<string>C56D.1</string> <string>C56D.1</string>
</array> </array>
</dict> </dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict> <dict>
<key>NSPrivacyAccessedAPIType</key> <key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string> <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
@@ -33,18 +41,171 @@
<string>85F4.1</string> <string>85F4.1</string>
</array> </array>
</dict> </dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array> </array>
<key>NSPrivacyCollectedDataTypes</key> <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> <key>NSPrivacyTracking</key>
<false/> <false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict> </dict>
</plist> </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> <string>group.com.sauci.app</string>
</array> </array>
</dict> </dict>
</plist> </plist>
+10 -8
View File
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <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="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" 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"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
@@ -17,15 +18,13 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView id="EXPO-SplashScreen" userLabel="SplashScreenLogo" image="SplashScreenLogo" contentMode="scaleAspectFit" clipsSubviews="true" userInteractionEnabled="false" translatesAutoresizingMaskIntoConstraints="false"> <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> </imageView>
</subviews> </subviews>
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/> <viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
<constraints> <constraints>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="83fcb9b545b870ba44c24f0feeb116490c499c52"/> <constraint firstItem="EXPO-SplashScreen" firstAttribute="centerX" secondItem="EXPO-ContainerView" secondAttribute="centerX" id="cad2ab56f97c5429bf29decf850647a4216861d4"/>
<constraint firstItem="EXPO-SplashScreen" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="61d16215e44b98e39d0a2c74fdbfaaa22601b12c"/> <constraint firstItem="EXPO-SplashScreen" firstAttribute="centerY" secondItem="EXPO-ContainerView" secondAttribute="centerY" id="1a145271b085b6ce89b1405a310f5b1bb7656595"/>
<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"/>
</constraints> </constraints>
<color key="backgroundColor" name="SplashScreenBackground"/> <color key="backgroundColor" name="SplashScreenBackground"/>
</view> </view>
@@ -36,7 +35,10 @@
</scene> </scene>
</scenes> </scenes>
<resources> <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"> <namedColor name="SplashScreenBackground">
<color alpha="1.000" blue="0.180392156862745" green="0.101960784313725" red="0.101960784313725" customColorSpace="sRGB" colorSpace="custom"/> <color alpha="1.000" blue="0.180392156862745" green="0.101960784313725" red="0.101960784313725" customColorSpace="sRGB" colorSpace="custom"/>
</namedColor> </namedColor>
@@ -31,6 +31,8 @@ Ug==
<key>alg</key> <key>alg</key>
<string>rsa-v1_5-sha256</string> <string>rsa-v1_5-sha256</string>
</dict> </dict>
<key>EXUpdatesEnableBsdiffPatchSupport</key>
<true/>
<key>EXUpdatesEnabled</key> <key>EXUpdatesEnabled</key>
<true/> <true/>
<key>EXUpdatesLaunchWaitMs</key> <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: { moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', '^@/(.*)$': '<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: [ testMatch: [
'<rootDir>/src/**/__tests__/**/*.(spec|test).(ts|tsx)', '<rootDir>/src/**/__tests__/**/*.(spec|test).(ts|tsx)',
+47 -44
View File
@@ -22,6 +22,8 @@
"release:android:ship": "bundle exec fastlane android release", "release:android:ship": "bundle exec fastlane android release",
"release:ios:upload": "bundle exec fastlane ios upload", "release:ios:upload": "bundle exec fastlane ios upload",
"release:ios:ship": "bundle exec fastlane ios release", "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:staging": "node scripts/ota-publish.mjs staging --preflight",
"ota:preflight:production": "node scripts/ota-publish.mjs production --preflight", "ota:preflight:production": "node scripts/ota-publish.mjs production --preflight",
"ota:publish:staging": "node scripts/ota-publish.mjs staging", "ota:publish:staging": "node scripts/ota-publish.mjs staging",
@@ -32,7 +34,7 @@
"ota:doctor:production": "node scripts/ota-publish.mjs production --doctor" "ota:doctor:production": "node scripts/ota-publish.mjs production --doctor"
}, },
"dependencies": { "dependencies": {
"@bacons/apple-targets": "^0.1.6", "@bacons/apple-targets": "^5.0.0",
"@bittingz/expo-widgets": "^3.0.2", "@bittingz/expo-widgets": "^3.0.2",
"@expo/ngrok": "^4.1.0", "@expo/ngrok": "^4.1.0",
"@expo/vector-icons": "^15.0.3", "@expo/vector-icons": "^15.0.3",
@@ -42,54 +44,54 @@
"@react-native-firebase/app": "^23.8.2", "@react-native-firebase/app": "^23.8.2",
"@react-native-firebase/crashlytics": "^23.8.4", "@react-native-firebase/crashlytics": "^23.8.4",
"@react-native-google-signin/google-signin": "^16.1.1", "@react-native-google-signin/google-signin": "^16.1.1",
"@react-navigation/bottom-tabs": "^7.0.0", "@shopify/react-native-skia": "2.6.2",
"@react-navigation/native": "^7.0.0",
"@react-navigation/native-stack": "^7.0.0",
"@shopify/react-native-skia": "2.2.12",
"@supabase/supabase-js": "^2.39.0", "@supabase/supabase-js": "^2.39.0",
"@tanstack/react-query": "^5.17.0", "@tanstack/react-query": "^5.17.0",
"babel-preset-expo": "~54.0.12", "babel-preset-expo": "~56.0.0",
"base64-arraybuffer": "^1.0.2", "base64-arraybuffer": "^1.0.2",
"expo": "~54.0.0", "expo": "^56.0.0",
"expo-apple-authentication": "~8.0.8", "expo-apple-authentication": "~56.0.4",
"expo-asset": "~12.0.13", "expo-asset": "~56.0.24",
"expo-av": "~16.0.8", "expo-av": "~16.0.8",
"expo-blur": "~15.0.8", "expo-blur": "~56.0.4",
"expo-build-properties": "~1.0.10", "expo-build-properties": "~56.0.27",
"expo-clipboard": "~8.0.8", "expo-clipboard": "~56.0.4",
"expo-constants": "~18.0.14", "expo-constants": "~56.0.25",
"expo-dev-client": "~6.0.21", "expo-dev-client": "~56.0.26",
"expo-file-system": "~19.0.24", "expo-file-system": "~56.0.11",
"expo-font": "~14.0.12", "expo-font": "~56.0.7",
"expo-haptics": "~15.0.8", "expo-haptics": "~56.0.3",
"expo-image": "~3.0.11", "expo-image": "~56.0.12",
"expo-image-picker": "~17.0.11", "expo-image-picker": "~56.0.25",
"expo-linear-gradient": "~15.0.8", "expo-linear-gradient": "~56.0.4",
"expo-linking": "~8.0.12", "expo-linking": "~56.0.17",
"expo-local-authentication": "~17.0.9", "expo-local-authentication": "~56.0.5",
"expo-media-library": "~18.2.1", "expo-media-library": "~56.0.11",
"expo-notifications": "~0.32.17", "expo-notifications": "~56.0.25",
"expo-router": "~6.0.24", "expo-observe": "~56.0.29",
"expo-secure-store": "~15.0.8", "expo-router": "~56.2.20",
"expo-sharing": "~14.0.8", "expo-secure-store": "~56.0.4",
"expo-status-bar": "~3.0.9", "expo-sharing": "~56.0.26",
"expo-updates": "~29.0.20", "expo-splash-screen": "~56.0.15",
"react": "19.1.0", "expo-status-bar": "~56.0.4",
"react-dom": "19.1.0", "expo-system-ui": "~56.0.5",
"react-native": "0.81.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-android-widget": "^0.22.1",
"react-native-compressor": "^1.13.0", "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-purchases": "^9.6.12",
"react-native-reanimated": "~4.1.1", "react-native-reanimated": "4.3.1",
"react-native-safe-area-context": "~5.6.0", "react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.16.0", "react-native-screens": "~4.26.0",
"react-native-share": "^12.3.1", "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-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-web": "~0.21.0",
"react-native-worklets": "0.5.1", "react-native-worklets": "0.8.3",
"zustand": "^4.4.7" "zustand": "^4.4.7"
}, },
"devDependencies": { "devDependencies": {
@@ -97,15 +99,16 @@
"@testing-library/jest-native": "^5.4.3", "@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^13.3.3", "@testing-library/react-native": "^13.3.3",
"@types/jest": "29.5.14", "@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", "@types/react-test-renderer": "^19.1.0",
"dotenv-cli": "^11.0.0", "dotenv-cli": "^11.0.0",
"eoas": "^3.1.3", "eoas": "^3.1.3",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-expo": "~10.0.0", "eslint-config-expo": "~56.0.4",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-expo": "~54.0.18", "jest-expo": "~56.0.5",
"react-test-renderer": "19.1.0", "react-test-renderer": "19.2.3",
"typescript": "~5.9.2" "typescript": "~6.0.3"
} }
} }
+6 -1
View File
@@ -3,6 +3,7 @@
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'; import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os'; import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path'; import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
const projectDir = resolve(import.meta.dirname, '..'); 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); 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, color: colors.textTertiary,
}, },
lockOverlay: { lockOverlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
+1 -1
View File
@@ -307,7 +307,7 @@ export const MatchConfetti: React.FC<Props> = ({ visible, onAnimationComplete })
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
zIndex: 1000, zIndex: 1000,
}, },
particle: { particle: {
@@ -344,7 +344,7 @@ export const MatchNotificationModal: React.FC<Props> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.75)', backgroundColor: 'rgba(0, 0, 0, 0.75)',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
@@ -377,7 +377,7 @@ const styles = StyleSheet.create({
borderColor: colors.border, borderColor: colors.border,
}, },
flatBackground: { flatBackground: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: colors.backgroundLight, backgroundColor: colors.backgroundLight,
}, },
// Removed blur/gradient styles // Removed blur/gradient styles
+1 -1
View File
@@ -269,7 +269,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end", justifyContent: "flex-end",
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.85)", backgroundColor: "rgba(0, 0, 0, 0.85)",
}, },
content: { content: {
@@ -101,7 +101,7 @@ const styles = StyleSheet.create({
position: 'relative', position: 'relative',
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)', backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg, borderRadius: radius.lg,
pointerEvents: 'none', pointerEvents: 'none',
@@ -115,7 +115,7 @@ const styles = StyleSheet.create({
fontWeight: '700', fontWeight: '700',
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)', backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg, borderRadius: radius.lg,
pointerEvents: 'none', pointerEvents: 'none',
@@ -110,7 +110,7 @@ const styles = StyleSheet.create({
marginTop: 2, marginTop: 2,
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)', backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg, borderRadius: radius.lg,
pointerEvents: 'none', pointerEvents: 'none',
@@ -90,7 +90,7 @@ const styles = StyleSheet.create({
marginTop: 2, marginTop: 2,
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.05)', backgroundColor: 'rgba(0, 0, 0, 0.05)',
borderRadius: radius.lg, borderRadius: radius.lg,
pointerEvents: 'none', pointerEvents: 'none',
@@ -336,7 +336,7 @@ const styles = StyleSheet.create({
zIndex: 1000, zIndex: 1000,
}, },
backdrop: { backdrop: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.8)', backgroundColor: 'rgba(0, 0, 0, 0.8)',
}, },
keyboardView: { keyboardView: {
@@ -615,7 +615,7 @@ const styles = StyleSheet.create({
borderColor: colors.premium.goldLight, borderColor: colors.premium.goldLight,
}, },
annualBackground: { annualBackground: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: colors.premium.goldLight, backgroundColor: colors.premium.goldLight,
opacity: 0.3, opacity: 0.3,
}, },
@@ -804,7 +804,7 @@ const styles = StyleSheet.create({
// Guest confirm modal // Guest confirm modal
guestConfirmOverlay: { guestConfirmOverlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.8)", backgroundColor: "rgba(0, 0, 0, 0.8)",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
@@ -131,7 +131,7 @@ const styles = StyleSheet.create({
padding: spacing.lg, padding: spacing.lg,
}, },
backdrop: { backdrop: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.7)", backgroundColor: "rgba(0, 0, 0, 0.7)",
}, },
modalContainer: { modalContainer: {
@@ -629,7 +629,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end", justifyContent: "flex-end",
}, },
backdrop: { backdrop: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.6)", backgroundColor: "rgba(0, 0, 0, 0.6)",
}, },
sheetContainer: { sheetContainer: {
@@ -86,7 +86,7 @@ const styles = StyleSheet.create({
padding: spacing.lg, padding: spacing.lg,
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.1)", backgroundColor: "rgba(0, 0, 0, 0.1)",
}, },
header: { header: {
@@ -19,7 +19,7 @@ import Animated, {
} from "react-native-reanimated"; } from "react-native-reanimated";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { useSafeAreaInsets } from "react-native-safe-area-context"; 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 Sharing from "expo-sharing";
import { colors, typography, spacing, blur } from "../../theme"; import { colors, typography, spacing, blur } from "../../theme";
import { shareToInstagramStories } from "../../lib/instagramShare"; import { shareToInstagramStories } from "../../lib/instagramShare";
@@ -50,7 +50,7 @@ export function SharePreviewModal({
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH); 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 [capturedUri, setCapturedUri] = useState<string | null>(null);
const [isCapturing, setIsCapturing] = useState(false); const [isCapturing, setIsCapturing] = useState(false);
@@ -308,7 +308,7 @@ export default function MatchesTutorial({ onComplete }: Props) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
zIndex: 1000, zIndex: 1000,
}, },
blur: { blur: {
@@ -152,7 +152,7 @@ const styles = StyleSheet.create({
padding: spacing.md, padding: spacing.md,
}, },
fullScreenContainer: { fullScreenContainer: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(13, 13, 26, 0.85)', backgroundColor: 'rgba(13, 13, 26, 0.85)',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
@@ -118,7 +118,7 @@ const styles = StyleSheet.create({
overflow: 'hidden', overflow: 'hidden',
}, },
glowContainer: { glowContainer: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
}, },
edgeGlowTop: { edgeGlowTop: {
position: 'absolute', position: 'absolute',
+1 -2
View File
@@ -1,7 +1,6 @@
import { useState, useEffect, useCallback, useRef } from "react"; import { useState, useEffect, useCallback, useRef } from "react";
import { View, StyleSheet, KeyboardAvoidingView, Platform, Alert } from "react-native"; import { View, StyleSheet, KeyboardAvoidingView, Platform, Alert } from "react-native";
import { useLocalSearchParams, useRouter } from "expo-router"; import { useLocalSearchParams, useRouter, useFocusEffect, useIsFocused } from "expo-router";
import { useFocusEffect, useIsFocused } from "@react-navigation/native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import { LinearGradient } from "expo-linear-gradient"; import { LinearGradient } from "expo-linear-gradient";
import Animated, { FadeInDown } from "react-native-reanimated"; import Animated, { FadeInDown } from "react-native-reanimated";
@@ -159,7 +159,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end", justifyContent: "flex-end",
}, },
backdrop: { backdrop: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.6)", backgroundColor: "rgba(0, 0, 0, 0.6)",
}, },
sheetContainer: { sheetContainer: {
@@ -203,13 +203,13 @@ const styles = StyleSheet.create({
marginTop: spacing.xs, marginTop: spacing.xs,
}, },
loadingOverlay: { loadingOverlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.3)', backgroundColor: 'rgba(0, 0, 0, 0.3)',
}, },
playOverlay: { playOverlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}, },
@@ -220,7 +220,7 @@ const styles = StyleSheet.create({
}, },
// ... // ...
inputFieldBorder: { inputFieldBorder: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
borderRadius: radius.xl, borderRadius: radius.xl,
borderWidth: 1, borderWidth: 1,
borderColor: colors.border, borderColor: colors.border,
@@ -790,7 +790,7 @@ const styles = StyleSheet.create({
height: '100%', height: '100%',
}, },
photoLoadingOverlay: { photoLoadingOverlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.3)', backgroundColor: 'rgba(0, 0, 0, 0.3)',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
@@ -179,7 +179,7 @@ const styles = StyleSheet.create({
justifyContent: "flex-end", justifyContent: "flex-end",
}, },
backdrop: { backdrop: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.6)", backgroundColor: "rgba(0, 0, 0, 0.6)",
}, },
sheetContainer: { sheetContainer: {
@@ -125,7 +125,7 @@ const styles = StyleSheet.create({
backgroundColor: 'rgba(0, 0, 0, 0.2)', backgroundColor: 'rgba(0, 0, 0, 0.2)',
}, },
thumbnail: { thumbnail: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
}, },
iconContainer: { iconContainer: {
width: 64, width: 64,
@@ -136,7 +136,7 @@ const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, View } from "react-native"; import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, View } from "react-native";
import { Ionicons } from "@expo/vector-icons"; 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 Animated, { FadeIn } from "react-native-reanimated";
import { GradientBackground } from "../../components/ui"; import { GradientBackground } from "../../components/ui";
import { Paywall } from "../../components/paywall"; import { Paywall } from "../../components/paywall";
@@ -7,8 +7,7 @@ import {
Alert, Alert,
useWindowDimensions, useWindowDimensions,
} from 'react-native'; } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router'; import { useLocalSearchParams, useRouter, useIsFocused } from 'expo-router';
import { useIsFocused } from '@react-navigation/native';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
@@ -21,7 +21,7 @@ import Animated, {
} from 'react-native-reanimated'; } from 'react-native-reanimated';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; 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 Sharing from 'expo-sharing';
import * as FileSystem from 'expo-file-system/legacy'; import * as FileSystem from 'expo-file-system/legacy';
import { colors, gradients, radius, typography, spacing, blur } from '../../../theme'; import { colors, gradients, radius, typography, spacing, blur } from '../../../theme';
@@ -44,7 +44,7 @@ export function DrawingShareModal({
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH); 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 [capturedUri, setCapturedUri] = useState<string | null>(null);
const [isCapturing, setIsCapturing] = useState(false); const [isCapturing, setIsCapturing] = useState(false);
@@ -239,7 +239,7 @@ const styles = StyleSheet.create({
overflow: 'hidden', overflow: 'hidden',
}, },
proBorderGradient: { proBorderGradient: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
opacity: 0.3, opacity: 0.3,
}, },
proCardInner: { proCardInner: {
@@ -330,7 +330,7 @@ const styles = StyleSheet.create({
overflow: 'hidden', overflow: 'hidden',
}, },
freeBorderGradient: { freeBorderGradient: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
}, },
freeHeader: { freeHeader: {
@@ -51,7 +51,7 @@ const styles = StyleSheet.create({
justifyContent: "space-between", justifyContent: "space-between",
}, },
overlay: { overlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
backgroundColor: "rgba(0, 0, 0, 0.08)", backgroundColor: "rgba(0, 0, 0, 0.08)",
}, },
content: { content: {
@@ -13,7 +13,7 @@ import { BlurView } from "expo-blur";
import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown } from "react-native-reanimated"; import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown } from "react-native-reanimated";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { useSafeAreaInsets } from "react-native-safe-area-context"; 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 Sharing from "expo-sharing";
import { colors, spacing, typography, blur } from "../../../theme"; import { colors, spacing, typography, blur } from "../../../theme";
import { shareToInstagramStories } from "../../../lib/instagramShare"; import { shareToInstagramStories } from "../../../lib/instagramShare";
@@ -32,7 +32,7 @@ export function QuizShareModal({ visible, onClose, scorePercent }: QuizShareModa
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH); 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 [capturedUri, setCapturedUri] = useState<string | null>(null);
const [isCapturing, setIsCapturing] = useState(false); const [isCapturing, setIsCapturing] = useState(false);
@@ -225,7 +225,7 @@ const styles = StyleSheet.create({
overflow: 'hidden', overflow: 'hidden',
}, },
premiumButtonGradient: { premiumButtonGradient: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFill,
}, },
premiumButtonContent: { premiumButtonContent: {
flexDirection: 'row', 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', () => { jest.mock('react-native-reanimated', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const Reanimated = require('react-native-reanimated/mock'); 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', () => ({ jest.mock('expo-file-system/legacy', () => ({
readAsStringAsync: jest.fn(async () => ''), readAsStringAsync: jest.fn(async () => ''),
EncodingType: { 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, "declarationMap": true,
"noEmit": true, "noEmit": true,
"jsx": "react-native", "jsx": "react-native",
"types": [
"node",
"jest"
],
"paths": { "paths": {
"@/*": [ "@/*": [
"./src/*" "./src/*"
+8 -1
View File
@@ -153,10 +153,17 @@ regenerate and review the native diff separately:
```bash ```bash
cd apps/mobile 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 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. A build produced before this native source change will not check for updates.
## Publishing an update ## Publishing an update
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff. Show more