From 54a2eee8276e70345f5940a79c6e974846d0bab7 Mon Sep 17 00:00:00 2001 From: Utku <74243531+utkubakir@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:12:28 +0300 Subject: [PATCH] Mobile CI with Tests (#631) * stuff * stuff (but for android) * test mobile ci * move pnpm up * install ndk * add ios & cleanup * onboarding flow test * test ci * fixes, cleanup, caches * why you do this cargo * fix pnpm-lock * add path to build rust script? * ci is fun * yolo * fix broken flow.. * fix pnpm * probably not gonna work * test x2 * use real branch of pcr * android emulator and try ios x2 * Use react native architectures, instead of all. * override architecture to speed up android build * protoc & build android on macos too * fix java ndk * android gradle * disable ios for now * use simulator sdk & debug configuration on ios build * cleanup * avd test * fix avd settings. * only build for x86_64 on ci * Fix ios build * Add IOS testing * maestro script * ios release build & wait for library creation * clean up and disable android for now * fix pnpm-lock * Add concurrency to cancel previous runs * fix pnpm-lock --------- Co-authored-by: Brendan Allan --- .github/scripts/setup-system.sh | 1 + .github/workflows/ci.yml | 5 + .github/workflows/mobile-ci.yml | 211 ++++++++++++++++++ .vscode/settings.json | 6 +- Cargo.lock | 6 +- Cargo.toml | 6 +- apps/mobile/.gitignore | 11 +- apps/mobile/android/app/build.gradle | 35 +-- .../android/app/src/main/AndroidManifest.xml | 26 ++- .../android/app/src/main/jni/Android.mk | 48 ---- .../src/main/res/drawable/splashscreen.xml | 3 +- .../app/src/main/res/values/strings.xml | 3 - apps/mobile/android/build.gradle | 3 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 60756 bytes apps/mobile/eas.json | 29 --- apps/mobile/ios/Podfile | 2 +- apps/mobile/ios/Podfile.lock | 6 +- .../ios/Spacedrive.xcodeproj/project.pbxproj | 10 +- .../xcschemes/Spacedrive.xcscheme | 21 +- apps/mobile/ios/Spacedrive/AppDelegate.h | 2 +- apps/mobile/ios/Spacedrive/AppDelegate.mm | 2 +- .../ios/Spacedrive/Supporting/Expo.plist | 2 +- apps/mobile/ios/Spacedrive/main.m | 3 +- apps/mobile/ios/build-rust.sh | 23 +- apps/mobile/package.json | 8 +- apps/mobile/scripts/postinstall.js | 19 -- apps/mobile/scripts/run-maestro-tests | 50 +++++ apps/mobile/src/components/header/Header.tsx | 6 +- .../src/screens/onboarding/MasterPassword.tsx | 9 +- .../src/screens/onboarding/NewLibrary.tsx | 1 + .../settings/client/LibrarySettings.tsx | 6 +- .../library/LibraryGeneralSettings.tsx | 6 +- apps/mobile/tests/onboarding.yml | 26 +++ apps/mobile/tsconfig.json | 1 + pnpm-lock.yaml | 140 ++++-------- 35 files changed, 451 insertions(+), 285 deletions(-) create mode 100644 .github/workflows/mobile-ci.yml delete mode 100644 apps/mobile/android/app/src/main/jni/Android.mk delete mode 100644 apps/mobile/eas.json delete mode 100644 apps/mobile/scripts/postinstall.js create mode 100644 apps/mobile/scripts/run-maestro-tests create mode 100644 apps/mobile/tests/onboarding.yml diff --git a/.github/scripts/setup-system.sh b/.github/scripts/setup-system.sh index c44ce0029..6dff259bc 100755 --- a/.github/scripts/setup-system.sh +++ b/.github/scripts/setup-system.sh @@ -58,6 +58,7 @@ if [ "${1:-}" == "mobile" ]; then rustup target add aarch64-apple-ios rustup target add aarch64-apple-ios-sim + rustup target add x86_64-apple-ios # for CI fi # Android requires python diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 200345b60..07e796794 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,11 @@ on: env: SPACEDRIVE_CUSTOM_APT_FLAGS: --no-install-recommends +# Cancel previous runs of the same workflow on the same branch. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: typescript: name: TypeScript diff --git a/.github/workflows/mobile-ci.yml b/.github/workflows/mobile-ci.yml new file mode 100644 index 000000000..9e35aa159 --- /dev/null +++ b/.github/workflows/mobile-ci.yml @@ -0,0 +1,211 @@ +name: Mobile CI + +on: + pull_request: + push: + branches: + - main + paths-ignore: + - '**/.md' + workflow_dispatch: + +env: + SPACEDRIVE_CUSTOM_APT_FLAGS: --no-install-recommends + SPACEDRIVE_CI: '1' + +# Cancel previous runs of the same workflow on the same branch. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # Disabled until I can figure out why our app on x86_64 crashes on startup. + # android: + # name: Android + # runs-on: macos-12 + # steps: + # - name: Checkout repository + # uses: actions/checkout@v3 + + # - name: Setup Java JDK + # uses: actions/setup-java@v3.10.0 + # with: + # java-version: '18' + # distribution: 'temurin' + + # - name: Install pnpm + # uses: pnpm/action-setup@v2.2.2 + # with: + # version: 7.x.x + + # - name: Install Node.js + # uses: actions/setup-node@v3 + # with: + # node-version: 18 + # cache: 'pnpm' + + # - name: Install Rust stable + # uses: actions-rs/toolchain@v1 + # with: + # toolchain: stable + # profile: minimal + + # - name: Cache Rust deps + # uses: Swatinem/rust-cache@v2 + # with: + # save-if: ${{ inputs.save-cache }} == "true" + + # - name: Run 'setup-system.sh' script + # shell: bash + # run: ./.github/scripts/setup-system.sh mobile + + # - name: Generate Prisma client + # uses: ./.github/actions/generate-prisma-client + + # - name: Install pnpm dependencies + # run: pnpm i --frozen-lockfile + + # - name: Setup Android SDK Tools + # uses: android-actions/setup-android@v2.0.2 + + # - name: Cache NDK + # uses: actions/cache@v3 + # with: + # path: ${{ env.ANDROID_HOME }}/ndk/23.1.7779620 + # key: ndk-23.1.7779620 + + # - name: Install NDK + # run: echo "y" | sudo ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "ndk;23.1.7779620" + + # - name: Cache Gradle + # uses: gradle/gradle-build-action@v2 + + # - name: Build Android + # working-directory: ./apps/mobile/android + # run: chmod +x ./gradlew && ./gradlew assembleRelease -PreactNativeArchitectures=x86_64 --no-daemon + + # - name: Cache AVD + # uses: actions/cache@v3 + # id: avd-cache + # with: + # path: | + # ~/.android/avd/* + # ~/.android/adb* + # key: avd-30 + + # - name: Generate AVD Snapshot + # if: steps.avd-cache.outputs.cache-hit != 'true' + # uses: ReactiveCircus/android-emulator-runner@v2.28.0 + # with: + # arch: x86_64 + # api-level: 30 + # target: google_apis + # ndk: 23.1.7779620 + # ram-size: 4096M + # emulator-boot-timeout: 12000 + # force-avd-creation: false + # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + # disable-animations: false + # script: echo "Generated AVD snapshot." + + # - name: Install Maestro + # run: | + # # workaround for https://github.com/mobile-dev-inc/maestro/issues/877 + # export MAESTRO_VERSION=1.21.3; curl -Ls "https://get.maestro.mobile.dev" | bash + # echo "$HOME/.maestro/bin" >> $GITHUB_PATH + + # - name: Run Tests + # uses: ReactiveCircus/android-emulator-runner@v2.28.0 + # with: + # arch: x86_64 + # api-level: 30 + # target: google_apis + # ndk: 23.1.7779620 + # ram-size: 4096M + # emulator-boot-timeout: 12000 + # force-avd-creation: false + # emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + # disable-animations: true + # script: | + # adb install -r apps/mobile/android/app/build/outputs/apk/release/app-release.apk + # adb wait-for-device + # bash ./apps/mobile/scripts/run-maestro-tests android + + ios: + name: iOS + runs-on: macos-12 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Xcode + uses: maxim-lobanov/setup-xcode@v1.5.1 + with: + xcode-version: latest-stable + + - name: Install pnpm + uses: pnpm/action-setup@v2.2.2 + with: + version: 7.x.x + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + + - name: Cache Rust deps + uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ inputs.save-cache }} == "true" + + - name: Run 'setup-system.sh' script + shell: bash + run: ./.github/scripts/setup-system.sh mobile + + - name: Generate Prisma client + uses: ./.github/actions/generate-prisma-client + + - name: Install pnpm dependencies + run: pnpm i --frozen-lockfile + + - name: Cache Pods + uses: actions/cache@v3 + with: + path: | + ./apps/mobile/ios/Pods + ~/Library/Caches/CocoaPods + ~/.cocoapods + key: pods-${{ hashFiles('./apps/mobile/ios/Podfile.lock') }} + restore-keys: pods- + + - name: Install Pods + working-directory: ./apps/mobile/ios + run: pod install --repo-update + + - name: Build iOS + working-directory: ./apps/mobile/ios + run: xcodebuild -workspace ./Spacedrive.xcworkspace -scheme Spacedrive -configuration Release -sdk iphonesimulator -derivedDataPath build -arch x86_64 + + - name: Install Maestro + run: | + curl -Ls "https://get.maestro.mobile.dev" | bash + brew tap facebook/fb + brew install facebook/fb/idb-companion + echo "$HOME/.maestro/bin" >> $GITHUB_PATH + + - name: Run Simulator + uses: futureware-tech/simulator-action@v2 + with: + model: 'iPhone 11' + + - name: Run Tests + run: | + xcrun simctl install booted apps/mobile/ios/build/Build/Products/Release-iphonesimulator/Spacedrive.app + bash ./apps/mobile/scripts/run-maestro-tests ios diff --git a/.vscode/settings.json b/.vscode/settings.json index ee5885290..daff849f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -48,10 +48,10 @@ "search.exclude": { "**/node_modules": true, "**/bower_components": true, - "**/*.code-search": true, + "**/*.code-search": true // Hiding these folders bcs they create a lot of noise in the search results - "apps/mobile/android": true, - "apps/mobile/ios": true + // "apps/mobile/android": true + // "apps/mobile/ios": true }, "eslint.workingDirectories": [ "apps/landing", diff --git a/Cargo.lock b/Cargo.lock index b438add4b..90a89b38a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5491,7 +5491,7 @@ dependencies = [ [[package]] name = "prisma-client-rust" version = "0.6.4" -source = "git+https://github.com/Brendonovich/prisma-client-rust?rev=c965b89f1a07a6931d90f4b5556421f7ffcda03b#c965b89f1a07a6931d90f4b5556421f7ffcda03b" +source = "git+https://github.com/Brendonovich/prisma-client-rust?branch=spacedrive#c965b89f1a07a6931d90f4b5556421f7ffcda03b" dependencies = [ "base64 0.13.1", "bigdecimal", @@ -5525,7 +5525,7 @@ dependencies = [ [[package]] name = "prisma-client-rust-cli" version = "0.6.4" -source = "git+https://github.com/Brendonovich/prisma-client-rust?rev=c965b89f1a07a6931d90f4b5556421f7ffcda03b#c965b89f1a07a6931d90f4b5556421f7ffcda03b" +source = "git+https://github.com/Brendonovich/prisma-client-rust?branch=spacedrive#c965b89f1a07a6931d90f4b5556421f7ffcda03b" dependencies = [ "directories", "flate2", @@ -5545,7 +5545,7 @@ dependencies = [ [[package]] name = "prisma-client-rust-sdk" version = "0.6.4" -source = "git+https://github.com/Brendonovich/prisma-client-rust?rev=c965b89f1a07a6931d90f4b5556421f7ffcda03b#c965b89f1a07a6931d90f4b5556421f7ffcda03b" +source = "git+https://github.com/Brendonovich/prisma-client-rust?branch=spacedrive#c965b89f1a07a6931d90f4b5556421f7ffcda03b" dependencies = [ "convert_case 0.5.0", "dml", diff --git a/Cargo.toml b/Cargo.toml index d1b462eca..82e2188a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,19 +13,19 @@ members = [ ] [workspace.dependencies] -prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust", rev = "c965b89f1a07a6931d90f4b5556421f7ffcda03b", features = [ +prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust", branch = "spacedrive", features = [ "rspc", "sqlite-create-many", "migrations", "sqlite", ], default-features = false } -prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust", rev = "c965b89f1a07a6931d90f4b5556421f7ffcda03b", features = [ +prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust", branch = "spacedrive", features = [ "rspc", "sqlite-create-many", "migrations", "sqlite", ], default-features = false } -prisma-client-rust-sdk = { git = "https://github.com/Brendonovich/prisma-client-rust", rev = "c965b89f1a07a6931d90f4b5556421f7ffcda03b", features = [ +prisma-client-rust-sdk = { git = "https://github.com/Brendonovich/prisma-client-rust", branch = "spacedrive", features = [ "sqlite", ], default-features = false } diff --git a/apps/mobile/.gitignore b/apps/mobile/.gitignore index 6b0b6caab..6ea85667f 100644 --- a/apps/mobile/.gitignore +++ b/apps/mobile/.gitignore @@ -30,6 +30,9 @@ build/ local.properties *.iml *.hprof +.cxx/ +*.keystore +!debug.keystore # node.js # @@ -43,10 +46,10 @@ yarn-error.log # CocoaPods /ios/Pods/ +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* + # Expo .expo/ web-build/ -dist/ - -# Temporary files created by Metro to check the health of the file watcher -.metro-health-check* \ No newline at end of file +dist/ \ No newline at end of file diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle index c12552698..05b7395ad 100644 --- a/apps/mobile/android/app/build.gradle +++ b/apps/mobile/android/app/build.gradle @@ -3,18 +3,25 @@ apply plugin: "com.facebook.react" import com.android.build.OutputFile - // SPACEDRIVE CODE + apply plugin: 'org.mozilla.rust-android-gradle.rust-android' +def cargoTargets = [] + +if (System.getenv('SPACEDRIVE_CI') == "1") { + cargoTargets = ["x86_64"] +} else { + cargoTargets = ["arm", "arm64", "x86", "x86_64"] +} + + cargo { module = "../../crates/android" libname = "sd_mobile_android" pythonCommand = 'python3' - profile = 'release' - targets = ["arm", "arm64", "x86", "x86_64"] - // profile = 'debug' - // targets = ["arm64"] + profile = 'release' // 'debug' + targets = cargoTargets targetDirectory = "../.././../../target" // Monorepo moment } @@ -25,7 +32,7 @@ tasks.whenTaskAdded { task -> } } -// SPACEDRIVE CODE END +// END SPACEDRIVE CODE def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() def expoDebuggableVariants = ['debug'] @@ -46,11 +53,6 @@ react { 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" debuggableVariants = expoDebuggableVariants - - // Use Expo CLI to bundle the app, this ensures the Metro config - // works correctly with Expo projects. - cliFile = new File(["node", "--print", "require.resolve('@expo/cli')"].execute(null, rootDir).text.trim()) - bundleCommand = "export:embed" /* Folders */ // The root of your project, i.e. where "package.json" lives. Default is '..' @@ -59,6 +61,8 @@ react { // reactNativeDir = file("../node_modules/react-native") // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen // codegenDir = file("../node_modules/react-native-codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js + // cliFile = file("../node_modules/react-native/cli.js") /* Variants */ // The list of variants to that are debuggable. For those we're going to @@ -69,7 +73,9 @@ react { /* Bundling */ // A list containing the node command and its flags. Default is just 'node'. // nodeExecutableAndArgs = ["node"] - + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" // // The path to the CLI configuration file. Default is empty. // bundleConfig = file(../rn-cli.config.js) @@ -138,8 +144,9 @@ android { compileSdkVersion rootProject.ext.compileSdkVersion + namespace 'com.spacedrive.app' defaultConfig { - applicationId "com.spacedrive.app" + applicationId 'com.spacedrive.app' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 @@ -257,4 +264,4 @@ dependencies { } apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle"); -applyNativeModulesAppBuildGradle(project) \ No newline at end of file +applyNativeModulesAppBuildGradle(project) diff --git a/apps/mobile/android/app/src/main/AndroidManifest.xml b/apps/mobile/android/app/src/main/AndroidManifest.xml index 5c035d5a5..23d2b8676 100644 --- a/apps/mobile/android/app/src/main/AndroidManifest.xml +++ b/apps/mobile/android/app/src/main/AndroidManifest.xml @@ -1,10 +1,10 @@ + - - + @@ -13,13 +13,22 @@ - + - - + + - - + + @@ -32,6 +41,7 @@ - + \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/jni/Android.mk b/apps/mobile/android/app/src/main/jni/Android.mk deleted file mode 100644 index 57530bb1d..000000000 --- a/apps/mobile/android/app/src/main/jni/Android.mk +++ /dev/null @@ -1,48 +0,0 @@ -THIS_DIR := $(call my-dir) - -include $(REACT_ANDROID_DIR)/Android-prebuilt.mk - -# If you wish to add a custom TurboModule or Fabric component in your app you -# will have to include the following autogenerated makefile. -# include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk -include $(CLEAR_VARS) - -LOCAL_PATH := $(THIS_DIR) - -# You can customize the name of your application .so file here. -LOCAL_MODULE := spacedrive_appmodules - -LOCAL_C_INCLUDES := $(LOCAL_PATH) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) - -# If you wish to add a custom TurboModule or Fabric component in your app you -# will have to uncomment those lines to include the generated source -# files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni) -# -# LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni -# LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp) -# LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni - -# Here you should add any native library you wish to depend on. -LOCAL_SHARED_LIBRARIES := \ - libfabricjni \ - libfbjni \ - libfolly_runtime \ - libglog \ - libjsi \ - libreact_codegen_rncore \ - libreact_debug \ - libreact_nativemodule_core \ - libreact_render_componentregistry \ - libreact_render_core \ - libreact_render_debug \ - libreact_render_graphics \ - librrc_view \ - libruntimeexecutor \ - libturbomodulejsijni \ - libyoga - -LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall - -include $(BUILD_SHARED_LIBRARY) diff --git a/apps/mobile/android/app/src/main/res/drawable/splashscreen.xml b/apps/mobile/android/app/src/main/res/drawable/splashscreen.xml index c8568e162..f7856616e 100644 --- a/apps/mobile/android/app/src/main/res/drawable/splashscreen.xml +++ b/apps/mobile/android/app/src/main/res/drawable/splashscreen.xml @@ -1,3 +1,4 @@ + - + \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/values/strings.xml b/apps/mobile/android/app/src/main/res/values/strings.xml index 291d93fd7..4f34679cd 100644 --- a/apps/mobile/android/app/src/main/res/values/strings.xml +++ b/apps/mobile/android/app/src/main/res/values/strings.xml @@ -1,6 +1,3 @@ Spacedrive - contain - false - automatic \ No newline at end of file diff --git a/apps/mobile/android/build.gradle b/apps/mobile/android/build.gradle index c05852a6e..4d76b78e9 100644 --- a/apps/mobile/android/build.gradle +++ b/apps/mobile/android/build.gradle @@ -1,5 +1,3 @@ -import org.apache.tools.ant.taskdefs.condition.Os - // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { @@ -34,7 +32,6 @@ buildscript { allprojects { repositories { - mavenLocal() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android')) diff --git a/apps/mobile/android/gradle/wrapper/gradle-wrapper.jar b/apps/mobile/android/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10158 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 delta 9041 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 diff --git a/apps/mobile/eas.json b/apps/mobile/eas.json deleted file mode 100644 index ba5b5924f..000000000 --- a/apps/mobile/eas.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "cli": { - "version": ">= 0.56.0", - "promptToConfigurePushNotifications": false - }, - "build": { - "production": { - "node": "18.12.1" - }, - "preview": { - "extends": "production", - "distribution": "internal" - }, - "development": { - "extends": "production", - "developmentClient": true, - "distribution": "internal", - "android": { - "gradleCommand": ":app:assembleDebug" - }, - "ios": { - "buildConfiguration": "Debug" - } - } - }, - "submit": { - "production": {} - } -} diff --git a/apps/mobile/ios/Podfile b/apps/mobile/ios/Podfile index 499f54880..48d3aa435 100644 --- a/apps/mobile/ios/Podfile +++ b/apps/mobile/ios/Podfile @@ -86,4 +86,4 @@ target 'Spacedrive' do Pod::UI.warn e end end -end \ No newline at end of file +end diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock index 56e28f269..019d64499 100644 --- a/apps/mobile/ios/Podfile.lock +++ b/apps/mobile/ios/Podfile.lock @@ -9,7 +9,7 @@ PODS: - ExpoModulesCore - EXFont (11.1.1): - ExpoModulesCore - - EXMediaLibrary (15.2.2): + - EXMediaLibrary (15.2.3): - ExpoModulesCore - React-Core - Expo (48.0.6): @@ -609,7 +609,7 @@ SPEC CHECKSUMS: EXConstants: f348da07e21b23d2b085e270d7b74f282df1a7d9 EXFileSystem: 844e86ca9b5375486ecc4ef06d3838d5597d895d EXFont: 6ea3800df746be7233208d80fe379b8ed74f4272 - EXMediaLibrary: 792fe9b828b5bfa2c5a8b629730f175af2938285 + EXMediaLibrary: 587cd8aad27a6fc8d7c38b950bc75bc1845a7480 Expo: 04ba1ddde0be07aff4306ae636a1804810679145 ExpoKeepAwake: 69f5f627670d62318410392d03e0b5db0f85759a ExpoModulesCore: 1667335d4f4c9b7801990930e6f0eea42c916a21 @@ -661,6 +661,6 @@ SPEC CHECKSUMS: RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2 Yoga: 5ed1699acbba8863755998a4245daa200ff3817b -PODFILE CHECKSUM: 17065850599b1e955efad7a619cf825c012744d7 +PODFILE CHECKSUM: a82d2337be62e4c4d092a59fb73875196b22ca98 COCOAPODS: 1.11.3 diff --git a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj index 8d11b30da..f17cc678a 100644 --- a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj @@ -147,8 +147,8 @@ buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Spacedrive" */; buildPhases = ( BC1F37E21575379C81F561DC /* [CP] Check Pods Manifest.lock */, - FD10A7F022414F080027D42C /* Start Packager */, 350DC403297BF2B8009CD6A1 /* Build Spacedrive Core */, + FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, @@ -222,7 +222,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\n`node --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n"; + shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" $PROJECT_ROOT ios relative | tail -n 1)\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; }; 350DC403297BF2B8009CD6A1 /* Build Spacedrive Core */ = { isa = PBXShellScriptBuildPhase; @@ -241,7 +241,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/zsh; - shellScript = "env -i CONFIGURATION=$CONFIGURATION PLATFORM_NAME=$PLATFORM_NAME ./build-rust.sh\n"; + shellScript = "env -i SPACEDRIVE_CI=$SPACEDRIVE_CI CONFIGURATION=$CONFIGURATION PLATFORM_NAME=$PLATFORM_NAME ./build-rust.sh\n"; }; 65CB42BC732A49C59CCA544A /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -299,7 +299,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > `node --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/.packager.env'\"`\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open `node --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/launchPackager.command'\"` || echo \"Can't start packager automatically\"\n fi\nfi\n"; + shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\nexport RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > `$NODE_BINARY --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/.packager.env'\"`\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open `$NODE_BINARY --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/launchPackager.command'\"` || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; FFF29AD1ADC998E2209017DB /* [CP] Copy Pods Resources */ = { @@ -432,6 +432,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.spacedrive.app; PRODUCT_NAME = Spacedrive; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; @@ -527,6 +528,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.spacedrive.app; PRODUCT_NAME = Spacedrive; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; diff --git a/apps/mobile/ios/Spacedrive.xcodeproj/xcshareddata/xcschemes/Spacedrive.xcscheme b/apps/mobile/ios/Spacedrive.xcodeproj/xcshareddata/xcschemes/Spacedrive.xcscheme index 5758d3c28..f1d422f56 100644 --- a/apps/mobile/ios/Spacedrive.xcodeproj/xcshareddata/xcschemes/Spacedrive.xcscheme +++ b/apps/mobile/ios/Spacedrive.xcodeproj/xcshareddata/xcschemes/Spacedrive.xcscheme @@ -28,16 +28,6 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - - - - + EXUpdatesSDKVersion 48.0.0 EXUpdatesURL - https://exp.host/@utkudev/spacedrive + https://exp.host/@spacedrive/spacedrive \ No newline at end of file diff --git a/apps/mobile/ios/Spacedrive/main.m b/apps/mobile/ios/Spacedrive/main.m index 44e059c6d..25181b6cc 100644 --- a/apps/mobile/ios/Spacedrive/main.m +++ b/apps/mobile/ios/Spacedrive/main.m @@ -6,4 +6,5 @@ int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } -} \ No newline at end of file +} + diff --git a/apps/mobile/ios/build-rust.sh b/apps/mobile/ios/build-rust.sh index be81cd494..35e593020 100755 --- a/apps/mobile/ios/build-rust.sh +++ b/apps/mobile/ios/build-rust.sh @@ -2,8 +2,6 @@ set -e -export PROTOC=/opt/homebrew/bin/protoc - TARGET_DIRECTORY=../../../target CARGO_FLAGS= @@ -11,6 +9,27 @@ if [[ $CONFIGURATION != "Debug" ]]; then CARGO_FLAGS=--release fi +# TODO: Also do this for non-Apple Silicon Macs +if [[ $SPACEDRIVE_CI == "1" ]]; then + + # Required for CI + export PATH="$HOME/.cargo/bin:$PATH" + export PROTOC=/usr/local/bin/protoc + + cargo build -p sd-mobile-ios --target x86_64-apple-ios + + if [[ $PLATFORM_NAME = "iphonesimulator" ]] + then + lipo -create -output $TARGET_DIRECTORY/libsd_mobile_ios-iossim.a $TARGET_DIRECTORY/x86_64-apple-ios/debug/libsd_mobile_ios.a + else + lipo -create -output $TARGET_DIRECTORY/libsd_mobile_ios-ios.a $TARGET_DIRECTORY/x86_64-apple-ios/debug/libsd_mobile_ios.a + fi + exit 0 +fi + +# Required for M1 Mac builds (?) +export PROTOC=/opt/homebrew/bin/protoc + if [[ $PLATFORM_NAME = "iphonesimulator" ]] then cargo build -p sd-mobile-ios --target aarch64-apple-ios-sim diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 150ee03b3..5d5a032c0 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -12,7 +12,8 @@ "android-studio": "open -a '/Applications/Android Studio.app' ./android", "lint": "eslint src", "typecheck": "tsc -b", - "eas-build-pre-install": "npm i -g pnpm@7.18.2" + "clean:android": "cd android && ./gradlew clean && cd ../", + "clean:ios": "cd ios && xcodebuild clean && cd ../" }, "dependencies": { "@gorhom/bottom-sheet": "^4.4.5", @@ -32,9 +33,9 @@ "byte-size": "^8.1.0", "class-variance-authority": "^0.4.0", "dayjs": "^1.11.5", - "expo": "^48.0.6", + "expo": "~48.0.6", "expo-linking": "~4.0.1", - "expo-media-library": "~15.2.2", + "expo-media-library": "~15.2.3", "expo-splash-screen": "~0.18.1", "expo-status-bar": "~1.4.4", "intl": "^1.2.5", @@ -60,6 +61,7 @@ "zod": "^3.21.4" }, "devDependencies": { + "@babel/core": "^7.21.0", "@rnx-kit/metro-config": "^1.3.5", "@sd/config": "workspace:*", "@types/react": "~18.0.27", diff --git a/apps/mobile/scripts/postinstall.js b/apps/mobile/scripts/postinstall.js deleted file mode 100644 index cca9a10cc..000000000 --- a/apps/mobile/scripts/postinstall.js +++ /dev/null @@ -1,19 +0,0 @@ -// @ts-ignore -let fs = require('fs-extra'); -let path = require('path'); - -// Not used atm, keeping it here in case we need it in the future -async function copyReactNativeCodegen() { - const paths = [ - ['../../../node_modules/react-native-codegen', '../node_modules/react-native-codegen'], - ['../../../node_modules/jsc-android', '../node_modules/jsc-android'] - ]; - - for (const pathTuple of paths) { - const [src, dest] = [path.join(__dirname, pathTuple[0]), path.join(__dirname, pathTuple[1])]; - await fs.remove(dest).catch(() => {}); - await fs.move(src, dest).catch(() => {}); - } -} - -copyReactNativeCodegen(); diff --git a/apps/mobile/scripts/run-maestro-tests b/apps/mobile/scripts/run-maestro-tests new file mode 100644 index 000000000..0c14cc7ae --- /dev/null +++ b/apps/mobile/scripts/run-maestro-tests @@ -0,0 +1,50 @@ +#!/bin/bash + +trap 'exit' INT + +PLATFORM=${1:-} + +case $PLATFORM in + ios | android ) + ;; + + *) + echo "Error! You must pass either 'android' or 'ios'" + echo "" + exit 1 + ;; +esac + +# NOTE: This script is intended to be run from the root of the project (CI) +if [ "$PLATFORM" == "ios" ]; then + testFiles=$(ls apps/mobile/tests/*.yml apps/mobile/tests/ios-only/*.yml) +else + testFiles=$(ls apps/mobile/tests/*.yml apps/mobile/tests/android-only/*.yml) +fi + +failedTests=() +for file in $testFiles +do + if ! maestro test "$file" + then + echo "Test ${file} failed. Retrying in 30 seconds..." + sleep 30 + if ! maestro test "$file" + then + echo "Test ${file} failed again. Retrying for the last time in 120 seconds..." + sleep 120 + if ! maestro test "$file" + then + failedTests+=("$file") + fi + fi + fi +done + +if [ ${#failedTests[@]} -eq 0 ]; then + exit 0 +else + echo "These tests failed:" + printf '%s\n' "${failedTests[@]}" + exit 1 +fi \ No newline at end of file diff --git a/apps/mobile/src/components/header/Header.tsx b/apps/mobile/src/components/header/Header.tsx index df0455b1f..febe7b284 100644 --- a/apps/mobile/src/components/header/Header.tsx +++ b/apps/mobile/src/components/header/Header.tsx @@ -22,7 +22,11 @@ export default function Header() { })} > - navigation.openDrawer()}> + navigation.openDrawer()} + > ( - + )} /> @@ -90,6 +96,7 @@ const MasterPasswordScreen = ({ navigation }: OnboardingStackScreenProps<'Master name="password_validate" render={({ field: { onBlur, onChange, value } }) => ( ( + - + } /> diff --git a/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx b/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx index a67dca83d..8d178b636 100644 --- a/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx +++ b/apps/mobile/src/screens/settings/library/LibraryGeneralSettings.tsx @@ -6,7 +6,7 @@ import { useBridgeMutation, useLibraryContext } from '@sd/client'; import { Input } from '~/components/form/Input'; import { Switch } from '~/components/form/Switch'; import DeleteLibraryModal from '~/components/modal/confirm-modals/DeleteLibraryModal'; -import { AnimatedButton } from '~/components/primitive/Button'; +import { FakeButton } from '~/components/primitive/Button'; import { Divider } from '~/components/primitive/Divider'; import { SettingsContainer, SettingsInputTitle } from '~/components/settings/SettingsContainer'; import { SettingsItem } from '~/components/settings/SettingsItem'; @@ -72,9 +72,9 @@ const LibraryGeneralSettingsScreen = ({ + - + } /> } diff --git a/apps/mobile/tests/onboarding.yml b/apps/mobile/tests/onboarding.yml new file mode 100644 index 000000000..a4f59c069 --- /dev/null +++ b/apps/mobile/tests/onboarding.yml @@ -0,0 +1,26 @@ +appId: com.spacedrive.app +--- +- launchApp: + clearState: true +- tapOn: 'Get Started' +- tapOn: + id: 'library-name' +- inputText: 'TestLib' +- tapOn: 'New Library' +- tapOn: + id: 'master-password' +- inputText: '12345678' +- tapOn: 'Set Password' +- tapOn: + id: 'master-password-confirm' +- inputText: '12345678' +- tapOn: 'Confirm Password' +- tapOn: 'Continue' +# Library creation can take a while... +- extendedWaitUntil: + visible: + id: 'drawer-toggle' + timeout: 180000 # 3 minutes +- tapOn: + id: 'drawer-toggle' +- assertVisible: 'TestLib' diff --git a/apps/mobile/tsconfig.json b/apps/mobile/tsconfig.json index 8645d920e..72d7bab67 100644 --- a/apps/mobile/tsconfig.json +++ b/apps/mobile/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "expo/tsconfig.base", "compilerOptions": { "allowJs": true, "esModuleInterop": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e805d8959..cde093095 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,6 +183,7 @@ importers: apps/mobile: specifiers: + '@babel/core': ^7.21.0 '@gorhom/bottom-sheet': ^4.4.5 '@hookform/resolvers': ^2.9.11 '@react-native-async-storage/async-storage': ~1.17.11 @@ -205,9 +206,9 @@ importers: class-variance-authority: ^0.4.0 dayjs: ^1.11.5 eslint-plugin-react-native: ^4.0.0 - expo: ^48.0.6 + expo: ~48.0.6 expo-linking: ~4.0.1 - expo-media-library: ~15.2.2 + expo-media-library: ~15.2.3 expo-splash-screen: ~0.18.1 expo-status-bar: ~1.4.4 intl: ^1.2.5 @@ -254,7 +255,7 @@ importers: dayjs: 1.11.7 expo: 48.0.6_@babel+core@7.21.3 expo-linking: 4.0.1_expo@48.0.6 - expo-media-library: 15.2.2_expo@48.0.6 + expo-media-library: 15.2.3_expo@48.0.6 expo-splash-screen: 0.18.1_mjojxfzfqol2n3a5upkwgtvxki expo-status-bar: 1.4.4 intl: 1.2.5 @@ -279,6 +280,7 @@ importers: valtio: 1.10.3_react@18.2.0 zod: 3.21.4 devDependencies: + '@babel/core': 7.21.3 '@rnx-kit/metro-config': 1.3.5_chpi2hywxvcqr7jb5bivwozu4e '@sd/config': link:../../packages/config '@types/react': 18.0.27 @@ -747,7 +749,7 @@ packages: resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.21.3 jsesc: 2.5.2 source-map: 0.5.7 dev: true @@ -755,18 +757,9 @@ packages: /@babel/generator/7.20.7: resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.2 - '@jridgewell/gen-mapping': 0.3.2 - jsesc: 2.5.2 - - /@babel/generator/7.21.1: - resolution: {integrity: sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==} - engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.21.3 '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 jsesc: 2.5.2 /@babel/generator/7.21.3: @@ -799,8 +792,8 @@ packages: dependencies: '@babel/compat-data': 7.20.10 '@babel/core': 7.20.12 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.4 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 lru-cache: 5.1.1 semver: 6.3.0 @@ -812,30 +805,11 @@ packages: dependencies: '@babel/compat-data': 7.20.10 '@babel/core': 7.21.3 - '@babel/helper-validator-option': 7.18.6 + '@babel/helper-validator-option': 7.21.0 browserslist: 4.21.4 lru-cache: 5.1.1 semver: 6.3.0 - /@babel/helper-create-class-features-plugin/7.20.12_@babel+core@7.21.3: - resolution: {integrity: sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.3 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.20.7 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 - transitivePeerDependencies: - - supports-color - dev: false - /@babel/helper-create-class-features-plugin/7.21.0_@babel+core@7.20.12: resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} engines: {node: '>=6.9.0'} @@ -955,13 +929,6 @@ packages: dependencies: '@babel/types': 7.21.3 - /@babel/helper-member-expression-to-functions/7.20.7: - resolution: {integrity: sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - dev: false - /@babel/helper-member-expression-to-functions/7.21.0: resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} engines: {node: '>=6.9.0'} @@ -984,8 +951,8 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.20.7 - '@babel/traverse': 7.21.2 - '@babel/types': 7.21.2 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color @@ -1082,10 +1049,6 @@ packages: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option/7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} - engines: {node: '>=6.9.0'} - /@babel/helper-validator-option/7.21.0: resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} @@ -1106,8 +1069,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/traverse': 7.21.2 - '@babel/types': 7.21.2 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color @@ -1134,14 +1097,15 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.21.3 /@babel/parser/7.21.2: resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.21.2 + '@babel/types': 7.21.3 + dev: true /@babel/parser/7.21.3: resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} @@ -1244,7 +1208,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.21.3 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.21.3 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.3 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-replace-supers': 7.20.7 '@babel/helper-split-export-declaration': 7.18.6 @@ -2826,13 +2790,13 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.1 + '@babel/generator': 7.21.3 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.21.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.2 - '@babel/types': 7.21.2 + '@babel/parser': 7.21.3 + '@babel/types': 7.21.3 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: @@ -2842,28 +2806,11 @@ packages: /@babel/traverse/7.20.12: resolution: {integrity: sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.1 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.2 - '@babel/types': 7.21.2 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - /@babel/traverse/7.21.2: - resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==} - engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 '@babel/generator': 7.21.3 '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 + '@babel/helper-function-name': 7.19.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.21.3 @@ -3675,7 +3622,7 @@ packages: resolution: {integrity: sha512-uhmrXNemXTbCTKP/ycyJHOU/KLGdFwVCrWNBzz1VkwnmL8yJV5F3C18a83ybFFnUNfkGHeH5LtID7CSNbbTWKg==} hasBin: true dependencies: - '@babel/runtime': 7.20.7 + '@babel/runtime': 7.21.0 '@expo/code-signing-certificates': 0.0.5 '@expo/config': 8.0.2 '@expo/config-plugins': 6.0.1 @@ -3698,7 +3645,7 @@ packages: bplist-parser: 0.3.2 cacache: 15.3.0 chalk: 4.1.2 - ci-info: 3.7.1 + ci-info: 3.8.0 debug: 4.3.4 env-editor: 0.4.2 form-data: 3.0.1 @@ -3715,7 +3662,7 @@ packages: md5-file: 3.2.3 md5hex: 1.0.0 minipass: 3.1.6 - node-fetch: 2.6.8 + node-fetch: 2.6.9 node-forge: 1.3.1 npm-package-arg: 7.0.0 ora: 3.4.0 @@ -3821,7 +3768,7 @@ packages: fs-extra: 9.0.0 is-docker: 2.2.1 is-wsl: 2.2.0 - node-fetch: 2.6.8 + node-fetch: 2.6.9 open: 8.4.0 resolve-from: 5.0.0 semver: 7.3.2 @@ -3953,7 +3900,7 @@ packages: '@segment/loosely-validate-event': 2.0.0 fetch-retry: 4.1.1 md5: 2.3.0 - node-fetch: 2.6.8 + node-fetch: 2.6.9 remove-trailing-slash: 0.1.1 uuid: 8.3.2 transitivePeerDependencies: @@ -6575,7 +6522,7 @@ packages: '@storybook/core-events': 7.0.0-rc.10 '@storybook/csf': 0.0.2-next.11 '@storybook/csf-tools': 7.0.0-rc.10 - '@storybook/docs-mdx': 0.0.1-next.6 + '@storybook/docs-mdx': 0.0.1-next.7 '@storybook/global': 5.0.0 '@storybook/manager': 7.0.0-rc.10 '@storybook/node-logger': 7.0.0-rc.10 @@ -6648,8 +6595,8 @@ packages: type-fest: 2.19.0 dev: true - /@storybook/docs-mdx/0.0.1-next.6: - resolution: {integrity: sha512-DjoSIXADmLJtdroXAjUotFiZlcZ2usWhqrS7aeOtZs0DVR0Ws5WQjnwtpDUXt8gryTSd+OZJ0cNsDcqg4JDEvQ==} + /@storybook/docs-mdx/0.0.1-next.7: + resolution: {integrity: sha512-JbgBf/EMBtx65iXtB3pOiX3818UeL9jZ+KAY241OAPqJVXjMQ5KaVOdg/57MSmd508HDIGx7CiImOMEmWwQ9/g==} dev: true /@storybook/docs-tools/7.0.0-rc.10: @@ -8715,7 +8662,7 @@ packages: dependencies: '@babel/plugin-proposal-decorators': 7.20.7_@babel+core@7.21.3 '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.21.3 - '@babel/plugin-transform-react-jsx': 7.20.7_@babel+core@7.21.3 + '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.21.3 '@babel/preset-env': 7.20.2_@babel+core@7.21.3 babel-plugin-module-resolver: 4.1.0 babel-plugin-react-native-web: 0.18.10 @@ -9020,8 +8967,8 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001472 - electron-to-chromium: 1.4.342 - node-releases: 2.0.10 + electron-to-chromium: 1.4.284 + node-releases: 2.0.8 update-browserslist-db: 1.0.10_browserslist@4.21.5 /bser/2.1.1: @@ -9289,11 +9236,6 @@ packages: /ci-info/2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} - /ci-info/3.7.1: - resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} - engines: {node: '>=8'} - dev: false - /ci-info/3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -10341,9 +10283,6 @@ packages: /electron-to-chromium/1.4.284: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} - /electron-to-chromium/1.4.342: - resolution: {integrity: sha512-dTei3VResi5bINDENswBxhL+N0Mw5YnfWyTqO75KGsVldurEkhC9+CelJVAse8jycWyP8pv3VSj4BSyP8wTWJA==} - /electron/11.5.0: resolution: {integrity: sha512-WjNDd6lGpxyiNjE3LhnFCAk/D9GIj1rU3GSDealVShhkkkPR3Vh4q8ErXGDl1OAO/faomVa10KoFPUN/pLbNxg==} engines: {node: '>= 8.6'} @@ -11032,8 +10971,8 @@ packages: - supports-color dev: false - /expo-media-library/15.2.2_expo@48.0.6: - resolution: {integrity: sha512-GebBavV9H+m0Qzoy4G7++BWmwUcddLnCee1qGYkCyHT6CvuLNhXUgC3FV9NINEwlii3HGAuCzk1auaEY60SGDA==} + /expo-media-library/15.2.3_expo@48.0.6: + resolution: {integrity: sha512-Oz8b8Xsvfj7YcutUBtI84NUIqSnt7iCM5HZ5DyKoWKKiDK/+aUuj3RXNQELG8jUw6pQPgEwgbZ1+J8SdH/y9jw==} peerDependencies: expo: '*' dependencies: @@ -11080,7 +11019,7 @@ packages: resolution: {integrity: sha512-ylm91v/xYjBBEqFHH+mpNyGijJgFXx4NwgKgHCIEfcAQyTZLXpGCL6teOVzAmHCCVF7EdalLl3If/+n09jOi4g==} hasBin: true dependencies: - '@babel/runtime': 7.20.7 + '@babel/runtime': 7.21.0 '@expo/cli': 0.6.2_6weo4dzsrefthwodkc5gdxxcii '@expo/config': 8.0.2 '@expo/config-plugins': 6.0.1 @@ -11099,7 +11038,7 @@ packages: getenv: 1.0.0 invariant: 2.2.4 md5-file: 3.2.3 - node-fetch: 2.6.8 + node-fetch: 2.6.9 pretty-format: 26.6.2 uuid: 3.4.0 transitivePeerDependencies: @@ -14098,8 +14037,8 @@ packages: /metro-source-map/0.73.7: resolution: {integrity: sha512-gbC/lfUN52TtQhEsTTA+987MaFUpQlufuCI05blLGLosDcFCsARikHsxa65Gtslm/rG2MqvFLiPA5hviONNv9g==} dependencies: - '@babel/traverse': 7.21.2 - '@babel/types': 7.21.2 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 invariant: 2.2.4 metro-symbolicate: 0.73.7 nullthrows: 1.1.1 @@ -14700,9 +14639,6 @@ packages: /node-int64/0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - /node-releases/2.0.10: - resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - /node-releases/2.0.8: resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==}