Merge remote-tracking branch 'origin/staging' into staging
# Conflicts: # .codex/skills/release-mobile/SKILL.md # apps/mobile/package.json # docs/releasing.md
No files matched your search
@@ -3,8 +3,8 @@ id: 2026-08-30-ios-extension-version-verification
|
||||
date: 2026-08-30
|
||||
status: promoted
|
||||
scope: iOS release artifact verification
|
||||
evidence: build 45 Xcode archive warning plus direct PlistBuddy inspection of the exported IPA
|
||||
destination: .codex/skills/release-mobile/SKILL.md
|
||||
evidence: v1.0.6 exported IPA inspection and reproduced v1.0.7 Xcode build-setting mismatch
|
||||
destination: apps/mobile/scripts/release-preflight.mjs and .codex/skills/release-mobile/SKILL.md
|
||||
review_after: 2027-02-28
|
||||
---
|
||||
|
||||
@@ -21,19 +21,23 @@ and every packaged extension `Info.plist` with `PlistBuddy`.
|
||||
|
||||
## Cause
|
||||
|
||||
The archive warning described an intermediate state and was not authoritative
|
||||
for the exported package. Direct IPA inspection showed both the app and
|
||||
`LiveDrawWidget.appex` at version 1.0.6 build 45.
|
||||
The v1.0.6 archive warning described an intermediate state and was not
|
||||
authoritative for that exported package. The later v1.0.7 reproduction found a
|
||||
separate real hazard: `CURRENT_PROJECT_VERSION` in both LiveDrawWidget
|
||||
configurations can override the widget plist and package an older build number.
|
||||
|
||||
## Remediation
|
||||
|
||||
For every iOS release, inspect version and build values in the exported app and
|
||||
all packaged extensions. Reject or rebuild only when the final IPA values differ;
|
||||
do not accept or reject a release from archive warnings alone.
|
||||
Before every iOS release, require public-version parity across app config, both
|
||||
plists, and Xcode marketing settings. Require the widget Debug and Release
|
||||
`CURRENT_PROJECT_VERSION` values to equal both plists. Then inspect the exported
|
||||
app and all packaged extensions. Reject or rebuild only when the final IPA values
|
||||
differ; do not accept or reject a release from archive warnings alone.
|
||||
|
||||
## Evidence
|
||||
|
||||
`PlistBuddy` read `CFBundleShortVersionString` 1.0.6 and `CFBundleVersion` 45 from
|
||||
both packaged plists. `codesign --verify --deep --strict` passed for the app, and
|
||||
the verified IPA SHA-256 was
|
||||
`9d4779bddac4647911a3a38e4ee37cf794c2b8691e0746a041b629c62b308e7f`.
|
||||
both v1.0.6 packaged plists. The v1.0.7 build later proved a stale widget Xcode
|
||||
setting can survive plist-only review. `release-preflight.mjs` and its node test
|
||||
now reject that mismatch before EAS allocation; exported IPA inspection remains
|
||||
the final authority.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: 2026-08-31-mobile-release-preflight-and-attempts
|
||||
date: 2026-08-31
|
||||
status: promoted
|
||||
scope: mobile production release preparation and local EAS builds
|
||||
evidence: v1.0.7 Android and iOS local EAS release investigation and verified artifacts
|
||||
destination: apps/mobile/scripts/release-preflight.mjs, apps/mobile/scripts/release-preflight.test.mjs, docs/releasing.md, and release skills
|
||||
review_after: 2027-02-28
|
||||
---
|
||||
|
||||
## Symptom
|
||||
|
||||
The v1.0.7 release spent repeated attempts on archive, plugin startup, package
|
||||
installation, disk-space, signing, and iOS extension-version failures before
|
||||
valid Android and iOS artifacts were accepted.
|
||||
|
||||
## Reproduction
|
||||
|
||||
Run a local production EAS build with an ignored public OTA certificate, a
|
||||
package-directory plugin path, insufficient disk space, or divergent widget
|
||||
`CURRENT_PROJECT_VERSION`. Each condition fails or produces an invalid input
|
||||
before a valid final artifact can be accepted.
|
||||
|
||||
## Cause
|
||||
|
||||
Pre-compilation setup failures were treated as rebuild failures. Source parity,
|
||||
archive inclusion, local EAS plugin shape, disk capacity, and effective iOS widget
|
||||
build settings were not checked together before EAS allocation.
|
||||
|
||||
## Remediation
|
||||
|
||||
Run the deterministic mobile release preflight before every production build.
|
||||
Classify attempts before retrying: archive, plugin, npm, and ENOSPC failures do
|
||||
not consume a native build; signing, profile, and capability failures require
|
||||
provider read-back before one replacement build. Retain work directories and logs
|
||||
at stable ignored paths. Accept only independently inspected artifacts and keep
|
||||
rejected artifacts out of the upload path.
|
||||
|
||||
The Android native input failure was a Gradle task ordering issue: Prefab inputs
|
||||
must be available before dependent native tasks. Preserve the explicit ordering
|
||||
fix rather than removing Firebase or changing dependency versions without proof.
|
||||
|
||||
## Evidence
|
||||
|
||||
The preflight node tests reject an ignored OTA certificate and an effective widget
|
||||
build-setting mismatch. The checked-in source invariants pass the preflight. The
|
||||
maintained release guide and skills record local EAS path requirements, provider
|
||||
read-back, phase-specific status language, artifact inspection, store read-back,
|
||||
and scoped upload authorization boundaries.
|
||||
@@ -43,6 +43,8 @@ Require all of the following:
|
||||
- consistent public and OTA runtime versions across `app.json`, explicit
|
||||
`app.config.js` runtime, Android, and every iOS target
|
||||
(`scripts/set_version.mjs --check`);
|
||||
- `npm run release:preflight -w @sauci/mobile`, which also checks the iOS widget
|
||||
effective build settings and OTA certificate archive inclusion;
|
||||
- Node 20, JDK 17 for Android, and Xcode/CocoaPods for iOS;
|
||||
- the configured production API/Auth/RevenueCat environment, without printing
|
||||
secret values.
|
||||
@@ -84,6 +86,15 @@ Build from the clean prepared source using the package scripts in
|
||||
versioned artifact directory. When building both platforms, build Android first,
|
||||
then iOS.
|
||||
|
||||
For local EAS, retain the working directory and logs under a stable ignored path,
|
||||
set `EAS_LOCAL_BUILD_ARTIFACTS_DIR`, and preflight available disk space before
|
||||
starting. Set `EAS_LOCAL_BUILD_PLUGIN_PATH` only to the matching plugin
|
||||
executable `bin/run`, never a package directory or a hardcoded cache location.
|
||||
Classify plugin, archive, npm, and ENOSPC failures as pre-compilation failures;
|
||||
repair and preflight them without spending another build. Make at most one
|
||||
meaningful build for each newly proven input state. After a signing, profile, or
|
||||
capability failure, read back and prove the provider correction before rebuilding.
|
||||
|
||||
After each build:
|
||||
|
||||
1. Inspect the worktree before doing anything else. Accept only the documented
|
||||
@@ -92,15 +103,19 @@ After each build:
|
||||
dependency, or native-project regeneration as a failed build.
|
||||
2. Independently inspect the binary for package/bundle identifier, public
|
||||
version, platform build number, production endpoint configuration where
|
||||
inspectable, and signing identity/fingerprint. Require the embedded public
|
||||
version to equal the requested version.
|
||||
inspectable, signing identity/fingerprint, OTA runtime/channel/certificate,
|
||||
and SHA-256. Require the embedded public version to equal the requested
|
||||
version. For Android, also inspect all required ABIs, Worklets/Reanimated/Skia
|
||||
libraries, and the release permissions. For iOS, inspect every packaged
|
||||
extension after export.
|
||||
3. Record the verified platform build number in the new release-note entry.
|
||||
4. Commit only the expected metadata and release-note update as
|
||||
`chore(<platform>): record v<version> build <number>`. The worktree must be
|
||||
clean before starting the next platform build.
|
||||
|
||||
Do not accept a successful EAS exit code as proof of a valid artifact. If either
|
||||
requested platform fails, do not tag a partial release.
|
||||
Do not accept a successful EAS exit code as proof of a valid artifact. Quarantine
|
||||
or remove rejected artifacts so only accepted paths can be offered for upload. If
|
||||
either requested platform fails, do not tag a partial release.
|
||||
|
||||
## Tag and prove the result
|
||||
|
||||
|
||||
@@ -52,6 +52,25 @@ verified EAS version metadata. Sauci tracks its native iOS and Android projects,
|
||||
so routine release builds must package those checked-in sources. Do not run
|
||||
`expo prebuild` first; native regeneration is a separate reviewed change.
|
||||
|
||||
Run `npm run release:preflight -w @sauci/mobile` before allocating an EAS build.
|
||||
It rejects inconsistent public/runtime versions, an iOS widget build-setting
|
||||
override, and a public OTA certificate that would be omitted from the archive.
|
||||
For a local EAS build, also run `npm run release:preflight:local-eas -w
|
||||
@sauci/mobile` after setting `EAS_LOCAL_BUILD_PLUGIN_PATH` to the matching
|
||||
plugin's executable `bin/run`. Do not point it at the package directory or
|
||||
hardcode a machine-local cache path.
|
||||
|
||||
Classify a failure before rebuilding. Archive, plugin-spawn, package-install, and
|
||||
disk-space failures happen before native compilation and must be repaired and
|
||||
preflighted without consuming another build. After a signing, profile, or
|
||||
capability failure, read back the provider state and prove only the affected
|
||||
input changed before rebuilding. Retain the EAS work directory, command log, and
|
||||
stable artifact path for every meaningful attempt. Never retry an ambiguous
|
||||
upload.
|
||||
|
||||
Report provider progress by phase: packaging, native compilation, submission
|
||||
queue, or store processing. Do not describe every failure as a rebuild.
|
||||
|
||||
Use the package scripts in `apps/mobile/package.json` to build artifacts and the
|
||||
Fastlane lanes in `apps/mobile/fastlane/Fastfile` to upload, submit, and roll
|
||||
out.
|
||||
@@ -83,6 +102,11 @@ submission, and rollout without requesting further confirmation.
|
||||
when used, pass explicit `--platform`, `--profile production`, `--path`, and
|
||||
`--wait` — never `--latest`.
|
||||
|
||||
App Store Connect API group association does not establish the automatic-
|
||||
distribution switch. Verify that switch in an authenticated App Store Connect UI
|
||||
session before upload; if that UI verification is unavailable, stop rather
|
||||
than inferring the distribution effect from API data.
|
||||
|
||||
If an upload or submission command returns an ambiguous result, inspect EAS and
|
||||
the store before doing anything else; do not retry blind. After submission, read
|
||||
back the store state (Play Console track status / App Store Connect version
|
||||
@@ -97,3 +121,27 @@ warnings as diagnostic evidence, not the final artifact state: decide whether to
|
||||
reject or rebuild only after reading the exported IPA itself. Report the
|
||||
artifact, version/build number, upload and submission results, rollout state,
|
||||
and store read-back state.
|
||||
|
||||
For Android, verify the accepted AAB has all four required ABIs, the Worklets,
|
||||
Reanimated, and Skia native libraries, no broad `READ_MEDIA_IMAGES` or
|
||||
`READ_MEDIA_VIDEO` permissions, and the production OTA runtime, channel, and
|
||||
public certificate. For iOS, verify the exported IPA, not archive warnings, and
|
||||
check both the containing app and every extension.
|
||||
|
||||
For the Android native dependency ordering already tracked in Gradle, preserve
|
||||
the explicit task dependency that makes Prefab inputs available before dependent
|
||||
native tasks. Fix task validation with ordering, not Firebase removal or
|
||||
dependency-version roulette.
|
||||
|
||||
After Android upload, EAS Submit `FINISHED`, the exact internal/DRAFT/no-rollout
|
||||
submit configuration, and the retained artifact digest are the available
|
||||
authoritative read-back when a direct Play track read would require creating an
|
||||
edit. Never create an edit merely to claim read-only verification.
|
||||
|
||||
For a checked-in iOS entitlement, inspect the real Apple App ID capability state
|
||||
before a signing rebuild. The capability sync bug tracked in
|
||||
https://github.com/expo/eas-cli/issues/3986 can report success while Apple
|
||||
rejects a malformed update. Read back Associated Domains, regenerate only the
|
||||
affected profile, and decode that profile to prove the entitlement before
|
||||
rebuilding. Noninteractive managed App Store Connect key auth may require
|
||||
`EXPO_APPLE_TEAM_ID`; preserve certificates and unrelated target profiles.
|
||||
@@ -2,6 +2,21 @@
|
||||
module.exports = {
|
||||
extends: 'expo',
|
||||
ignorePatterns: ['/dist/*'],
|
||||
rules: {
|
||||
// eslint-config-expo (SDK 56) pulls eslint-plugin-react-hooks v7, whose
|
||||
// recommended config adds React Compiler era rules as errors. Existing
|
||||
// patterns this codebase relies on deliberately (the "latest value" ref
|
||||
// pattern, effect-driven setState, dynamically created inline
|
||||
// components, and manual memoization) trip these new rules throughout
|
||||
// the app. Adopting the stricter rules is a deliberate follow-up
|
||||
// refactor, not an SDK upgrade side effect, so they are downgraded to
|
||||
// warnings here.
|
||||
'react-hooks/refs': 'warn',
|
||||
'react-hooks/set-state-in-effect': 'warn',
|
||||
'react-hooks/immutability': 'warn',
|
||||
'react-hooks/preserve-manual-memoization': 'warn',
|
||||
'react-hooks/static-components': 'warn',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['plugins/**/*.js'],
|
||||
|
||||
@@ -12,5 +12,8 @@ local.properties
|
||||
*.hprof
|
||||
.cxx/
|
||||
|
||||
# generated inline modules
|
||||
app/src/main/java/inline/
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
@@ -11,7 +11,7 @@ def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
|
||||
react {
|
||||
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
|
||||
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
|
||||
hermesCommand = new File(["node", "--print", "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/hermesc/%OS-BIN%/hermesc"
|
||||
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
|
||||
enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean()
|
||||
@@ -64,7 +64,7 @@ react {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true in release builds to optimize the app using R8.
|
||||
* Set this to true in release builds to optimize the app using [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization).
|
||||
*/
|
||||
def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBuilds') ?: false).toBoolean()
|
||||
|
||||
@@ -94,6 +94,7 @@ android {
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 49
|
||||
versionName "1.0.7"
|
||||
|
||||
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
||||
}
|
||||
signingConfigs {
|
||||
@@ -181,54 +182,4 @@ dependencies {
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
// Reanimated 4.1.7 imports Worklets from its pre-AGP-8 CMake output path,
|
||||
// while AGP 8.14 writes release outputs below intermediates/cxx/RelWithDebInfo.
|
||||
// Keep the checked-in Android project compatible with the supported SDK 54
|
||||
// dependency set by materializing that one legacy input after Worklets builds.
|
||||
def workletsReleaseAbis = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
|
||||
def syncWorkletsReleaseCmakeInputs = tasks.register("syncWorkletsReleaseCmakeInputs") {
|
||||
dependsOn(":react-native-worklets:externalNativeBuildRelease")
|
||||
|
||||
doLast {
|
||||
def workletsIntermediates = file("${rootDir}/../../../node_modules/react-native-worklets/android/build/intermediates")
|
||||
workletsReleaseAbis.each { abi ->
|
||||
def libraries = fileTree(workletsIntermediates) {
|
||||
include "cxx/RelWithDebInfo/**/obj/${abi}/libworklets.so"
|
||||
}.files
|
||||
if (libraries.size() != 1) {
|
||||
throw new GradleException("Expected exactly one Worklets release library for ${abi}, found ${libraries.size()}")
|
||||
}
|
||||
def source = libraries.iterator().next()
|
||||
def destination = file("${workletsIntermediates}/cmake/release/obj/${abi}/libworklets.so")
|
||||
destination.parentFile.mkdirs()
|
||||
java.nio.file.Files.deleteIfExists(destination.toPath())
|
||||
source.withInputStream { input ->
|
||||
destination.withOutputStream { output -> output << input }
|
||||
}
|
||||
if (destination.length() != source.length()) {
|
||||
throw new GradleException("Failed to copy Worklets release library for ${abi}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
def reanimatedProject = findProject(":react-native-reanimated")
|
||||
reanimatedProject?.tasks?.configureEach { task ->
|
||||
if (task.name == "externalNativeBuildRelease" || task.name == "prefabReleasePackage") {
|
||||
task.dependsOn(syncWorkletsReleaseCmakeInputs)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.configureEach { task ->
|
||||
if (task.name.startsWith("configureCMakeRelWithDebInfo")) {
|
||||
task.dependsOn(
|
||||
":react-native-worklets:prefabReleasePackage",
|
||||
":react-native-reanimated:prefabReleasePackage",
|
||||
":shopify_react-native-skia:prefabReleasePackage",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic" />
|
||||
</manifest>
|
||||
@@ -1,10 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
@@ -12,7 +12,7 @@
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
|
||||
<uses-permission android:name="com.android.vending.BILLING"/>
|
||||
<queries>
|
||||
<package android:name="com.instagram.android"/>
|
||||
@@ -22,7 +22,7 @@
|
||||
<data android:scheme="https"/>
|
||||
</intent>
|
||||
</queries>
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:requestLegacyExternalStorage="true" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules" android:enableOnBackInvokedCallback="false">
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false" android:requestLegacyExternalStorage="true" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules">
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
||||
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
|
||||
@@ -30,13 +30,14 @@
|
||||
<meta-data android:name="expo.modules.updates.CODE_SIGNING_CERTIFICATE" android:value="-----BEGIN CERTIFICATE-----
MIICzTCCAbWgAwIBAgIJAPrPjxnUOGisMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
BAMTBVNhdWNpMB4XDTI2MDgzMDE4MTAzMFoXDTM2MDgzMDE4MTAzMFowEDEOMAwG
A1UEAxMFU2F1Y2kwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWiUac
8i9bIbNZjv8iunVr6kqlgb9vea98JHz/A83biyszg0+GpmLYsr4zV2F7A6RitiIx
SOML+wVedyD0F2quWccBGXinvMAds0TxgIOjQfP+8+VJ/duDjveHvn+OFW4s7XxW
SY7TK1ufDcPWo/f5XzfqtBveug49zv3/F3MwfmMUbEY/zvH7nayPGJNuSyf3wISn
JIt0BsHNhWykRXCzjaF2fy3nAebOyezbtbapCjUlxPENVtBCzBQ7C9kaQXCBw5mV
hxhJlOxHWXpHJgDLHpev8A/CWQRh67tsevjKi6R6SDlf0R0xSbDJTElx8goWOOQW
VO9aDqI+oH0KpWfnAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E
DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAyUc20bIRCxoBTJg84mPl
2nFDWSaAfzgo0K0b6x2T1suuq4LAOUgzBqJISrQAALd0besK1qUZSAMhjQu1wPZT
MHuWRMxQHfOkyHMYUfOoPMS4bE+4K0WjzfoVt3y1A+oxhcIAQUUGyNgNmebfEN07
JlX0B8UI8aIMjyfpsIDgZx1piHKwittGy/V4MPOLbmT2BBwS9BELcmWE4CwWbfeo
JM4lXY0hk3M/GTstRYaUKiwBWEU+z1PdAmWAgrtxpVnXN+oaToWh0WbVkbF1K6r5
3oBGFTL7kT+Wogg9GK04XoAREw0jZJ/k77oJx68lGVBNiB+Ue2KqX0H/0cf2CslY
Ug==
-----END CERTIFICATE-----
"/>
|
||||
<meta-data android:name="expo.modules.updates.CODE_SIGNING_METADATA" android:value="{"keyid":"main","alg":"rsa-v1_5-sha256"}"/>
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||
<meta-data android:name="expo.modules.updates.ENABLE_BSDIFF_PATCH_SUPPORT" android:value="true"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="5000"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://ota.apps.jbrunton.co.uk/manifest"/>
|
||||
<meta-data android:name="expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY" android:value="{"expo-channel-name":"production","expo-app-id":"9a66f3c2-fda7-416f-87e2-6db833b32e9d","xprem-branch":""}"/>
|
||||
<service android:name="com.reactnativeandroidwidget.RNWidgetCollectionService" android:permission="android.permission.BIND_REMOTEVIEWS"/>
|
||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize|assetsPaths" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
@@ -46,7 +47,6 @@
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="app.sauci"/>
|
||||
<data android:scheme="com.sauci.app"/>
|
||||
<data android:scheme="exp+sauci"/>
|
||||
</intent-filter>
|
||||
<intent-filter android:autoVerify="true" data-generated="true">
|
||||
@@ -56,7 +56,7 @@
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<receiver android:name=".widget.LiveDraw" android:exported="true" android:label="Live Draw">
|
||||
<receiver android:name=".widget.LiveDraw" android:exported="false" android:label="Live Draw">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
<action android:name="com.sauci.app.WIDGET_CLICK"/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
package com.sauci.app
|
||||
import expo.modules.splashscreen.SplashScreenManager
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -15,7 +16,10 @@ class MainActivity : ReactActivity() {
|
||||
// Set the theme to AppTheme BEFORE onCreate to support
|
||||
// coloring the background, status bar, and navigation bar.
|
||||
// This is required for expo-splash-screen.
|
||||
setTheme(R.style.AppTheme);
|
||||
// setTheme(R.style.AppTheme);
|
||||
// @generated begin expo-splashscreen - expo prebuild (DO NOT MODIFY) sync-f3ff59a738c56c9a6119210cb55f0b613eb8b6af
|
||||
SplashScreenManager.registerOnActivity(this)
|
||||
// @generated end expo-splashscreen
|
||||
super.onCreate(null)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,40 +5,27 @@ import android.content.res.Configuration
|
||||
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactNativeHost
|
||||
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.react.common.ReleaseLevel
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher
|
||||
import expo.modules.ReactNativeHostWrapper
|
||||
import expo.modules.ExpoReactHostFactory
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
|
||||
this,
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> {
|
||||
val packages = PackageList(this).packages
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages
|
||||
}
|
||||
|
||||
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
|
||||
|
||||
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
||||
|
||||
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
||||
}
|
||||
)
|
||||
|
||||
override val reactHost: ReactHost
|
||||
get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
|
||||
override val reactHost: ReactHost by lazy {
|
||||
ExpoReactHostFactory.getDefaultReactHost(
|
||||
context = applicationContext,
|
||||
packageList =
|
||||
PackageList(this).packages.apply {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// add(MyReactNativePackage())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
@@ -1,84 +1,6 @@
|
||||
package com.sauci.app.widget;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.Base64;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.RemoteViews;
|
||||
import com.reactnativeandroidwidget.RNWidgetProvider;
|
||||
|
||||
import com.sauci.app.R;
|
||||
|
||||
public class LiveDraw extends AppWidgetProvider {
|
||||
|
||||
private static final String PREFS_NAME_SUFFIX = ".widgetdata";
|
||||
private static final String KEY_IMAGE = "LiveDrawImage";
|
||||
private static final int BG_COLOR = 0xFF1A1A2E;
|
||||
private static final int CORNER_RADIUS_DP = 16;
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
for (int widgetId : appWidgetIds) {
|
||||
updateWidget(context, appWidgetManager, widgetId);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) {
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_live_draw);
|
||||
|
||||
String base64 = getStoredImage(context);
|
||||
if (base64 != null && !base64.isEmpty()) {
|
||||
try {
|
||||
byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
|
||||
Bitmap decoded = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||
if (decoded != null) {
|
||||
Bitmap rounded = addRoundedBackground(context, decoded);
|
||||
views.setImageViewBitmap(R.id.widget_image, rounded);
|
||||
decoded.recycle();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
setPlaceholder(views);
|
||||
}
|
||||
} else {
|
||||
setPlaceholder(views);
|
||||
}
|
||||
|
||||
appWidgetManager.updateAppWidget(widgetId, views);
|
||||
}
|
||||
|
||||
private String getStoredImage(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
context.getPackageName() + PREFS_NAME_SUFFIX, Context.MODE_PRIVATE);
|
||||
return prefs.getString(KEY_IMAGE, null);
|
||||
}
|
||||
|
||||
private void setPlaceholder(RemoteViews views) {
|
||||
views.setImageViewResource(R.id.widget_image, R.drawable.widget_placeholder);
|
||||
}
|
||||
|
||||
private Bitmap addRoundedBackground(Context context, Bitmap drawing) {
|
||||
float radius = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS_DP,
|
||||
context.getResources().getDisplayMetrics());
|
||||
|
||||
int w = drawing.getWidth();
|
||||
int h = drawing.getHeight();
|
||||
|
||||
Bitmap output = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(output);
|
||||
|
||||
Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
bgPaint.setColor(BG_COLOR);
|
||||
canvas.drawRoundRect(new RectF(0, 0, w, h), radius, radius, bgPaint);
|
||||
|
||||
canvas.drawBitmap(drawing, 0, 0, null);
|
||||
|
||||
return output;
|
||||
}
|
||||
public class LiveDraw extends RNWidgetProvider {
|
||||
}
|
||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 24 KiB |
@@ -2,6 +2,5 @@
|
||||
<color name="splashscreen_background">#1a1a2e</color>
|
||||
<color name="iconBackground">#1a1a2e</color>
|
||||
<color name="colorPrimary">#023c69</color>
|
||||
<color name="colorPrimaryDark">#1a1a2e</color>
|
||||
<color name="notification_icon_color">#e94560</color>
|
||||
</resources>
|
||||
@@ -1,7 +1,7 @@
|
||||
<resources>
|
||||
<string name="app_name">Sauci</string>
|
||||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||
<string name="widget_livedraw_description" translatable="false">See your shared drawing with your partner.</string>
|
||||
<string name="expo_system_ui_user_interface_style" translatable="false">automatic</string>
|
||||
<string name="expo_runtime_version">1.0.7</string>
|
||||
<string name="widget_livedraw_description" translatable="false">See your shared drawing with your partner.</string>
|
||||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||
</resources>
|
||||
@@ -1,18 +1,14 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:enforceNavigationBarContrast" tools:targetApi="29">true</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
<item name="android:editTextStyle">@style/ResetEditText</item>
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="android:statusBarColor">#1a1a2e</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
<style name="ResetEditText" parent="@android:style/Widget.EditText">
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:textColorHint">#c8c8c8</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
<style name="Theme.App.SplashScreen" parent="AppTheme">
|
||||
<item name="android:windowBackground">@drawable/ic_launcher_background</item>
|
||||
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
|
||||
<item name="postSplashScreenTheme">@style/AppTheme</item>
|
||||
<item name="android:windowSplashScreenBehavior">icon_preferred</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -4,10 +4,18 @@
|
||||
android:minHeight="110dp"
|
||||
android:targetCellWidth="4"
|
||||
android:targetCellHeight="2"
|
||||
|
||||
|
||||
|
||||
android:resizeMode="none"
|
||||
|
||||
android:description="@string/widget_livedraw_description"
|
||||
android:initialLayout="@layout/widget_live_draw"
|
||||
|
||||
android:initialLayout="@layout/rn_widget"
|
||||
android:previewImage="@drawable/livedraw_preview"
|
||||
|
||||
|
||||
|
||||
android:updatePeriodMillis="1800000"
|
||||
android:widgetCategory="home_screen">
|
||||
</appwidget-provider>
|
||||
@@ -1,26 +1,26 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6'
|
||||
classpath 'com.google.gms:google-services:4.4.1'
|
||||
classpath('com.android.tools.build:gradle')
|
||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
||||
}
|
||||
classpath 'com.google.gms:google-services:4.4.4'
|
||||
classpath('com.android.tools.build:gradle')
|
||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "expo-root-project"
|
||||
apply plugin: "com.facebook.react.rootproject"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
@@ -41,6 +41,11 @@ newArchEnabled=true
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
||||
|
||||
# Use this property to enable edge-to-edge display support.
|
||||
# This allows your app to draw behind system bars for an immersive UI.
|
||||
# Note: Only works with ReactActivity and should not be used with custom Activity.
|
||||
edgeToEdgeEnabled=true
|
||||
|
||||
# Enable GIF support in React Native images (~200 B increase)
|
||||
expo.gif.enabled=true
|
||||
# Enable webp support in React Native images (~85 KB increase)
|
||||
@@ -55,16 +60,7 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
|
||||
# Use legacy packaging to compress native libraries in the resulting APK.
|
||||
expo.useLegacyPackaging=false
|
||||
|
||||
android.targetSdkVersion=36
|
||||
android.extraMavenRepos=[]
|
||||
# Specifies whether the app is configured to use edge-to-edge via the app config or plugin
|
||||
# WARNING: This property has been deprecated and will be removed in Expo SDK 55. Use `edgeToEdgeEnabled` or `react.edgeToEdgeEnabled` to determine whether the project is using edge-to-edge.
|
||||
expo.edgeToEdgeEnabled=true
|
||||
|
||||
# Use this property to enable edge-to-edge display support.
|
||||
# This allows your app to draw behind system bars for an immersive UI.
|
||||
# Note: Only works with ReactActivity and should not be used with custom Activity.
|
||||
edgeToEdgeEnabled=true
|
||||
|
||||
expo.inlineModules.watchedDirectories=[]
|
||||
android.compileSdkVersion=36
|
||||
android.buildToolsVersion=36.0.0
|
||||
android.targetSdkVersion=36
|
||||
android.buildToolsVersion=36.0.0
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -115,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -173,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -206,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
@REM
|
||||
@REM This source code is licensed under the MIT license found in the
|
||||
@REM LICENSE file in the root directory of this source tree.
|
||||
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@@ -70,11 +75,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -6,7 +6,7 @@ pluginManagement {
|
||||
}.standardOutput.asText.get().trim()
|
||||
).getParentFile().absolutePath
|
||||
includeBuild(reactNativeGradlePlugin)
|
||||
|
||||
|
||||
def expoPluginsPath = new File(
|
||||
providers.exec {
|
||||
workingDir(rootDir)
|
||||
@@ -29,10 +29,10 @@ extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
|
||||
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
|
||||
}
|
||||
}
|
||||
expoAutolinking.useExpoModules()
|
||||
|
||||
rootProject.name = 'Sauci'
|
||||
|
||||
expoAutolinking.useExpoModules()
|
||||
expoAutolinking.useExpoVersionCatalog()
|
||||
|
||||
include ':app'
|
||||
|
||||
@@ -7,12 +7,6 @@
|
||||
"icon": "./assets/icon.png",
|
||||
"scheme": "app.sauci",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"newArchEnabled": true,
|
||||
"splash": {
|
||||
"image": "./assets/splash-icon.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#1a1a2e"
|
||||
},
|
||||
"ios": {
|
||||
"supportsTablet": false,
|
||||
"bundleIdentifier": "com.sauci.app",
|
||||
@@ -223,6 +217,14 @@
|
||||
},
|
||||
"plugins": [
|
||||
"./plugins/withFixHoisting.js",
|
||||
[
|
||||
"expo-splash-screen",
|
||||
{
|
||||
"image": "./assets/splash-icon.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#1a1a2e"
|
||||
}
|
||||
],
|
||||
"@react-native-firebase/app",
|
||||
[
|
||||
"expo-build-properties",
|
||||
@@ -320,7 +322,10 @@
|
||||
"origin": false
|
||||
},
|
||||
"eas": {
|
||||
"projectId": "807ca0af-e9bd-40e8-a658-f3382072c724"
|
||||
"projectId": "807ca0af-e9bd-40e8-a658-f3382072c724",
|
||||
"observe": {
|
||||
"endpointUrl": "https://ota.apps.jbrunton.co.uk/observe/9a66f3c2-fda7-416f-87e2-6db833b32e9d"
|
||||
}
|
||||
}
|
||||
},
|
||||
"owner": "jjbrunton-apps"
|
||||
|
||||
@@ -389,7 +389,7 @@ const styles = StyleSheet.create({
|
||||
zIndex: 100,
|
||||
},
|
||||
navBarBackground: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.border,
|
||||
backgroundColor: colors.background,
|
||||
|
||||
@@ -5,6 +5,11 @@ import "../src/polyfills/web";
|
||||
import { initCrashlytics, setUserContext, clearUserContext } from "../src/lib/crashlytics";
|
||||
initCrashlytics();
|
||||
|
||||
// Install the Observe global JS error handler after Crashlytics so it wraps
|
||||
// (and always calls) whatever handler is already registered.
|
||||
import { installObserveGlobalErrorHandler, logAppStarted } from "../src/lib/observe";
|
||||
installObserveGlobalErrorHandler();
|
||||
|
||||
// Analytics imports (initialization deferred until app mounts)
|
||||
import { initAnalytics, setUserId, clearUserId, logScreenView, Events } from "../src/lib/analytics";
|
||||
|
||||
@@ -73,6 +78,7 @@ export default function RootLayout() {
|
||||
analyticsInitialized.current = true;
|
||||
initAnalytics();
|
||||
Events.appOpened("cold");
|
||||
logAppStarted();
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -115,15 +121,15 @@ export default function RootLayout() {
|
||||
// Fetch user on mount
|
||||
fetchUser();
|
||||
|
||||
// Handle initial URL if app was opened via deep link
|
||||
const handleDeepLink = async (url: string | null) => {
|
||||
if (!url) return;
|
||||
// Handle initial URL if app was opened via deep link
|
||||
const handleDeepLink = async (url: string | null) => {
|
||||
if (!url) return;
|
||||
|
||||
// Invite links (https://sauci.app/join/{code} or app.sauci://join?code={code})
|
||||
// are handled separately from the Supabase auth deep link flow below.
|
||||
const inviteLink = parseInviteLinkCode(url);
|
||||
if (inviteLink) {
|
||||
Events.inviteLinkOpened(inviteLink.source);
|
||||
const inviteLink = parseInviteLinkCode(url);
|
||||
if (inviteLink) {
|
||||
Events.inviteLinkOpened(inviteLink.source);
|
||||
await stashPendingInviteCode(inviteLink.code);
|
||||
|
||||
const { isAuthenticated, user } = useAuthStore.getState();
|
||||
@@ -200,15 +206,15 @@ export default function RootLayout() {
|
||||
}
|
||||
};
|
||||
|
||||
// Get initial URL
|
||||
Linking.getInitialURL().then((url) => {
|
||||
handleDeepLink(url);
|
||||
});
|
||||
// Get initial URL
|
||||
Linking.getInitialURL().then((url) => {
|
||||
handleDeepLink(url);
|
||||
});
|
||||
|
||||
// Listen for new deep links while app is open
|
||||
const linkingSubscription = Linking.addEventListener("url", (event) => {
|
||||
handleDeepLink(event.url);
|
||||
});
|
||||
// Listen for new deep links while app is open
|
||||
const linkingSubscription = Linking.addEventListener("url", (event) => {
|
||||
handleDeepLink(event.url);
|
||||
});
|
||||
|
||||
// Set up push notification response listener (for taps while app is running)
|
||||
const notificationSubscription = setupNotificationResponseListener();
|
||||
|
||||
@@ -425,7 +425,7 @@ const styles = StyleSheet.create({
|
||||
zIndex: 100,
|
||||
},
|
||||
navBarBackground: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.border,
|
||||
overflow: "hidden",
|
||||
|
||||
@@ -28,3 +28,6 @@ project.xcworkspace
|
||||
|
||||
# CocoaPods
|
||||
/Pods/
|
||||
|
||||
# @bacons/apple-targets generated entitlements cache
|
||||
/.targets/
|
||||
@@ -1,15 +1,28 @@
|
||||
# Set by expo-router. This enables Fabric-only features from react-native-screens
|
||||
ENV['RNS_GAMMA_ENABLED'] ||= '1'
|
||||
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
||||
|
||||
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
|
||||
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
||||
def ccache_enabled?(podfile_properties)
|
||||
# Environment variable takes precedence
|
||||
return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE']
|
||||
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
|
||||
install! 'cocoapods',
|
||||
:deterministic_uuids => false
|
||||
# Fall back to Podfile properties
|
||||
podfile_properties['apple.ccacheEnabled'] == 'true'
|
||||
end
|
||||
|
||||
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
||||
ENV['RCT_USE_RN_DEP'] ||= podfile_properties['ios.buildReactNativeFromSource'] == 'true' ? '0' : '1'
|
||||
ENV['RCT_USE_PREBUILT_RNCORE'] ||= podfile_properties['ios.buildReactNativeFromSource'] == 'true' ? '0' : '1'
|
||||
ENV['RCT_HERMES_V1_ENABLED'] ||= '0' if podfile_properties['expo.useHermesV1'] == 'false'
|
||||
# An explicit `ios.usePrecompiledModules: false` must win even when the env var is already set
|
||||
# (e.g. EXPO_USE_PRECOMPILED_MODULES exported by EAS Build), so force-disable here before the default.
|
||||
ENV['EXPO_USE_PRECOMPILED_MODULES'] = '0' if podfile_properties['EXPO_USE_PRECOMPILED_MODULES'] == 'false'
|
||||
ENV['EXPO_USE_PRECOMPILED_MODULES'] ||= '1'
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '16.4'
|
||||
|
||||
prepare_react_native_project!
|
||||
|
||||
@@ -23,7 +36,8 @@ target 'Sauci' do
|
||||
'node',
|
||||
'--no-warnings',
|
||||
'--eval',
|
||||
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
|
||||
'require(\'expo/bin/autolinking\')',
|
||||
'expo-modules-autolinking',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
@@ -49,34 +63,8 @@ target 'Sauci' do
|
||||
installer,
|
||||
config[:reactNativePath],
|
||||
:mac_catalyst_enabled => false,
|
||||
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
|
||||
:ccache_enabled => ccache_enabled?(podfile_properties),
|
||||
)
|
||||
|
||||
# The fmt version bundled with React Native 0.76 enables consteval for
|
||||
# Apple Clang, but Apple Clang 21 (Xcode 26.4+) rejects fmt's FMT_STRING
|
||||
# call sites. Patch the generated pod until React Native supplies a newer
|
||||
# fmt release. Keeping this here makes clean pod installs reproducible.
|
||||
fmt_base_header = File.join(__dir__, 'Pods', 'fmt', 'include', 'fmt', 'base.h')
|
||||
if File.exist?(fmt_base_header)
|
||||
fmt_base_source = File.read(fmt_base_header)
|
||||
fmt_base_source.sub!(
|
||||
/#elif defined\(__apple_build_version__\) && __apple_build_version__ < 14000029L/,
|
||||
'#elif defined(__apple_build_version__)'
|
||||
)
|
||||
File.chmod(0644, fmt_base_header)
|
||||
File.write(fmt_base_header, fmt_base_source)
|
||||
end
|
||||
|
||||
# This is necessary for Xcode 14, because it signs resource bundles by default
|
||||
# when building for devices.
|
||||
installer.target_installation_results.pod_target_installation_results
|
||||
.each do |pod_name, target_installation_result|
|
||||
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
|
||||
resource_bundle_target.build_configurations.each do |config|
|
||||
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"expo.jsEngine": "hermes",
|
||||
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
|
||||
"newArchEnabled": "true",
|
||||
"ios.useFrameworks": "static",
|
||||
"apple.extraPods": "[]",
|
||||
"apple.ccacheEnabled": "false",
|
||||
"apple.privacyManifestAggregationEnabled": "true"
|
||||
"expo.inlineModules.watchedDirectories": "[]",
|
||||
"expo.inlineModules.xcodeProjectTargets": "{\"mainTarget\":\"Sauci\",\"targets\":[]}",
|
||||
"ios.forceStaticLinking": "[]",
|
||||
"apple.privacyManifestAggregationEnabled": "true",
|
||||
"EXPO_USE_PRECOMPILED_MODULES": "true",
|
||||
"ios.useFrameworks": "static"
|
||||
}
|
||||
@@ -3,19 +3,18 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||
5254BDADE70741A0B2504FB1 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = F744EAD4ABA54509966475D6 /* noop-file.swift */; };
|
||||
7B71EEB2AF682FA7AFF556D5 /* Pods_Sauci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */; };
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
|
||||
3F202C2A859975F48A83ADD7 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */; };
|
||||
45941E25D336B6152BFCEFB9 /* Pods_Sauci.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */; };
|
||||
804CD03F6B584D08B7803C63 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */; };
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||
CEF2F278ABBC5FBFD470A0F7 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */; };
|
||||
D60EEEF252214D45B67A0063 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */; };
|
||||
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
|
||||
FDFABE1A1A3547B1B22DD2DD /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */; };
|
||||
XX12B55A1EDABF083C3B51XX /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */; };
|
||||
XX1A6800511A685AB346D2XX /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXA12889081EB894481655XX /* AppIntents.framework */; };
|
||||
XX3C562DEF346B45594832XX /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */; };
|
||||
@@ -24,11 +23,11 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
XXA7C7D5580082A497FD95XX /* PBXContainerItemProxy */ = {
|
||||
XX77D941E1DB38490B8F4EXX /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = XX540FB4ABC75CE91A6036XX;
|
||||
remoteGlobalIDString = XXB3405CC208C2ECA4797FXX;
|
||||
remoteInfo = LiveDrawWidget;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
@@ -51,18 +50,17 @@
|
||||
13B07F961A680F5B00A75B9A /* Sauci.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; fileEncoding = 4; includeInIndex = 0; path = Sauci.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Sauci/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sauci/Info.plist; sourceTree = "<group>"; };
|
||||
6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.debug.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.release.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.release.xcconfig"; sourceTree = "<group>"; };
|
||||
A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Sauci/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = PrivacyInfo.xcprivacy; path = Sauci/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Sauci/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
||||
A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.release.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.release.xcconfig"; sourceTree = "<group>"; };
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Sauci/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||
B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sauci.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||
D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = undefined; name = PrivacyInfo.xcprivacy; path = Sauci/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
DA536CAC88B349A59DFC1F1E /* Sauci-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Sauci-Bridging-Header.h"; path = "Sauci/Sauci-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; fileEncoding = 4; includeInIndex = undefined; path = Pods_Sauci.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Sauci/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Sauci/AppDelegate.swift; sourceTree = "<group>"; };
|
||||
F744EAD4ABA54509966475D6 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Sauci/noop-file.swift"; sourceTree = "<group>"; };
|
||||
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Sauci/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
||||
F11748442D0722820044C1D9 /* Sauci-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Sauci-Bridging-Header.h"; path = "Sauci/Sauci-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sauci.debug.xcconfig"; path = "Target Support Files/Pods-Sauci/Pods-Sauci.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
|
||||
XX4DFF38D47332D6BF0183XX /* SwiftUI.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
XXA12889081EB894481655XX /* AppIntents.framework */ = {isa = PBXFileReference; fileEncoding = 4; includeInIndex = undefined; lastKnownFileType = wrapper.framework; name = AppIntents.framework; path = System/Library/Frameworks/AppIntents.framework; sourceTree = SDKROOT; };
|
||||
@@ -71,21 +69,21 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
XX020388E3BB9C764E774FXX /* Exceptions for "widget" folder in "LiveDrawWidget" target */ = {
|
||||
XX6FB9E6678087E3AAC658XX /* Exceptions for "widget" folder in "LiveDrawWidget" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
"expo-target.config.js",
|
||||
);
|
||||
target = XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */;
|
||||
target = XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
XX683E29AD7AD8279E17B2XX /* widget */ = {
|
||||
XXBB44BF07EB68666150ABXX /* widget */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
XX020388E3BB9C764E774FXX /* Exceptions for "widget" folder in "LiveDrawWidget" target */,
|
||||
XX6FB9E6678087E3AAC658XX /* Exceptions for "widget" folder in "LiveDrawWidget" target */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
@@ -101,7 +99,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7B71EEB2AF682FA7AFF556D5 /* Pods_Sauci.framework in Frameworks */,
|
||||
45941E25D336B6152BFCEFB9 /* Pods_Sauci.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -122,15 +120,32 @@
|
||||
13B07FAE1A68108700A75B9A /* Sauci */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB2F792B24A3F905000567C9 /* Supporting */,
|
||||
F11748412D0307B40044C1D9 /* AppDelegate.swift */,
|
||||
F11748442D0722820044C1D9 /* Sauci-Bridging-Header.h */,
|
||||
BB2F792B24A3F905000567C9 /* Supporting */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
|
||||
A9280FDF3E0D43AC90382DBC /* GoogleService-Info.plist */,
|
||||
F744EAD4ABA54509966475D6 /* noop-file.swift */,
|
||||
DA536CAC88B349A59DFC1F1E /* Sauci-Bridging-Header.h */,
|
||||
D7B5C17768E3B74AC152531F /* PrivacyInfo.xcprivacy */,
|
||||
69E91809C21A4977A3249D53 /* PrivacyInfo.xcprivacy */,
|
||||
D58CE235EF5F40F58FB8111D /* GoogleService-Info.plist */,
|
||||
);
|
||||
name = Sauci;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1957149DB83AAF8603C60859 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */,
|
||||
A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2BFCD64E7EEB594CC2D9E261 /* Sauci */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
779267BD380A5D29590F1BDC /* ExpoModulesProvider.swift */,
|
||||
);
|
||||
name = Sauci;
|
||||
sourceTree = "<group>";
|
||||
@@ -141,9 +156,9 @@
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
XX470101BAEB8E0BBC8B0DXX /* WidgetKit.framework */,
|
||||
XX4DFF38D47332D6BF0183XX /* SwiftUI.framework */,
|
||||
DDDAD8C12C38D3BD0A76D345 /* Pods_Sauci.framework */,
|
||||
XXCAF23C3FC41F82140FA1XX /* ActivityKit.framework */,
|
||||
XXA12889081EB894481655XX /* AppIntents.framework */,
|
||||
B482C7D915C22B3F33CA9841 /* Pods_Sauci.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -163,8 +178,8 @@
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
D65327D7A22EEC0BE12398D9 /* Pods */,
|
||||
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
|
||||
1957149DB83AAF8603C60859 /* Pods */,
|
||||
CC8FEB5C5178B9E5E71D4F61 /* ExpoModulesProviders */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
@@ -180,14 +195,6 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92DBD88DE9BF7D494EA9DA96 /* Sauci */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
|
||||
);
|
||||
name = Sauci;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BB2F792B24A3F905000567C9 /* Supporting */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -197,19 +204,10 @@
|
||||
path = Sauci/Supporting;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D65327D7A22EEC0BE12398D9 /* Pods */ = {
|
||||
CC8FEB5C5178B9E5E71D4F61 /* ExpoModulesProviders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */,
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92DBD88DE9BF7D494EA9DA96 /* Sauci */,
|
||||
2BFCD64E7EEB594CC2D9E261 /* Sauci */,
|
||||
);
|
||||
name = ExpoModulesProviders;
|
||||
sourceTree = "<group>";
|
||||
@@ -217,7 +215,7 @@
|
||||
XX3E41C1850246162C0061XX /* expo:targets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
XX683E29AD7AD8279E17B2XX /* widget */,
|
||||
XXBB44BF07EB68666150ABXX /* widget */,
|
||||
);
|
||||
name = "expo:targets";
|
||||
path = ../targets;
|
||||
@@ -231,30 +229,31 @@
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sauci" */;
|
||||
buildPhases = (
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
|
||||
DC3D179C6B62F16DA5F17C2F /* [Expo] Configure project */,
|
||||
E6F7433229E3C1448FC9F062 /* [Expo] Configure project */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||
D58CF933D4804A0CB49C2891 /* [Expo Dev Launcher] Strip Local Network Keys for Release */,
|
||||
XX72A8CFBF0FBA13A79902XX /* Embed Foundation Extensions */,
|
||||
A1248897D24903117793599B /* [CP] Embed Pods Frameworks */,
|
||||
FB4B019D835055733ACDE109 /* [CP-User] [RNFB] Core Configuration */,
|
||||
563ACE2651AC39438F3B399B /* [CP-User] [RNFB] Crashlytics Configuration */,
|
||||
503A0FD3DBA862B4BE649BBB /* [CP] Embed Pods Frameworks */,
|
||||
8490E10253F91A7BEB80F555 /* [CP-User] [RNFB] Core Configuration */,
|
||||
0EE1B67C9AAD8BD8562E800E /* [CP-User] [RNFB] Crashlytics Configuration */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
XX97289CEC7994AD5F4142XX /* PBXTargetDependency */,
|
||||
XX5D3A21868E45E6E5BDC3XX /* PBXTargetDependency */,
|
||||
);
|
||||
name = Sauci;
|
||||
productName = Sauci;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* Sauci.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */ = {
|
||||
XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = XX63FAB1BD532DA0D8AE18XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */;
|
||||
buildConfigurationList = XX35D62F4FBF2C9086E8C9XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */;
|
||||
buildPhases = (
|
||||
XX074B8811C1CF8E8A3D74XX /* Frameworks */,
|
||||
XXE45443DC38DCFE1DB622XX /* Sources */,
|
||||
@@ -265,7 +264,7 @@
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
XX683E29AD7AD8279E17B2XX /* widget */,
|
||||
XXBB44BF07EB68666150ABXX /* widget */,
|
||||
);
|
||||
name = LiveDrawWidget;
|
||||
productName = LiveDrawWidget;
|
||||
@@ -283,7 +282,7 @@
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1250;
|
||||
};
|
||||
XX540FB4ABC75CE91A6036XX = {
|
||||
XXB3405CC208C2ECA4797FXX = {
|
||||
CreatedOnToolsVersion = 14.3;
|
||||
DevelopmentTeam = BXVPBATDZY;
|
||||
ProvisioningStyle = Automatic;
|
||||
@@ -304,7 +303,7 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* Sauci */,
|
||||
XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */,
|
||||
XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -317,8 +316,8 @@
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
|
||||
FDFABE1A1A3547B1B22DD2DD /* GoogleService-Info.plist in Resources */,
|
||||
CEF2F278ABBC5FBFD470A0F7 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
804CD03F6B584D08B7803C63 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
D60EEEF252214D45B67A0063 /* GoogleService-Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -341,6 +340,8 @@
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/.xcode.env",
|
||||
"$(SRCROOT)/.xcode.env.local",
|
||||
);
|
||||
name = "Bundle React Native code and images";
|
||||
outputFileListPaths = (
|
||||
@@ -373,7 +374,7 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
563ACE2651AC39438F3B399B /* [CP-User] [RNFB] Crashlytics Configuration */ = {
|
||||
0EE1B67C9AAD8BD8562E800E /* [CP-User] [RNFB] Crashlytics Configuration */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -387,6 +388,52 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n";
|
||||
};
|
||||
503A0FD3DBA862B4BE649BBB /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem.framework/ExpoFileSystem",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoFont/ExpoFont.framework/ExpoFont",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/ExpoImage.framework/ExpoImage",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImage.framework/SDWebImage",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImageAVIFCoder.framework/SDWebImageAVIFCoder",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImageSVGCoder.framework/SDWebImageSVGCoder",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/SDWebImageWebPCoder.framework/SDWebImageWebPCoder",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoImage/libavif.framework/libavif",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary.framework/ExpoMediaLibrary",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoModulesCore/ExpoModulesCore.framework/ExpoModulesCore",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoModulesJSI/ExpoModulesJSI.framework/ExpoModulesJSI",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ExpoModulesWorklets/ExpoModulesWorklets.framework/ExpoModulesWorklets",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/React-Core-prebuilt/React.framework/React",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ReactNativeDependencies/ReactNativeDependencies.framework/ReactNativeDependencies",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermesvm.framework/hermesvm",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoFileSystem.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoFont.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoImage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageAVIFCoder.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libavif.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoMediaLibrary.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoModulesCore.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoModulesJSI.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExpoModulesWorklets.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactNativeDependencies.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermesvm.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -398,9 +445,10 @@
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoNotifications/ExpoNotifications_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle",
|
||||
@@ -414,18 +462,16 @@
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/PurchasesHybridCommon/PurchasesHybridCommon.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/ReachabilitySwift.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/RevenueCat/RevenueCat.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/react-native-view-shot/RNViewShotPrivacyInfo.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
@@ -433,9 +479,10 @@
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle",
|
||||
@@ -449,43 +496,54 @@
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PurchasesHybridCommon.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReachabilitySwift.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RevenueCat.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNViewShotPrivacyInfo.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
A1248897D24903117793599B /* [CP] Embed Pods Frameworks */ = {
|
||||
8490E10253F91A7BEB80F555 /* [CP-User] [RNFB] Core Configuration */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
|
||||
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
|
||||
);
|
||||
name = "[CP-User] [RNFB] Core Configuration";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"note: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"note: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"note: -> RNFB build script started\"\necho \"note: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"note: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"note: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"error: python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACLine truncated
|
||||
};
|
||||
D58CF933D4804A0CB49C2891 /* [Expo Dev Launcher] Strip Local Network Keys for Release */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[Expo Dev Launcher] Strip Local Network Keys for Release";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sauci/Pods-Sauci-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
shellScript = "# Strip dev-launcher-specific local network permission keys from non-Debug builds\n# This only removes _expo._tcp Bonjour services and the dev-launcher usage description.\n# Other Bonjour services and custom descriptions are preserved for production use.\n\nif [ \"$CONFIGURATION\" != \"Debug\" ]; then\n PLIST_PATH=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n if [ -f \"$PLIST_PATH\" ]; then\n # Check if NSBonjourServices exists\n if /usr/libexec/PlistBuddy -c \"Print :NSBonjourServices\" \"$PLIST_PATH\" >/dev/null 2>&1; then\n # Get the count of services\n COUNT=$(/usr/libexec/PlistBuddy -c \"Print :NSBonjourServices\" \"$PLIST_PATH\" 2>/dev/null | grep \"^ \" | wc -l | tr -d ' ')\n\n # Remove _expo._tcp\n for ((i=COUNT-1; i>=0; i--)); do\n SERVICE=$(/usr/libexec/PlistBuddy -c \"Print :NSBonjourServices:$i\" \"$PLIST_PATH\" 2>/dev/null || echo \"\")\n if echo \"$SERVICE\" | grep -q \"_expo._tcp\"; then\n /usr/libexec/PlistBuddy -c \"Delete :NSBonjourServices:$i\" \"$PLIST_PATH\" 2>/dev/null || true\n fi\n done\n\n # If the array is now empty, remove it entirely\n REMAINING=$(/usr/libexec/PlistBuddy -c \"Print :NSBonjourServices\" \"$PLIST_PATH\" 2>/dev/null | grep \"^ \" | wc -l | tr -d ' ')\n if [ \"$REMAINING\" -eq \"0\" ]; then\n /usr/libexec/PlistBuddy -c \"Delete :NSBonjourServices\" \"$PLIST_PATH\" 2>/dev/null || true\n fi\n fi\n\n # Only delete the description if it matches the dev-launcher default text\n DESC=$(/usr/libexec/PlistBuddy -c \"Print :NSLocalNetworkUsageDescription\" \"$PLIST_PATH\" 2>/dev/null || echo \"\")\n if echo \"$DESC\" | grep -q \"Expo Dev Launcher\"; then\n /usr/libexec/PlistBuddy -c \"Delete :NSLocalNetworkUsageDescription\" \"$PLIST_PATH\" 2>/dev/null || true\n fi\n fi\nfi\n";
|
||||
};
|
||||
DC3D179C6B62F16DA5F17C2F /* [Expo] Configure project */ = {
|
||||
E6F7433229E3C1448FC9F062 /* [Expo] Configure project */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -509,19 +567,6 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Sauci/expo-configure-project.sh\"\n";
|
||||
};
|
||||
FB4B019D835055733ACDE109 /* [CP-User] [RNFB] Core Configuration */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
|
||||
);
|
||||
name = "[CP-User] [RNFB] Core Configuration";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"note: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"note: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"note: -> RNFB build script started\"\necho \"note: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"note: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"note: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"error: python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACLine truncated
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -530,8 +575,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */,
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
|
||||
5254BDADE70741A0B2504FB1 /* noop-file.swift in Sources */,
|
||||
3F202C2A859975F48A83ADD7 /* ExpoModulesProvider.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -545,17 +589,17 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
XX97289CEC7994AD5F4142XX /* PBXTargetDependency */ = {
|
||||
XX5D3A21868E45E6E5BDC3XX /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = XX540FB4ABC75CE91A6036XX /* LiveDrawWidget */;
|
||||
targetProxy = XXA7C7D5580082A497FD95XX /* PBXContainerItemProxy */;
|
||||
target = XXB3405CC208C2ECA4797FXX /* LiveDrawWidget */;
|
||||
targetProxy = XX77D941E1DB38490B8F4EXX /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-Sauci.debug.xcconfig */;
|
||||
baseConfigurationReference = FEACE2E5B4EA50AFBF4DDF34 /* Pods-Sauci.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -568,8 +612,12 @@
|
||||
"FB_SONARKIT_ENABLED=1",
|
||||
);
|
||||
INFOPLIST_FILE = Sauci/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.4;
|
||||
MARKETING_VERSION = 1.0.7;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -589,7 +637,7 @@
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-Sauci.release.xcconfig */;
|
||||
baseConfigurationReference = A172EA788EF476A7083810FE /* Pods-Sauci.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -597,8 +645,12 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = BXVPBATDZY;
|
||||
INFOPLIST_FILE = Sauci/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.4;
|
||||
MARKETING_VERSION = 1.0.7;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -664,29 +716,39 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers/platform/ios",
|
||||
"${PODS_ROOT}/ReactCommon",
|
||||
"${PODS_ROOT}/ReactCommon/react/nativemodule/core",
|
||||
"${PODS_ROOT}/React-runtimeexecutor",
|
||||
"${PODS_ROOT}/React-runtimeexecutor/platform/ios",
|
||||
"${PODS_ROOT}/ReactCommon-Samples",
|
||||
"${PODS_ROOT}/ReactCommon-Samples/platform/ios",
|
||||
"${PODS_ROOT}/React-Fabric/react/renderer/components/view/platform/cxx",
|
||||
"${PODS_ROOT}/React-NativeModulesApple",
|
||||
"${PODS_ROOT}/React-graphics",
|
||||
"${PODS_ROOT}/React-graphics/react/renderer/graphics/platform/ios",
|
||||
"${PODS_ROOT}/React-featureflags",
|
||||
"${PODS_ROOT}/React-renderercss",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
/usr/lib/swift,
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = (
|
||||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
"-DRCT_REMOVE_LEGACY_ARCH=1",
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(inherited)",
|
||||
"-DRCT_REMOVE_LEGACY_ARCH=1",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
|
||||
USE_HERMES = true;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -733,33 +795,43 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimeexecutor/React_runtimeexecutor.framework/Headers/platform/ios",
|
||||
"${PODS_ROOT}/ReactCommon",
|
||||
"${PODS_ROOT}/ReactCommon/react/nativemodule/core",
|
||||
"${PODS_ROOT}/React-runtimeexecutor",
|
||||
"${PODS_ROOT}/React-runtimeexecutor/platform/ios",
|
||||
"${PODS_ROOT}/ReactCommon-Samples",
|
||||
"${PODS_ROOT}/ReactCommon-Samples/platform/ios",
|
||||
"${PODS_ROOT}/React-Fabric/react/renderer/components/view/platform/cxx",
|
||||
"${PODS_ROOT}/React-NativeModulesApple",
|
||||
"${PODS_ROOT}/React-graphics",
|
||||
"${PODS_ROOT}/React-graphics/react/renderer/graphics/platform/ios",
|
||||
"${PODS_ROOT}/React-featureflags",
|
||||
"${PODS_ROOT}/React-renderercss",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
/usr/lib/swift,
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
"-DRCT_REMOVE_LEGACY_ARCH=1",
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(inherited)",
|
||||
"-DRCT_REMOVE_LEGACY_ARCH=1",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ENABLE_EXPLICIT_MODULES = NO;
|
||||
USE_HERMES = true;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
XX1230635DC14DD95E28A2XX /* Release */ = {
|
||||
XX0A0FB078256184BF7EA2XX /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
@@ -769,7 +841,7 @@
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = ../targets/widget/generated.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = .targets/LiveDrawWidget/generated.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
@@ -781,12 +853,17 @@
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.7;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.LiveDrawWidget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.widget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -796,7 +873,7 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
XX710DB962925BCE8D59ACXX /* Debug */ = {
|
||||
XX42CD38636C47E26CBE73XX /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
@@ -806,7 +883,7 @@
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = ../targets/widget/generated.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = .targets/LiveDrawWidget/generated.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
@@ -817,13 +894,18 @@
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LiveDrawWidget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.7;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.LiveDrawWidget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.sauci.app.widget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -854,11 +936,11 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
XX63FAB1BD532DA0D8AE18XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */ = {
|
||||
XX35D62F4FBF2C9086E8C9XX /* Build configuration list for PBXNativeTarget "LiveDrawWidget" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
XX710DB962925BCE8D59ACXX /* Debug */,
|
||||
XX1230635DC14DD95E28A2XX /* Release */,
|
||||
XX42CD38636C47E26CBE73XX /* Debug */,
|
||||
XX0A0FB078256184BF7EA2XX /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Expo
|
||||
internal import Expo
|
||||
import FirebaseCore
|
||||
import React
|
||||
import ReactAppDependencyProvider
|
||||
|
||||
@UIApplicationMain
|
||||
public class AppDelegate: ExpoAppDelegate {
|
||||
@main
|
||||
class AppDelegate: ExpoAppDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
|
||||
@@ -20,11 +20,12 @@ public class AppDelegate: ExpoAppDelegate {
|
||||
|
||||
reactNativeDelegate = delegate
|
||||
reactNativeFactory = factory
|
||||
bindReactNativeFactory(factory)
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
FirebaseApp.configure()
|
||||
// @generated begin @react-native-firebase/app-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-10e8520570672fd76b2403b7e1e27f5198a6349a
|
||||
FirebaseApp.configure()
|
||||
// @generated end @react-native-firebase/app-didFinishLaunchingWithOptions
|
||||
factory.startReactNative(
|
||||
withModuleName: "main",
|
||||
in: window,
|
||||
@@ -34,42 +35,39 @@ public class AppDelegate: ExpoAppDelegate {
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
// Linking API
|
||||
public override func application(
|
||||
_ app: UIApplication,
|
||||
open url: URL,
|
||||
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
||||
) -> Bool {
|
||||
super.application(app, open: url, options: options)
|
||||
|| RCTLinkingManager.application(app, open: url, options: options)
|
||||
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
||||
}
|
||||
|
||||
// Universal Links
|
||||
public override func application(
|
||||
_ application: UIApplication,
|
||||
continue userActivity: NSUserActivity,
|
||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
||||
) -> Bool {
|
||||
let result = RCTLinkingManager.application(
|
||||
application,
|
||||
continue: userActivity,
|
||||
restorationHandler: restorationHandler)
|
||||
return super.application(
|
||||
application,
|
||||
continue: userActivity,
|
||||
restorationHandler: restorationHandler) || result
|
||||
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
|
||||
}
|
||||
}
|
||||
|
||||
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
||||
// Extension point for config-plugins
|
||||
|
||||
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
||||
// needed to return the correct URL for expo-dev-client.
|
||||
bridge.bundleURL ?? bundleURL()
|
||||
}
|
||||
|
||||
override func bundleURL() -> URL? {
|
||||
#if DEBUG
|
||||
RCTBundleURLProvider.sharedSettings()
|
||||
.jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
||||
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
||||
#else
|
||||
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||||
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 57 KiB |
@@ -1,118 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Sauci</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>app.sauci</string>
|
||||
<string>com.sauci.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>exp+sauci</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>46</string>
|
||||
<key>GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED</key>
|
||||
<false/>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>instagram</string>
|
||||
<string>instagram-stories</string>
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Sauci uses your camera to take photos and videos that you can share privately with your partner.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Sauci uses Face ID to unlock the app and keep your conversations private.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Sauci uses your microphone to record audio when you capture videos to share privately with your partner.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Sauci uses your photo library so you can select photos and videos to share privately with your partner.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||
</array>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Sauci</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>app.sauci</string>
|
||||
<string>com.sauci.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>exp+sauci</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.861972684340-0u2as8i5equfhmhmvjihottkf0ig7237</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>46</string>
|
||||
<key>GOOGLE_ANALYTICS_INAPURCHASE_COLLECTION_ENABLED</key>
|
||||
<false/>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>instagram</string>
|
||||
<string>instagram-stories</string>
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_expo._tcp</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Sauci uses your camera to take photos and videos that you can share privately with your partner.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Sauci uses Face ID to unlock the app and keep your conversations private.</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Expo Dev Launcher uses the local network to discover and connect to development servers running on your computer.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Sauci uses your microphone to record audio when you capture videos to share privately with your partner.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Sauci saves photos and videos from your partner's messages to your photo library when you tap the save button.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Sauci uses your photo library so you can select photos and videos to share privately with your partner.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||
</array>
|
||||
<key>RCTMetroPort</key>
|
||||
<string>$(RCT_METRO_PORT)</string>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -24,6 +24,14 @@
|
||||
<string>C56D.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
@@ -33,18 +41,171 @@
|
||||
<string>85F4.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeName</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeSensitiveInfo</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeUserID</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePhotosorVideos</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeAudioData</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeOtherUserContent</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeProductInteraction</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeCrashData</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePerformanceData</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypePurchaseHistory</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,3 +1,3 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
@@ -17,4 +17,4 @@
|
||||
<string>group.com.sauci.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24053.1"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@@ -17,15 +18,13 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView id="EXPO-SplashScreen" userLabel="SplashScreenLogo" image="SplashScreenLogo" contentMode="scaleAspectFit" clipsSubviews="true" userInteractionEnabled="false" translatesAutoresizingMaskIntoConstraints="false">
|
||||
<rect key="frame" x="0" y="0" width="414" height="736"/>
|
||||
<rect key="frame" x="146.5" y="376" width="100" height="100"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
|
||||
<constraints>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="83fcb9b545b870ba44c24f0feeb116490c499c52"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="61d16215e44b98e39d0a2c74fdbfaaa22601b12c"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="trailing" secondItem="EXPO-ContainerView" secondAttribute="trailing" id="f934da460e9ab5acae3ad9987d5b676a108796c1"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="bottom" secondItem="EXPO-ContainerView" secondAttribute="bottom" id="d6a0be88096b36fb132659aa90203d39139deda9"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="centerX" secondItem="EXPO-ContainerView" secondAttribute="centerX" id="cad2ab56f97c5429bf29decf850647a4216861d4"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="centerY" secondItem="EXPO-ContainerView" secondAttribute="centerY" id="1a145271b085b6ce89b1405a310f5b1bb7656595"/>
|
||||
</constraints>
|
||||
<color key="backgroundColor" name="SplashScreenBackground"/>
|
||||
</view>
|
||||
@@ -36,7 +35,10 @@
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="SplashScreenLogo" width="414" height="736"/>
|
||||
<image name="SplashScreenLogo" width="100" height="100"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<namedColor name="SplashScreenBackground">
|
||||
<color alpha="1.000" blue="0.180392156862745" green="0.101960784313725" red="0.101960784313725" customColorSpace="sRGB" colorSpace="custom"/>
|
||||
</namedColor>
|
||||
|
||||
@@ -31,6 +31,8 @@ Ug==
|
||||
<key>alg</key>
|
||||
<string>rsa-v1_5-sha256</string>
|
||||
</dict>
|
||||
<key>EXUpdatesEnableBsdiffPatchSupport</key>
|
||||
<true/>
|
||||
<key>EXUpdatesEnabled</key>
|
||||
<true/>
|
||||
<key>EXUpdatesLaunchWaitMs</key>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
//
|
||||
// @generated
|
||||
// A blank Swift file must be created for native modules with Swift files to work correctly.
|
||||
//
|
||||
@@ -12,6 +12,12 @@ module.exports = {
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1',
|
||||
// react-native-worklets ships a JS-only mock for its native module at
|
||||
// this path (react-native-reanimated depends on it internally and has
|
||||
// no equivalent of its own). Without this, requiring the real
|
||||
// react-native-worklets under Jest throws because no native module is
|
||||
// registered.
|
||||
'^react-native-worklets$': '<rootDir>/../../node_modules/react-native-worklets/src/mock.ts',
|
||||
},
|
||||
testMatch: [
|
||||
'<rootDir>/src/**/__tests__/**/*.(spec|test).(ts|tsx)',
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
"release:android:ship": "bundle exec fastlane android release",
|
||||
"release:ios:upload": "bundle exec fastlane ios upload",
|
||||
"release:ios:ship": "bundle exec fastlane ios release",
|
||||
"release:preflight": "node scripts/release-preflight.mjs",
|
||||
"release:preflight:local-eas": "node scripts/release-preflight.mjs --local-eas",
|
||||
"ota:preflight:staging": "node scripts/ota-publish.mjs staging --preflight",
|
||||
"ota:preflight:production": "node scripts/ota-publish.mjs production --preflight",
|
||||
"ota:publish:staging": "node scripts/ota-publish.mjs staging",
|
||||
@@ -32,7 +34,7 @@
|
||||
"ota:doctor:production": "node scripts/ota-publish.mjs production --doctor"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bacons/apple-targets": "^0.1.6",
|
||||
"@bacons/apple-targets": "^5.0.0",
|
||||
"@bittingz/expo-widgets": "^3.0.2",
|
||||
"@expo/ngrok": "^4.1.0",
|
||||
"@expo/vector-icons": "^15.0.3",
|
||||
@@ -42,54 +44,54 @@
|
||||
"@react-native-firebase/app": "^23.8.2",
|
||||
"@react-native-firebase/crashlytics": "^23.8.4",
|
||||
"@react-native-google-signin/google-signin": "^16.1.1",
|
||||
"@react-navigation/bottom-tabs": "^7.0.0",
|
||||
"@react-navigation/native": "^7.0.0",
|
||||
"@react-navigation/native-stack": "^7.0.0",
|
||||
"@shopify/react-native-skia": "2.2.12",
|
||||
"@shopify/react-native-skia": "2.6.2",
|
||||
"@supabase/supabase-js": "^2.39.0",
|
||||
"@tanstack/react-query": "^5.17.0",
|
||||
"babel-preset-expo": "~54.0.12",
|
||||
"babel-preset-expo": "~56.0.0",
|
||||
"base64-arraybuffer": "^1.0.2",
|
||||
"expo": "~54.0.0",
|
||||
"expo-apple-authentication": "~8.0.8",
|
||||
"expo-asset": "~12.0.13",
|
||||
"expo": "^56.0.0",
|
||||
"expo-apple-authentication": "~56.0.4",
|
||||
"expo-asset": "~56.0.24",
|
||||
"expo-av": "~16.0.8",
|
||||
"expo-blur": "~15.0.8",
|
||||
"expo-build-properties": "~1.0.10",
|
||||
"expo-clipboard": "~8.0.8",
|
||||
"expo-constants": "~18.0.14",
|
||||
"expo-dev-client": "~6.0.21",
|
||||
"expo-file-system": "~19.0.24",
|
||||
"expo-font": "~14.0.12",
|
||||
"expo-haptics": "~15.0.8",
|
||||
"expo-image": "~3.0.11",
|
||||
"expo-image-picker": "~17.0.11",
|
||||
"expo-linear-gradient": "~15.0.8",
|
||||
"expo-linking": "~8.0.12",
|
||||
"expo-local-authentication": "~17.0.9",
|
||||
"expo-media-library": "~18.2.1",
|
||||
"expo-notifications": "~0.32.17",
|
||||
"expo-router": "~6.0.24",
|
||||
"expo-secure-store": "~15.0.8",
|
||||
"expo-sharing": "~14.0.8",
|
||||
"expo-status-bar": "~3.0.9",
|
||||
"expo-updates": "~29.0.20",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"expo-blur": "~56.0.4",
|
||||
"expo-build-properties": "~56.0.27",
|
||||
"expo-clipboard": "~56.0.4",
|
||||
"expo-constants": "~56.0.25",
|
||||
"expo-dev-client": "~56.0.26",
|
||||
"expo-file-system": "~56.0.11",
|
||||
"expo-font": "~56.0.7",
|
||||
"expo-haptics": "~56.0.3",
|
||||
"expo-image": "~56.0.12",
|
||||
"expo-image-picker": "~56.0.25",
|
||||
"expo-linear-gradient": "~56.0.4",
|
||||
"expo-linking": "~56.0.17",
|
||||
"expo-local-authentication": "~56.0.5",
|
||||
"expo-media-library": "~56.0.11",
|
||||
"expo-notifications": "~56.0.25",
|
||||
"expo-observe": "~56.0.29",
|
||||
"expo-router": "~56.2.20",
|
||||
"expo-secure-store": "~56.0.4",
|
||||
"expo-sharing": "~56.0.26",
|
||||
"expo-splash-screen": "~56.0.15",
|
||||
"expo-status-bar": "~56.0.4",
|
||||
"expo-system-ui": "~56.0.5",
|
||||
"expo-updates": "~56.0.26",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"react-native": "0.85.3",
|
||||
"react-native-android-widget": "^0.22.1",
|
||||
"react-native-compressor": "^1.13.0",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-gesture-handler": "~2.31.1",
|
||||
"react-native-purchases": "^9.6.12",
|
||||
"react-native-reanimated": "~4.1.1",
|
||||
"react-native-safe-area-context": "~5.6.0",
|
||||
"react-native-screens": "~4.16.0",
|
||||
"react-native-reanimated": "4.3.1",
|
||||
"react-native-safe-area-context": "~5.7.0",
|
||||
"react-native-screens": "~4.26.0",
|
||||
"react-native-share": "^12.3.1",
|
||||
"react-native-svg": "15.12.1",
|
||||
"react-native-svg": "15.15.4",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-view-shot": "^4.0.3",
|
||||
"react-native-view-shot": "5.1.0",
|
||||
"react-native-web": "~0.21.0",
|
||||
"react-native-worklets": "0.5.1",
|
||||
"react-native-worklets": "0.8.3",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -97,15 +99,16 @@
|
||||
"@testing-library/jest-native": "^5.4.3",
|
||||
"@testing-library/react-native": "^13.3.3",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/react": "~19.1.10",
|
||||
"@types/node": "^20.19.0",
|
||||
"@types/react": "~19.2.14",
|
||||
"@types/react-test-renderer": "^19.1.0",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"eoas": "^3.1.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-expo": "~10.0.0",
|
||||
"eslint-config-expo": "~56.0.4",
|
||||
"jest": "^29.7.0",
|
||||
"jest-expo": "~54.0.18",
|
||||
"react-test-renderer": "19.1.0",
|
||||
"typescript": "~5.9.2"
|
||||
"jest-expo": "~56.0.5",
|
||||
"react-test-renderer": "19.2.3",
|
||||
"typescript": "~6.0.3"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
const projectDir = resolve(import.meta.dirname, '..');
|
||||
@@ -140,4 +141,8 @@ export function main(args = process.argv.slice(2), environment = process.env) {
|
||||
run('npx', ['eoas', 'publish', '--branch', branch, ...parsePublishArgs(rawPublishArgs)], exportEnv);
|
||||
}
|
||||
|
||||
if (process.argv[1] === new URL(import.meta.url).pathname) main();
|
||||
// Compare real filesystem paths. A URL pathname percent-encodes spaces, so
|
||||
// comparing it to process.argv[1] silently skips main() under any checkout
|
||||
// whose absolute path contains a space, and every ota:* script then exits 0
|
||||
// having done nothing.
|
||||
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { existsSync, readFileSync, statSync, statfsSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { resolve, join } from 'node:path';
|
||||
|
||||
const projectDir = resolve(import.meta.dirname, '..');
|
||||
const repoRoot = resolve(projectDir, '../..');
|
||||
const certificatePath = 'apps/mobile/certs/certificate.pem';
|
||||
const semverPattern = /^\d+\.\d+\.\d+$/;
|
||||
const minimumFreeBuildBytes = 20 * 1024 ** 3;
|
||||
|
||||
function fail(message) {
|
||||
throw new Error(`Mobile release preflight: ${message}`);
|
||||
}
|
||||
|
||||
function plistValue(source, key, label) {
|
||||
const values = [...source.matchAll(new RegExp(`<key>${key}</key>\\s*<string>([^<]+)</string>`, 'g'))].map((match) => match[1]);
|
||||
if (values.length !== 1) fail(`expected one ${label} ${key}`);
|
||||
return values[0];
|
||||
}
|
||||
|
||||
function allMatches(source, expression, label) {
|
||||
const values = [...source.matchAll(expression)].map((match) => match[1].trim());
|
||||
if (!values.length) fail(`found no ${label}`);
|
||||
return values;
|
||||
}
|
||||
|
||||
function gitStatus(args, root = repoRoot) {
|
||||
const result = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
|
||||
if (result.error) fail(`could not run git ${args.join(' ')}: ${result.error.message}`);
|
||||
return result.status;
|
||||
}
|
||||
|
||||
export function validateReleaseSources({ root = repoRoot, isTracked } = {}) {
|
||||
const file = (relative) => readFileSync(join(root, relative), 'utf8');
|
||||
const app = JSON.parse(file('apps/mobile/app.json')).expo;
|
||||
const appConfig = file('apps/mobile/app.config.js');
|
||||
const androidGradle = file('apps/mobile/android/app/build.gradle');
|
||||
const androidStrings = file('apps/mobile/android/app/src/main/res/values/strings.xml');
|
||||
const xcode = file('apps/mobile/ios/Sauci.xcodeproj/project.pbxproj');
|
||||
const iosInfo = file('apps/mobile/ios/Sauci/Info.plist');
|
||||
const widgetInfo = file('apps/mobile/targets/widget/Info.plist');
|
||||
const iosExpo = file('apps/mobile/ios/Sauci/Supporting/Expo.plist');
|
||||
const publicVersion = app.version;
|
||||
const appConfigRuntime = allMatches(appConfig, /runtimeVersion:\s*['"]([^'"]+)['"]/g, 'explicit app.config.js runtimeVersion');
|
||||
const androidVersion = allMatches(androidGradle, /\bversionName\s+["']([^"']+)["']/g, 'Android versionName');
|
||||
const androidRuntime = allMatches(androidStrings, /<string name="expo_runtime_version">([^<]+)<\/string>/g, 'Android Expo runtime version');
|
||||
const xcodeMarketing = allMatches(xcode, /\bMARKETING_VERSION\s*=\s*([^;]+);/g, 'iOS MARKETING_VERSION settings');
|
||||
const widgetBuildSettings = [...xcode.matchAll(/CURRENT_PROJECT_VERSION\s*=\s*([^;]+);[\s\S]{0,900}?PRODUCT_BUNDLE_IDENTIFIER\s*=\s*com\.sauci\.app\.LiveDrawWidget;/g)].map((match) => match[1].trim());
|
||||
if (widgetBuildSettings.length !== 2) fail('expected Debug and Release LiveDrawWidget CURRENT_PROJECT_VERSION settings');
|
||||
|
||||
const versions = [
|
||||
publicVersion,
|
||||
...appConfigRuntime,
|
||||
...androidVersion,
|
||||
...androidRuntime,
|
||||
...xcodeMarketing,
|
||||
plistValue(iosInfo, 'CFBundleShortVersionString', 'iOS app Info.plist'),
|
||||
plistValue(widgetInfo, 'CFBundleShortVersionString', 'widget Info.plist'),
|
||||
plistValue(iosExpo, 'EXUpdatesRuntimeVersion', 'iOS Expo.plist'),
|
||||
];
|
||||
if (!versions.every((version) => semverPattern.test(version))) fail(`versions must be numeric SemVer: ${versions.join(', ')}`);
|
||||
if (new Set(versions).size !== 1) fail(`public or OTA runtime versions disagree: ${versions.join(', ')}`);
|
||||
|
||||
const appBuild = plistValue(iosInfo, 'CFBundleVersion', 'iOS app Info.plist');
|
||||
const widgetBuild = plistValue(widgetInfo, 'CFBundleVersion', 'widget Info.plist');
|
||||
if (!/^\d+$/.test(appBuild) || !/^\d+$/.test(widgetBuild)) fail('iOS build numbers must be positive integers');
|
||||
if (appBuild !== widgetBuild || widgetBuildSettings.some((build) => build !== appBuild)) {
|
||||
fail(`iOS app, widget, and effective widget Xcode build numbers disagree: app ${appBuild}; widget plist ${widgetBuild}; widget settings ${widgetBuildSettings.join(', ')}`);
|
||||
}
|
||||
|
||||
const tracked = isTracked ?? ((relative) => gitStatus(['ls-files', '--error-unmatch', relative], root) === 0);
|
||||
if (!tracked(certificatePath)) fail(`${certificatePath} must be tracked`);
|
||||
const archiveIgnore = existsSync(join(root, '.easignore')) ? '.easignore' : '.gitignore';
|
||||
if (!existsSync(join(root, archiveIgnore))) fail(`missing root ${archiveIgnore} archive rules`);
|
||||
const archiveRules = file(archiveIgnore).split(/\r?\n/).map((line) => line.trim());
|
||||
const pemRules = archiveRules.map((line, index) => line === '*.pem' ? index : -1).filter((index) => index >= 0);
|
||||
const certificateAllowlist = archiveRules.lastIndexOf(`!${certificatePath}`);
|
||||
if (!pemRules.length) fail(`${archiveIgnore} must retain the private-key-safe *.pem archive rule`);
|
||||
if (certificateAllowlist <= Math.max(...pemRules)) {
|
||||
fail(`${archiveIgnore} must later allowlist !${certificatePath} for the public OTA certificate`);
|
||||
}
|
||||
if (!file(certificatePath).trim()) fail(`${certificatePath} is empty`);
|
||||
|
||||
return { publicVersion, iosBuild: appBuild };
|
||||
}
|
||||
|
||||
export function validateLocalEas(environment = process.env) {
|
||||
const pluginPath = environment.EAS_LOCAL_BUILD_PLUGIN_PATH;
|
||||
if (!pluginPath) fail('EAS_LOCAL_BUILD_PLUGIN_PATH must name the matching local-build plugin executable bin/run');
|
||||
if (!pluginPath.endsWith('/bin/run') || !existsSync(pluginPath) || !statSync(pluginPath).isFile()) {
|
||||
fail('EAS_LOCAL_BUILD_PLUGIN_PATH must point to an existing executable bin/run, not a package directory');
|
||||
}
|
||||
if (!statSync(pluginPath).mode.toString(8).match(/[1357][0-7][0-7]$/)) fail('EAS_LOCAL_BUILD_PLUGIN_PATH bin/run is not executable');
|
||||
}
|
||||
|
||||
export function validateFreeDisk(root = repoRoot, readStats = statfsSync) {
|
||||
const stats = readStats(root);
|
||||
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
||||
if (freeBytes < minimumFreeBuildBytes) {
|
||||
fail(`need at least 20 GiB free before a local build; found ${(freeBytes / 1024 ** 3).toFixed(1)} GiB`);
|
||||
}
|
||||
}
|
||||
|
||||
export function main(args = process.argv.slice(2), environment = process.env) {
|
||||
if (args.length > 1 || (args.length === 1 && args[0] !== '--local-eas')) {
|
||||
fail('usage: node scripts/release-preflight.mjs [--local-eas]');
|
||||
}
|
||||
const result = validateReleaseSources();
|
||||
validateFreeDisk();
|
||||
if (args.includes('--local-eas')) validateLocalEas(environment);
|
||||
console.log(`Mobile release source preflight passed for v${result.publicVersion}, iOS build ${result.iosBuild}`);
|
||||
}
|
||||
|
||||
if (process.argv[1] === new URL(import.meta.url).pathname) main();
|
||||
@@ -0,0 +1,97 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { chmodSync, cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { validateFreeDisk, validateLocalEas, validateReleaseSources } from './release-preflight.mjs';
|
||||
|
||||
const repoRoot = join(import.meta.dirname, '..', '..', '..');
|
||||
|
||||
function fixture() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'sauci-release-preflight-'));
|
||||
cpSync(join(repoRoot, 'apps', 'mobile'), join(root, 'apps', 'mobile'), { recursive: true });
|
||||
cpSync(join(repoRoot, '.gitignore'), join(root, '.gitignore'));
|
||||
return root;
|
||||
}
|
||||
|
||||
function replace(root, relative, from, to) {
|
||||
const file = join(root, relative);
|
||||
writeFileSync(file, readFileSync(file, 'utf8').replace(from, to));
|
||||
}
|
||||
|
||||
function validate(root) {
|
||||
return validateReleaseSources({ root, isTracked: () => true });
|
||||
}
|
||||
|
||||
test('accepts current checked-in mobile release sources', () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
const result = validate(root);
|
||||
assert.match(result.publicVersion, /^\d+\.\d+\.\d+$/);
|
||||
assert.match(result.iosBuild, /^\d+$/);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects a widget Xcode build setting that overrides its plist', () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
const { iosBuild } = validate(root);
|
||||
const project = 'apps/mobile/ios/Sauci.xcodeproj/project.pbxproj';
|
||||
const source = readFileSync(join(root, project), 'utf8');
|
||||
const expected = `CURRENT_PROJECT_VERSION = ${iosBuild};`;
|
||||
const replacement = `CURRENT_PROJECT_VERSION = ${Number(iosBuild) + 1};`;
|
||||
assert.ok(source.includes(expected));
|
||||
replace(root, project, expected, replacement);
|
||||
assert.ok(readFileSync(join(root, project), 'utf8').includes(replacement));
|
||||
assert.throws(() => validate(root), /iOS app, widget, and effective widget Xcode build numbers disagree/);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects an OTA certificate without its root archive allowlist', () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
const archiveRules = readFileSync(join(root, '.gitignore'), 'utf8');
|
||||
assert.ok(archiveRules.includes('!apps/mobile/certs/certificate.pem'));
|
||||
replace(root, '.gitignore', '!apps/mobile/certs/certificate.pem', '');
|
||||
assert.ok(!readFileSync(join(root, '.gitignore'), 'utf8').includes('!apps/mobile/certs/certificate.pem'));
|
||||
assert.throws(
|
||||
() => validate(root),
|
||||
/must later allowlist !apps\/mobile\/certs\/certificate\.pem/,
|
||||
);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('requires the local EAS plugin executable rather than its package directory', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'sauci-eas-plugin-'));
|
||||
const executable = join(root, 'bin-run');
|
||||
try {
|
||||
writeFileSync(executable, '#!/bin/sh\n');
|
||||
chmodSync(executable, 0o755);
|
||||
assert.throws(
|
||||
() => validateLocalEas({ EAS_LOCAL_BUILD_PLUGIN_PATH: root }),
|
||||
/executable bin\/run, not a package directory/,
|
||||
);
|
||||
const bin = join(root, 'bin');
|
||||
mkdirSync(bin);
|
||||
const run = join(bin, 'run');
|
||||
cpSync(executable, run);
|
||||
chmodSync(run, 0o755);
|
||||
validateLocalEas({ EAS_LOCAL_BUILD_PLUGIN_PATH: run });
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('requires enough disk before an expensive local build', () => {
|
||||
assert.throws(
|
||||
() => validateFreeDisk('/fixture', () => ({ bavail: 19, bsize: 1024 ** 3 })),
|
||||
/need at least 20 GiB free/,
|
||||
);
|
||||
validateFreeDisk('/fixture', () => ({ bavail: 20, bsize: 1024 ** 3 }));
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
jest.mock('expo-observe', () => ({
|
||||
Observe: {
|
||||
logEvent: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { Observe } = require('expo-observe');
|
||||
const logEventMock = Observe.logEvent as jest.Mock;
|
||||
|
||||
type ErrorHandler = (error: unknown, isFatal: boolean) => void;
|
||||
|
||||
function withFreshObserveModule<T>(run: (mod: typeof import('@/lib/observe')) => T): T {
|
||||
let result!: T;
|
||||
jest.isolateModules(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const mod = require('@/lib/observe') as typeof import('@/lib/observe');
|
||||
result = run(mod);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function installFakeErrorUtils(previousHandler: jest.Mock) {
|
||||
let installedHandler: ErrorHandler = () => undefined;
|
||||
const globalWithErrorUtils = global as unknown as {
|
||||
ErrorUtils: { getGlobalHandler: () => unknown; setGlobalHandler: (fn: unknown) => void };
|
||||
};
|
||||
globalWithErrorUtils.ErrorUtils = {
|
||||
getGlobalHandler: () => previousHandler,
|
||||
setGlobalHandler: (fn) => {
|
||||
installedHandler = fn as ErrorHandler;
|
||||
},
|
||||
};
|
||||
return () => installedHandler;
|
||||
}
|
||||
|
||||
describe('observe', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('logs app_started only once per module instance', () => {
|
||||
withFreshObserveModule(({ logAppStarted }) => {
|
||||
logAppStarted();
|
||||
logAppStarted();
|
||||
});
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledTimes(1);
|
||||
expect(logEventMock).toHaveBeenCalledWith('app_started');
|
||||
});
|
||||
|
||||
it('does not throw when Observe.logEvent fails', () => {
|
||||
logEventMock.mockImplementationOnce(() => {
|
||||
throw new Error('native module unavailable');
|
||||
});
|
||||
|
||||
withFreshObserveModule(({ logAppStarted }) => {
|
||||
expect(() => logAppStarted()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('wraps the previous ErrorUtils handler, logging xprem_js_crash and delegating', () => {
|
||||
const previousHandler = jest.fn();
|
||||
const getInstalledHandler = installFakeErrorUtils(previousHandler);
|
||||
|
||||
withFreshObserveModule(({ installObserveGlobalErrorHandler }) => {
|
||||
installObserveGlobalErrorHandler();
|
||||
});
|
||||
|
||||
const error = new Error('boom');
|
||||
getInstalledHandler()(error, true);
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
'xprem_js_crash',
|
||||
expect.objectContaining({
|
||||
severity: 'fatal',
|
||||
body: 'boom',
|
||||
})
|
||||
);
|
||||
expect(previousHandler).toHaveBeenCalledWith(error, true);
|
||||
});
|
||||
|
||||
it('does not log xprem_js_crash for non-fatal errors but still delegates', () => {
|
||||
const previousHandler = jest.fn();
|
||||
const getInstalledHandler = installFakeErrorUtils(previousHandler);
|
||||
|
||||
withFreshObserveModule(({ installObserveGlobalErrorHandler }) => {
|
||||
installObserveGlobalErrorHandler();
|
||||
});
|
||||
|
||||
const error = new Error('handled');
|
||||
getInstalledHandler()(error, false);
|
||||
|
||||
expect(logEventMock).not.toHaveBeenCalled();
|
||||
expect(previousHandler).toHaveBeenCalledWith(error, false);
|
||||
});
|
||||
|
||||
it('still calls the previous handler when Observe.logEvent throws', () => {
|
||||
const previousHandler = jest.fn();
|
||||
const getInstalledHandler = installFakeErrorUtils(previousHandler);
|
||||
logEventMock.mockImplementationOnce(() => {
|
||||
throw new Error('native module unavailable');
|
||||
});
|
||||
|
||||
withFreshObserveModule(({ installObserveGlobalErrorHandler }) => {
|
||||
installObserveGlobalErrorHandler();
|
||||
});
|
||||
|
||||
getInstalledHandler()(new Error('boom'), true);
|
||||
|
||||
expect(previousHandler).toHaveBeenCalledWith(expect.any(Error), true);
|
||||
});
|
||||
});
|
||||
@@ -310,7 +310,7 @@ const styles = StyleSheet.create({
|
||||
color: colors.textTertiary,
|
||||
},
|
||||
lockOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -307,7 +307,7 @@ export const MatchConfetti: React.FC<Props> = ({ visible, onAnimationComplete })
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
zIndex: 1000,
|
||||
},
|
||||
particle: {
|
||||
|
||||
@@ -344,7 +344,7 @@ export const MatchNotificationModal: React.FC<Props> = ({
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.75)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
@@ -377,7 +377,7 @@ const styles = StyleSheet.create({
|
||||
borderColor: colors.border,
|
||||
},
|
||||
flatBackground: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: colors.backgroundLight,
|
||||
},
|
||||
// Removed blur/gradient styles
|
||||
|
||||
@@ -269,7 +269,7 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.85)",
|
||||
},
|
||||
content: {
|
||||
|
||||
@@ -101,7 +101,7 @@ const styles = StyleSheet.create({
|
||||
position: 'relative',
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
||||
borderRadius: radius.lg,
|
||||
pointerEvents: 'none',
|
||||
|
||||
@@ -115,7 +115,7 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '700',
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
||||
borderRadius: radius.lg,
|
||||
pointerEvents: 'none',
|
||||
|
||||
@@ -110,7 +110,7 @@ const styles = StyleSheet.create({
|
||||
marginTop: 2,
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
||||
borderRadius: radius.lg,
|
||||
pointerEvents: 'none',
|
||||
|
||||
@@ -90,7 +90,7 @@ const styles = StyleSheet.create({
|
||||
marginTop: 2,
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
||||
borderRadius: radius.lg,
|
||||
pointerEvents: 'none',
|
||||
|
||||
@@ -336,7 +336,7 @@ const styles = StyleSheet.create({
|
||||
zIndex: 1000,
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
},
|
||||
keyboardView: {
|
||||
|
||||
@@ -615,7 +615,7 @@ const styles = StyleSheet.create({
|
||||
borderColor: colors.premium.goldLight,
|
||||
},
|
||||
annualBackground: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: colors.premium.goldLight,
|
||||
opacity: 0.3,
|
||||
},
|
||||
@@ -804,7 +804,7 @@ const styles = StyleSheet.create({
|
||||
|
||||
// Guest confirm modal
|
||||
guestConfirmOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
@@ -131,7 +131,7 @@ const styles = StyleSheet.create({
|
||||
padding: spacing.lg,
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
||||
},
|
||||
modalContainer: {
|
||||
|
||||
@@ -629,7 +629,7 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||
},
|
||||
sheetContainer: {
|
||||
|
||||
@@ -86,7 +86,7 @@ const styles = StyleSheet.create({
|
||||
padding: spacing.lg,
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
header: {
|
||||
|
||||
@@ -19,7 +19,7 @@ import Animated, {
|
||||
} from "react-native-reanimated";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import ViewShot from "react-native-view-shot";
|
||||
import ViewShot, { type ViewShotRef } from "react-native-view-shot";
|
||||
import * as Sharing from "expo-sharing";
|
||||
import { colors, typography, spacing, blur } from "../../theme";
|
||||
import { shareToInstagramStories } from "../../lib/instagramShare";
|
||||
@@ -50,7 +50,7 @@ export function SharePreviewModal({
|
||||
const insets = useSafeAreaInsets();
|
||||
const { width: screenWidth } = useWindowDimensions();
|
||||
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH);
|
||||
const shareViewRef = useRef<ViewShot>(null);
|
||||
const shareViewRef = useRef<ViewShotRef>(null);
|
||||
const [capturedUri, setCapturedUri] = useState<string | null>(null);
|
||||
const [isCapturing, setIsCapturing] = useState(false);
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ export default function MatchesTutorial({ onComplete }: Props) {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
zIndex: 1000,
|
||||
},
|
||||
blur: {
|
||||
|
||||
@@ -152,7 +152,7 @@ const styles = StyleSheet.create({
|
||||
padding: spacing.md,
|
||||
},
|
||||
fullScreenContainer: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(13, 13, 26, 0.85)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
@@ -118,7 +118,7 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
},
|
||||
glowContainer: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
},
|
||||
edgeGlowTop: {
|
||||
position: 'absolute',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { View, StyleSheet, KeyboardAvoidingView, Platform, Alert } from "react-native";
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useFocusEffect, useIsFocused } from "@react-navigation/native";
|
||||
import { useLocalSearchParams, useRouter, useFocusEffect, useIsFocused } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import Animated, { FadeInDown } from "react-native-reanimated";
|
||||
|
||||
@@ -159,7 +159,7 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||
},
|
||||
sheetContainer: {
|
||||
|
||||
@@ -203,13 +203,13 @@ const styles = StyleSheet.create({
|
||||
marginTop: spacing.xs,
|
||||
},
|
||||
loadingOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
playOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
@@ -220,7 +220,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
// ...
|
||||
inputFieldBorder: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
borderRadius: radius.xl,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
|
||||
@@ -790,7 +790,7 @@ const styles = StyleSheet.create({
|
||||
height: '100%',
|
||||
},
|
||||
photoLoadingOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -179,7 +179,7 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||
},
|
||||
sheetContainer: {
|
||||
|
||||
@@ -125,7 +125,7 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||
},
|
||||
thumbnail: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
},
|
||||
iconContainer: {
|
||||
width: 64,
|
||||
@@ -136,7 +136,7 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, View } from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useIsFocused } from "@react-navigation/native";
|
||||
import { useIsFocused } from "expo-router";
|
||||
import Animated, { FadeIn } from "react-native-reanimated";
|
||||
import { GradientBackground } from "../../components/ui";
|
||||
import { Paywall } from "../../components/paywall";
|
||||
|
||||
@@ -7,8 +7,7 @@ import {
|
||||
Alert,
|
||||
useWindowDimensions,
|
||||
} from 'react-native';
|
||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import { useLocalSearchParams, useRouter, useIsFocused } from 'expo-router';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
|
||||
@@ -21,7 +21,7 @@ import Animated, {
|
||||
} from 'react-native-reanimated';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import ViewShot from 'react-native-view-shot';
|
||||
import ViewShot, { type ViewShotRef } from 'react-native-view-shot';
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import { colors, gradients, radius, typography, spacing, blur } from '../../../theme';
|
||||
@@ -44,7 +44,7 @@ export function DrawingShareModal({
|
||||
const insets = useSafeAreaInsets();
|
||||
const { width: screenWidth } = useWindowDimensions();
|
||||
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH);
|
||||
const shareViewRef = useRef<ViewShot>(null);
|
||||
const shareViewRef = useRef<ViewShotRef>(null);
|
||||
const [capturedUri, setCapturedUri] = useState<string | null>(null);
|
||||
const [isCapturing, setIsCapturing] = useState(false);
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
},
|
||||
proBorderGradient: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
opacity: 0.3,
|
||||
},
|
||||
proCardInner: {
|
||||
@@ -330,7 +330,7 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
},
|
||||
freeBorderGradient: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
},
|
||||
|
||||
freeHeader: {
|
||||
|
||||
@@ -51,7 +51,7 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.08)",
|
||||
},
|
||||
content: {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { BlurView } from "expo-blur";
|
||||
import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown } from "react-native-reanimated";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import ViewShot from "react-native-view-shot";
|
||||
import ViewShot, { type ViewShotRef } from "react-native-view-shot";
|
||||
import * as Sharing from "expo-sharing";
|
||||
import { colors, spacing, typography, blur } from "../../../theme";
|
||||
import { shareToInstagramStories } from "../../../lib/instagramShare";
|
||||
@@ -32,7 +32,7 @@ export function QuizShareModal({ visible, onClose, scorePercent }: QuizShareModa
|
||||
const insets = useSafeAreaInsets();
|
||||
const { width: screenWidth } = useWindowDimensions();
|
||||
const cardWidth = Math.min(screenWidth - 64, MAX_CARD_WIDTH);
|
||||
const shareViewRef = useRef<ViewShot>(null);
|
||||
const shareViewRef = useRef<ViewShotRef>(null);
|
||||
const [capturedUri, setCapturedUri] = useState<string | null>(null);
|
||||
const [isCapturing, setIsCapturing] = useState(false);
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
},
|
||||
premiumButtonGradient: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...StyleSheet.absoluteFill,
|
||||
},
|
||||
premiumButtonContent: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -50,8 +50,6 @@ jest.mock('@supabase/supabase-js', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper', () => ({}), { virtual: true });
|
||||
|
||||
jest.mock('react-native-reanimated', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Reanimated = require('react-native-reanimated/mock');
|
||||
@@ -103,6 +101,11 @@ jest.mock('expo-image-picker', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('expo-media-library', () => ({
|
||||
usePermissions: jest.fn(() => [{ granted: true }, jest.fn(async () => ({ granted: true }))]),
|
||||
createAssetAsync: jest.fn(async () => ({ id: 'mock-asset-id' })),
|
||||
}));
|
||||
|
||||
jest.mock('expo-file-system/legacy', () => ({
|
||||
readAsStringAsync: jest.fn(async () => ''),
|
||||
EncodingType: {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.sauci.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -16,6 +16,10 @@
|
||||
"declarationMap": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-native",
|
||||
"types": [
|
||||
"node",
|
||||
"jest"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
|
||||
@@ -153,10 +153,17 @@ regenerate and review the native diff separately:
|
||||
|
||||
```bash
|
||||
cd apps/mobile
|
||||
RELEASE_CHANNEL=production npx expo prebuild
|
||||
RELEASE_CHANNEL=production npx expo prebuild --clean
|
||||
git diff # retain only reviewed expo-updates native changes
|
||||
```
|
||||
|
||||
`--clean` is required as of `@bacons/apple-targets` 5.x: running `expo prebuild`
|
||||
without it against an already-generated iOS project fails inside the widget
|
||||
target's Xcode project update step (`withIosXcodeProjectBeta2BaseMod`). A clean
|
||||
regeneration from the committed source avoids that failure. Run `pod install`
|
||||
in `apps/mobile/ios` afterwards to refresh `Podfile.lock` and the `.xcworkspace`
|
||||
before committing.
|
||||
|
||||
A build produced before this native source change will not check for updates.
|
||||
|
||||
## Publishing an update
|
||||
|
||||