From c7441d1d6498d53fc7e249b77b46209e2c29f709 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Mon, 29 Aug 2022 19:59:09 +0800 Subject: [PATCH] Rustify mobile (#361) * Refactor navigation flow & types * Remove drawer screen wrapper * Remove DrawerItem + cleanup * Switch to JS Stack Nav & header animations * [WIP] Spacedrive core on Android & IOS * Update Podfile and cleanup Contributing guide. * Remove @sd/core from mobile * File Modal * Prettify File Modal & Add date-fns * IOS subscriptions * Update package versions * Custom header for stack screens * android subscriptions * Animate Drawer button & template for Search screen * Search header * Fix Search icon being weird * Merge branch 'main' into rustify-mobile * fix rspc dep + setup script for mobile * Less margin on header * Move shared assets & drawer logo for mobile * support for IOS simulator * add type safe rspc hooks to mobile * Cleanup PR & Update packages * Updated bindings from main * Update lefthook.yml * Remove `tag` folder from core The `tag` folder came back from the dead. Maybe it got confused in merge conflict? * update pnpm lockfile + fix tsc errors * fix asset import Co-authored-by: Utku Bakir <74243531+utkubakir@users.noreply.github.com> --- CONTRIBUTING.md | 38 +- Cargo.lock | Bin 170602 -> 171012 bytes Cargo.toml | 18 +- apps/desktop/src-tauri/Cargo.toml | 1 + apps/desktop/src-tauri/src/main.rs | 2 +- apps/landing/package.json | 1 + apps/landing/src/components/Footer.tsx | 3 +- apps/landing/src/components/NavBar.tsx | 2 +- apps/mobile/README.md | 2 + apps/mobile/android/app/build.gradle | 18 + .../com/spacedrive/app/MainApplication.java | 2 +- .../main/java/com/spacedrive/app/SDCore.java | 76 +++ .../com/spacedrive/app/SpacedrivePackage.java | 28 + apps/mobile/android/build.gradle | 4 + apps/mobile/ios/Podfile.lock | 510 +++++++++--------- .../ios/Spacedrive.xcodeproj/project.pbxproj | 244 +++++++-- apps/mobile/ios/Spacedrive/Info.plist | 152 +++--- apps/mobile/ios/Spacedrive/SDCore.h | 17 + apps/mobile/ios/Spacedrive/SDCore.m | 81 +++ apps/mobile/metro.config.js | 3 - apps/mobile/package.json | 50 +- apps/mobile/pnpm-lock.yaml | Bin 322567 -> 326993 bytes apps/mobile/rust/Cargo.toml | 27 + apps/mobile/rust/src/android.rs | 111 ++++ apps/mobile/rust/src/ios.rs | 96 ++++ apps/mobile/rust/src/lib.rs | 31 ++ apps/mobile/src/App.tsx | 39 +- apps/mobile/src/assets/temp/folder-white.svg | 15 - apps/mobile/src/assets/temp/folder.svg | 16 - apps/mobile/src/assets/temp/logo.png | Bin 129137 -> 0 bytes apps/mobile/src/components/base/Divider.tsx | 14 + .../components/browse/BrowseLocationItem.tsx | 2 +- apps/mobile/src/components/device/Device.tsx | 21 +- .../src/components/drawer/DrawerContent.tsx | 118 ++-- .../src/components/drawer/DrawerItem.tsx | 34 -- .../components/drawer/DrawerLocationItem.tsx | 26 + .../src/components/drawer/DrawerLogo.tsx | 19 + .../components/drawer/DrawerScreenWrapper.tsx | 31 -- .../src/components/drawer/DrawerTagItem.tsx | 26 + apps/mobile/src/components/file/FileIcon.tsx | 73 +++ apps/mobile/src/components/file/FileItem.tsx | 80 +-- apps/mobile/src/components/header/Header.tsx | 41 ++ .../icons/{Folder.tsx => FolderIcon.tsx} | 5 +- .../src/components/modals/FileDetails.tsx | 1 - .../src/components/modals/FileModal.tsx | 128 +++++ .../src/components/modals/GlobalModals.tsx | 13 + .../modals/layout/ModalBackdrop.tsx | 10 + .../components/modals/layout/ModalHandle.tsx | 16 + apps/mobile/src/containers/OverviewStats.tsx | 13 +- apps/mobile/src/hooks/rspc.ts | 152 ++++++ .../mobile/src/navigation/DrawerNavigator.tsx | 16 +- .../src/navigation/OnboardingNavigator.tsx | 6 +- apps/mobile/src/navigation/SharedScreens.tsx | 36 ++ apps/mobile/src/navigation/TabNavigator.tsx | 41 +- apps/mobile/src/navigation/index.tsx | 33 +- .../src/navigation/tabs/BrowseStack.tsx | 35 +- .../src/navigation/tabs/OverviewStack.tsx | 35 ++ .../src/navigation/tabs/PhotosStack.tsx | 35 ++ .../src/navigation/tabs/SpacesStack.tsx | 35 ++ apps/mobile/src/screens/Browse.tsx | 4 +- apps/mobile/src/screens/Location.tsx | 3 +- apps/mobile/src/screens/Overview.tsx | 48 +- apps/mobile/src/screens/Photos.tsx | 4 +- apps/mobile/src/screens/Spaces.tsx | 11 +- apps/mobile/src/screens/Tag.tsx | 3 +- apps/mobile/src/screens/modals/Search.tsx | 57 ++ .../src/screens/modals/settings/Settings.tsx | 2 +- .../src/screens/onboarding/Onboarding.tsx | 7 +- apps/mobile/src/stores/useLibraryStore.ts | 13 + apps/mobile/src/stores/useModalStore.ts | 19 + apps/mobile/src/types/bindings.ts | 117 ++++ apps/mobile/src/types/declarations.d.ts | 8 - apps/server/Cargo.toml | 1 + core/Cargo.toml | 12 +- core/derive/Cargo.toml | 11 - core/derive/src/lib.rs | 42 -- core/index.ts | 135 +++-- core/src/api/mod.rs | 6 +- core/src/api/utils/invalidate.rs | 8 +- core/src/encode/metadata.rs | 8 +- core/src/lib.rs | 4 +- core/src/tag/mod.rs | 185 ------- lefthook.yml | 4 +- package.json | 4 +- .../src => packages}/assets/images/logo.png | Bin packages/assets/svgs/folder-white.svg | 15 + packages/assets/svgs/folder.svg | 16 + packages/client/package.json | 2 +- .../interface/src/assets/svg/folder-white.svg | 15 - packages/interface/src/assets/svg/folder.svg | 16 - .../src/components/file/FileItem.tsx | 2 +- .../interface/src/components/icons/Folder.tsx | 5 +- pnpm-lock.yaml | Bin 663002 -> 663100 bytes 93 files changed, 2368 insertions(+), 1101 deletions(-) create mode 100644 apps/mobile/android/app/src/main/java/com/spacedrive/app/SDCore.java create mode 100644 apps/mobile/android/app/src/main/java/com/spacedrive/app/SpacedrivePackage.java create mode 100644 apps/mobile/ios/Spacedrive/SDCore.h create mode 100644 apps/mobile/ios/Spacedrive/SDCore.m create mode 100644 apps/mobile/rust/Cargo.toml create mode 100644 apps/mobile/rust/src/android.rs create mode 100644 apps/mobile/rust/src/ios.rs create mode 100644 apps/mobile/rust/src/lib.rs delete mode 100644 apps/mobile/src/assets/temp/folder-white.svg delete mode 100644 apps/mobile/src/assets/temp/folder.svg delete mode 100644 apps/mobile/src/assets/temp/logo.png create mode 100644 apps/mobile/src/components/base/Divider.tsx delete mode 100644 apps/mobile/src/components/drawer/DrawerItem.tsx create mode 100644 apps/mobile/src/components/drawer/DrawerLocationItem.tsx create mode 100644 apps/mobile/src/components/drawer/DrawerLogo.tsx delete mode 100644 apps/mobile/src/components/drawer/DrawerScreenWrapper.tsx create mode 100644 apps/mobile/src/components/drawer/DrawerTagItem.tsx create mode 100644 apps/mobile/src/components/file/FileIcon.tsx create mode 100644 apps/mobile/src/components/header/Header.tsx rename apps/mobile/src/components/icons/{Folder.tsx => FolderIcon.tsx} (81%) delete mode 100644 apps/mobile/src/components/modals/FileDetails.tsx create mode 100644 apps/mobile/src/components/modals/FileModal.tsx create mode 100644 apps/mobile/src/components/modals/GlobalModals.tsx create mode 100644 apps/mobile/src/components/modals/layout/ModalBackdrop.tsx create mode 100644 apps/mobile/src/components/modals/layout/ModalHandle.tsx create mode 100644 apps/mobile/src/hooks/rspc.ts create mode 100644 apps/mobile/src/navigation/SharedScreens.tsx create mode 100644 apps/mobile/src/navigation/tabs/OverviewStack.tsx create mode 100644 apps/mobile/src/navigation/tabs/PhotosStack.tsx create mode 100644 apps/mobile/src/navigation/tabs/SpacesStack.tsx create mode 100644 apps/mobile/src/screens/modals/Search.tsx create mode 100644 apps/mobile/src/stores/useLibraryStore.ts create mode 100644 apps/mobile/src/stores/useModalStore.ts create mode 100644 apps/mobile/src/types/bindings.ts delete mode 100644 core/derive/Cargo.toml delete mode 100644 core/derive/src/lib.rs delete mode 100644 core/src/tag/mod.rs rename {apps/landing/src => packages}/assets/images/logo.png (100%) create mode 100644 packages/assets/svgs/folder-white.svg create mode 100644 packages/assets/svgs/folder.svg delete mode 100644 packages/interface/src/assets/svg/folder-white.svg delete mode 100644 packages/interface/src/assets/svg/folder.svg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee1475ee3..26e2000ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,39 +37,43 @@ This project uses [Cargo](https://doc.rust-lang.org/cargo/getting-started/instal > Note: MacOS M1 users should choose the customize option in the rustup init script and enter `x86_64-apple-darwin` as the default host triple instead of the default `aarch64-apple-darwin` -- `$ git clone https://github.com/spacedriveapp/spacedrive` -- `$ cd spacedrive` +- `git clone https://github.com/spacedriveapp/spacedrive` +- `cd spacedrive` - For Linux or MacOS users run: `./.github/scripts/setup-system.sh` - This will install FFMPEG and any other required dependencies for Spacedrive to build. - For Windows users run using PowerShell: `.\.github\scripts\setup-system.ps1` - This will install pnpm, LLVM, FFMPEG and any other required dependencies for Spacedrive to build. - Ensure you run it like documented above as it expects it is executed from the root of the repository. -- `$ pnpm i` -- `$ pnpm prep` - Runs all necessary codegen & builds required dependencies. +- `pnpm i` +- `pnpm prep` - Runs all necessary codegen & builds required dependencies. To quickly run only the desktop app after `prep` you can use: -- `$ pnpm desktop dev` +- `pnpm desktop dev` To run the landing page -- `$ pnpm web dev` - runs the web app for the embed -- `$ pnpm landing dev` - -To run mobile app - -- `$ cd apps/mobile && pnpm i` - As this is a seperated workspace, you need to do this! -- `$ pnpm android` - runs on Android Emulator -- `$ pnpm ios` - runs on iOS Emulator -- `$ pnpm dev` - For already bundled app - -You also need `expo-cli` installed globally. +- `pnpm web dev` - runs the web app for the embed +- `pnpm landing dev` If you are having issues ensure you are using the following versions of Rust and Node: -- Rust version: **1.62.0** +- Rust version: **1.63.0** - Node version: **17** +##### Mobile app + +To run mobile app + +- Install [Android Studio](https://developer.android.com/studio) for Android and [Xcode](https://apps.apple.com/au/app/xcode/id497799835) for IOS development +- `./.github/scripts/setup-system.sh mobile` + - The should setup most of the dependencies for the mobile app to build. +- You must also ensure [you must have NDK 24.0.8215888 and CMake](https://developer.android.com/studio/projects/install-ndk#default-version) in Android Studio +- `cd apps/mobile && pnpm i` - This is a separate workspace, you need to do this! +- `pnpm android` - runs on Android Emulator +- `pnpm ios` - runs on iOS Emulator +- `pnpm dev` - For already bundled app - This is only temporarily supported. The final app will require the Spacedrive Rust code which isn't included in Expo Go. + ### Pull Request When you're finished with the changes, create a pull request, also known as a PR. diff --git a/Cargo.lock b/Cargo.lock index d4285c8fed94c934c71b81c163f37088f0f960a1..a619404082d49fa89e469938199d73f556783820 100644 GIT binary patch delta 7052 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 f00}&B delta 6912 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 O?8(;YD{r2BT>LMnycqxh diff --git a/Cargo.toml b/Cargo.toml index bb526593c..507f92891 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,13 @@ [workspace] -members = [ - "apps/desktop/src-tauri", - "core", - "core/prisma", - "core/derive", - "apps/server", -] resolver = "2" +members = [ + "apps/desktop/src-tauri", + "apps/mobile/rust", + "core", + "core/prisma", + "apps/server" +] + +[patch.crates-io] +# We use this patch so we can compile for the IOS simulator on M1 +openssl-sys = { git = "https://github.com/spacedriveapp/rust-openssl" } \ No newline at end of file diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index edf6c13bb..9bbcfb57c 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -11,6 +11,7 @@ build = "build.rs" [dependencies] tauri = { version = "1.0.4", features = ["api-all", "macos-private-api"] } +rspc = { version = "0.0.4", features = ["tauri"] } sdcore = { path = "../../../core" } tokio = { version = "1.17.0", features = ["sync"] } window-shadows = "0.1.2" diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index 5916fddf5..8ae8ba8e3 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -23,7 +23,7 @@ async fn main() { let (node, router) = Node::new(data_dir).await; let app = tauri::Builder::default() - .plugin(sdcore::rspc::integrations::tauri::plugin(router, { + .plugin(rspc::integrations::tauri::plugin(router, { let node = node.clone(); move || node.get_request_context() })) diff --git a/apps/landing/package.json b/apps/landing/package.json index 15a886cde..3f8f28596 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -14,6 +14,7 @@ "@icons-pack/react-simple-icons": "^5.2.0", "@sd/interface": "link:../../packages/interface", "@sd/ui": "link:../../packages/ui", + "@sd/assets": "link:../../packages/assets", "@tryghost/content-api": "^1.11.0", "@types/compression": "^1.7.2", "@types/express": "^4.17.13", diff --git a/apps/landing/src/components/Footer.tsx b/apps/landing/src/components/Footer.tsx index a0a74210d..4aab03454 100644 --- a/apps/landing/src/components/Footer.tsx +++ b/apps/landing/src/components/Footer.tsx @@ -6,10 +6,9 @@ import { Twitch, Twitter } from '@icons-pack/react-simple-icons'; +import AppLogo from '@sd/assets/images/logo.png'; import React from 'react'; -import AppLogo from '../assets/images/logo.png'; - function FooterLink(props: { children: string | JSX.Element; link: string; blank?: boolean }) { return ( + if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) { + task.dependsOn 'cargoBuild' + } +} + /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). diff --git a/apps/mobile/android/app/src/main/java/com/spacedrive/app/MainApplication.java b/apps/mobile/android/app/src/main/java/com/spacedrive/app/MainApplication.java index 8838de57a..1d47d6150 100644 --- a/apps/mobile/android/app/src/main/java/com/spacedrive/app/MainApplication.java +++ b/apps/mobile/android/app/src/main/java/com/spacedrive/app/MainApplication.java @@ -34,7 +34,7 @@ public class MainApplication extends Application implements ReactApplication { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new MyReactNativePackage()); + packages.add(new com.spacedrive.app.SpacedrivePackage()); return packages; } diff --git a/apps/mobile/android/app/src/main/java/com/spacedrive/app/SDCore.java b/apps/mobile/android/app/src/main/java/com/spacedrive/app/SDCore.java new file mode 100644 index 000000000..782f44a1b --- /dev/null +++ b/apps/mobile/android/app/src/main/java/com/spacedrive/app/SDCore.java @@ -0,0 +1,76 @@ +package com.spacedrive.app; + +import android.content.Context; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.modules.core.DeviceEventManagerModule; + +import javax.annotation.Nullable; + +public class SDCore extends ReactContextBaseJavaModule { + SDCore(ReactApplicationContext context) { super(context); } + + private boolean registeredWithRust = false; + private int listeners = 0; + + @Override + public String getName() + { + return "SDCore"; + } + + static { + System.loadLibrary("sdcore"); + } + + // is exposed by Rust and is used to register the subscription + private native void registerCoreEventListener(); + + private native void handleCoreMsg(String query, Promise promise); + + @ReactMethod + public void sd_core_msg(String query, Promise promise) + { + this.handleCoreMsg(query, promise); + } + + public String getDataDirectory() + { + return getCurrentActivity().getFilesDir().toString(); + } + + @ReactMethod + public void addListener(String eventName) + { + if (!registeredWithRust) + { + this.registerCoreEventListener(); + } + + this.listeners++; + } + + @ReactMethod + public void removeListeners(Integer count) + { + this.listeners--; + } + + public void sendCoreEvent(String body) + { + if (this.listeners > 0) + { + this.getReactApplicationContext() + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("SDCoreEvent", body); + } + } +} \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/java/com/spacedrive/app/SpacedrivePackage.java b/apps/mobile/android/app/src/main/java/com/spacedrive/app/SpacedrivePackage.java new file mode 100644 index 000000000..b9fe3bc95 --- /dev/null +++ b/apps/mobile/android/app/src/main/java/com/spacedrive/app/SpacedrivePackage.java @@ -0,0 +1,28 @@ +package com.spacedrive.app; +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SpacedrivePackage implements ReactPackage { + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @Override + public List createNativeModules( + ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new SDCore(reactContext)); + + return modules; + } + +} \ No newline at end of file diff --git a/apps/mobile/android/build.gradle b/apps/mobile/android/build.gradle index a19a7b074..ab32c2ff7 100644 --- a/apps/mobile/android/build.gradle +++ b/apps/mobile/android/build.gradle @@ -24,11 +24,15 @@ buildscript { repositories { google() mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } } dependencies { classpath('com.android.tools.build:gradle:7.1.1') classpath('com.facebook.react:react-native-gradle-plugin') classpath('de.undercouch:gradle-download-task:5.0.1') + classpath('org.mozilla.rust-android-gradle:plugin:0.9.3') // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock index 804fee0e9..00b80fd0f 100644 --- a/apps/mobile/ios/Podfile.lock +++ b/apps/mobile/ios/Podfile.lock @@ -9,27 +9,27 @@ PODS: - ExpoModulesCore - EXFont (10.2.0): - ExpoModulesCore - - Expo (46.0.2): + - Expo (46.0.9): - ExpoModulesCore - ExpoKeepAwake (10.2.0): - ExpoModulesCore - - ExpoModulesCore (0.11.3): + - ExpoModulesCore (0.11.4): - React-Core - ReactCommon/turbomodule/core - - EXSplashScreen (0.16.1): + - EXSplashScreen (0.16.2): - ExpoModulesCore - React-Core - - FBLazyVector (0.69.3) - - FBReactNativeSpec (0.69.3): + - FBLazyVector (0.69.4) + - FBReactNativeSpec (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - RCTRequired (= 0.69.3) - - RCTTypeSafety (= 0.69.3) - - React-Core (= 0.69.3) - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) + - RCTRequired (= 0.69.4) + - RCTTypeSafety (= 0.69.4) + - React-Core (= 0.69.4) + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) - fmt (6.2.1) - glog (0.3.5) - - hermes-engine (0.69.3) + - hermes-engine (0.69.4) - libevent (2.1.12) - RCT-Folly (2021.06.28.00-v2): - boost @@ -48,214 +48,214 @@ PODS: - fmt (~> 6.2.1) - glog - libevent - - RCTRequired (0.69.3) - - RCTTypeSafety (0.69.3): - - FBLazyVector (= 0.69.3) - - RCTRequired (= 0.69.3) - - React-Core (= 0.69.3) - - React (0.69.3): - - React-Core (= 0.69.3) - - React-Core/DevSupport (= 0.69.3) - - React-Core/RCTWebSocket (= 0.69.3) - - React-RCTActionSheet (= 0.69.3) - - React-RCTAnimation (= 0.69.3) - - React-RCTBlob (= 0.69.3) - - React-RCTImage (= 0.69.3) - - React-RCTLinking (= 0.69.3) - - React-RCTNetwork (= 0.69.3) - - React-RCTSettings (= 0.69.3) - - React-RCTText (= 0.69.3) - - React-RCTVibration (= 0.69.3) - - React-bridging (0.69.3): + - RCTRequired (0.69.4) + - RCTTypeSafety (0.69.4): + - FBLazyVector (= 0.69.4) + - RCTRequired (= 0.69.4) + - React-Core (= 0.69.4) + - React (0.69.4): + - React-Core (= 0.69.4) + - React-Core/DevSupport (= 0.69.4) + - React-Core/RCTWebSocket (= 0.69.4) + - React-RCTActionSheet (= 0.69.4) + - React-RCTAnimation (= 0.69.4) + - React-RCTBlob (= 0.69.4) + - React-RCTImage (= 0.69.4) + - React-RCTLinking (= 0.69.4) + - React-RCTNetwork (= 0.69.4) + - React-RCTSettings (= 0.69.4) + - React-RCTText (= 0.69.4) + - React-RCTVibration (= 0.69.4) + - React-bridging (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - React-jsi (= 0.69.3) - - React-callinvoker (0.69.3) - - React-Codegen (0.69.3): - - FBReactNativeSpec (= 0.69.3) + - React-jsi (= 0.69.4) + - React-callinvoker (0.69.4) + - React-Codegen (0.69.4): + - FBReactNativeSpec (= 0.69.4) - RCT-Folly (= 2021.06.28.00-v2) - - RCTRequired (= 0.69.3) - - RCTTypeSafety (= 0.69.3) - - React-Core (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-Core (0.69.3): + - RCTRequired (= 0.69.4) + - RCTTypeSafety (= 0.69.4) + - React-Core (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-Core (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/Default (= 0.69.3) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-Core/Default (= 0.69.4) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/CoreModulesHeaders (0.69.3): + - React-Core/CoreModulesHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/Default (0.69.3): + - React-Core/Default (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/DevSupport (0.69.3): + - React-Core/DevSupport (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/Default (= 0.69.3) - - React-Core/RCTWebSocket (= 0.69.3) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-jsinspector (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-Core/Default (= 0.69.4) + - React-Core/RCTWebSocket (= 0.69.4) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-jsinspector (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTActionSheetHeaders (0.69.3): + - React-Core/RCTActionSheetHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTAnimationHeaders (0.69.3): + - React-Core/RCTAnimationHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTBlobHeaders (0.69.3): + - React-Core/RCTBlobHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTImageHeaders (0.69.3): + - React-Core/RCTImageHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTLinkingHeaders (0.69.3): + - React-Core/RCTLinkingHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTNetworkHeaders (0.69.3): + - React-Core/RCTNetworkHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTSettingsHeaders (0.69.3): + - React-Core/RCTSettingsHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTTextHeaders (0.69.3): + - React-Core/RCTTextHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTVibrationHeaders (0.69.3): + - React-Core/RCTVibrationHeaders (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-Core/RCTWebSocket (0.69.3): + - React-Core/RCTWebSocket (0.69.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/Default (= 0.69.3) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-Core/Default (= 0.69.4) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-perflogger (= 0.69.4) - Yoga - - React-CoreModules (0.69.3): + - React-CoreModules (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.69.3) - - React-Codegen (= 0.69.3) - - React-Core/CoreModulesHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - React-RCTImage (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-cxxreact (0.69.3): + - RCTTypeSafety (= 0.69.4) + - React-Codegen (= 0.69.4) + - React-Core/CoreModulesHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - React-RCTImage (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-cxxreact (0.69.4): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-callinvoker (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsinspector (= 0.69.3) - - React-logger (= 0.69.3) - - React-perflogger (= 0.69.3) - - React-runtimeexecutor (= 0.69.3) - - React-hermes (0.69.3): + - React-callinvoker (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsinspector (= 0.69.4) + - React-logger (= 0.69.4) + - React-perflogger (= 0.69.4) + - React-runtimeexecutor (= 0.69.4) + - React-hermes (0.69.4): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly/Futures (= 2021.06.28.00-v2) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-jsiexecutor (= 0.69.3) - - React-jsinspector (= 0.69.3) - - React-perflogger (= 0.69.3) - - React-jsi (0.69.3): + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-jsiexecutor (= 0.69.4) + - React-jsinspector (= 0.69.4) + - React-perflogger (= 0.69.4) + - React-jsi (0.69.4): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-jsi/Default (= 0.69.3) - - React-jsi/Default (0.69.3): + - React-jsi/Default (= 0.69.4) + - React-jsi/Default (0.69.4): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-jsiexecutor (0.69.3): + - React-jsiexecutor (0.69.4): - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-perflogger (= 0.69.3) - - React-jsinspector (0.69.3) - - React-logger (0.69.3): + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-perflogger (= 0.69.4) + - React-jsinspector (0.69.4) + - React-logger (0.69.4): - glog - react-native-safe-area-context (4.3.1): - RCT-Folly @@ -263,74 +263,76 @@ PODS: - RCTTypeSafety - React - ReactCommon/turbomodule/core - - React-perflogger (0.69.3) - - React-RCTActionSheet (0.69.3): - - React-Core/RCTActionSheetHeaders (= 0.69.3) - - React-RCTAnimation (0.69.3): + - React-perflogger (0.69.4) + - React-RCTActionSheet (0.69.4): + - React-Core/RCTActionSheetHeaders (= 0.69.4) + - React-RCTAnimation (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.69.3) - - React-Codegen (= 0.69.3) - - React-Core/RCTAnimationHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-RCTBlob (0.69.3): + - RCTTypeSafety (= 0.69.4) + - React-Codegen (= 0.69.4) + - React-Core/RCTAnimationHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-RCTBlob (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - React-Codegen (= 0.69.3) - - React-Core/RCTBlobHeaders (= 0.69.3) - - React-Core/RCTWebSocket (= 0.69.3) - - React-jsi (= 0.69.3) - - React-RCTNetwork (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-RCTImage (0.69.3): + - React-Codegen (= 0.69.4) + - React-Core/RCTBlobHeaders (= 0.69.4) + - React-Core/RCTWebSocket (= 0.69.4) + - React-jsi (= 0.69.4) + - React-RCTNetwork (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-RCTImage (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.69.3) - - React-Codegen (= 0.69.3) - - React-Core/RCTImageHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - React-RCTNetwork (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-RCTLinking (0.69.3): - - React-Codegen (= 0.69.3) - - React-Core/RCTLinkingHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-RCTNetwork (0.69.3): + - RCTTypeSafety (= 0.69.4) + - React-Codegen (= 0.69.4) + - React-Core/RCTImageHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - React-RCTNetwork (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-RCTLinking (0.69.4): + - React-Codegen (= 0.69.4) + - React-Core/RCTLinkingHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-RCTNetwork (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.69.3) - - React-Codegen (= 0.69.3) - - React-Core/RCTNetworkHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-RCTSettings (0.69.3): + - RCTTypeSafety (= 0.69.4) + - React-Codegen (= 0.69.4) + - React-Core/RCTNetworkHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-RCTSettings (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.69.3) - - React-Codegen (= 0.69.3) - - React-Core/RCTSettingsHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-RCTText (0.69.3): - - React-Core/RCTTextHeaders (= 0.69.3) - - React-RCTVibration (0.69.3): + - RCTTypeSafety (= 0.69.4) + - React-Codegen (= 0.69.4) + - React-Core/RCTSettingsHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-RCTText (0.69.4): + - React-Core/RCTTextHeaders (= 0.69.4) + - React-RCTVibration (0.69.4): - RCT-Folly (= 2021.06.28.00-v2) - - React-Codegen (= 0.69.3) - - React-Core/RCTVibrationHeaders (= 0.69.3) - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (= 0.69.3) - - React-runtimeexecutor (0.69.3): - - React-jsi (= 0.69.3) - - ReactCommon/turbomodule/core (0.69.3): + - React-Codegen (= 0.69.4) + - React-Core/RCTVibrationHeaders (= 0.69.4) + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (= 0.69.4) + - React-runtimeexecutor (0.69.4): + - React-jsi (= 0.69.4) + - ReactCommon/turbomodule/core (0.69.4): - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-bridging (= 0.69.3) - - React-callinvoker (= 0.69.3) - - React-Core (= 0.69.3) - - React-cxxreact (= 0.69.3) - - React-jsi (= 0.69.3) - - React-logger (= 0.69.3) - - React-perflogger (= 0.69.3) + - React-bridging (= 0.69.4) + - React-callinvoker (= 0.69.4) + - React-Core (= 0.69.4) + - React-cxxreact (= 0.69.4) + - React-jsi (= 0.69.4) + - React-logger (= 0.69.4) + - React-perflogger (= 0.69.4) - RNCAsyncStorage (1.17.7): - React-Core + - RNCMaskedView (0.2.7): + - React-Core - RNGestureHandler (2.5.0): - React-Core - RNReanimated (2.9.1): @@ -363,21 +365,21 @@ PODS: - RNScreens (3.15.0): - React-Core - React-RCTImage - - RNSVG (12.4.3): + - RNSVG (13.0.0): - React-Core - Yoga (1.14.0) DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - "EXApplication (from `../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.2/node_modules/expo-application/ios`)" - - "EXConstants (from `../node_modules/.pnpm/expo-constants@13.2.3_expo@46.0.2/node_modules/expo-constants/ios`)" - - "EXFileSystem (from `../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.2/node_modules/expo-file-system/ios`)" - - "EXFont (from `../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.2/node_modules/expo-font/ios`)" - - "Expo (from `../node_modules/.pnpm/expo@46.0.2_@babel+core@7.18.10/node_modules/expo`)" - - "ExpoKeepAwake (from `../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.2/node_modules/expo-keep-awake/ios`)" - - "ExpoModulesCore (from `../node_modules/.pnpm/expo-modules-core@0.11.3/node_modules/expo-modules-core/ios`)" - - "EXSplashScreen (from `../node_modules/.pnpm/expo-splash-screen@0.16.1_expo@46.0.2/node_modules/expo-splash-screen/ios`)" + - "EXApplication (from `../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.9/node_modules/expo-application/ios`)" + - "EXConstants (from `../node_modules/.pnpm/expo-constants@13.2.3_expo@46.0.9/node_modules/expo-constants/ios`)" + - "EXFileSystem (from `../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.9/node_modules/expo-file-system/ios`)" + - "EXFont (from `../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.9/node_modules/expo-font/ios`)" + - "Expo (from `../node_modules/.pnpm/expo@46.0.9_@babel+core@7.18.10/node_modules/expo`)" + - "ExpoKeepAwake (from `../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.9/node_modules/expo-keep-awake/ios`)" + - "ExpoModulesCore (from `../node_modules/.pnpm/expo-modules-core@0.11.4/node_modules/expo-modules-core/ios`)" + - "EXSplashScreen (from `../node_modules/.pnpm/expo-splash-screen@0.16.2_expo@46.0.9/node_modules/expo-splash-screen/ios`)" - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) @@ -413,6 +415,7 @@ DEPENDENCIES: - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" + - "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) @@ -430,21 +433,21 @@ EXTERNAL SOURCES: DoubleConversion: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" EXApplication: - :path: "../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.2/node_modules/expo-application/ios" + :path: "../node_modules/.pnpm/expo-application@4.2.2_expo@46.0.9/node_modules/expo-application/ios" EXConstants: - :path: "../node_modules/.pnpm/expo-constants@13.2.3_expo@46.0.2/node_modules/expo-constants/ios" + :path: "../node_modules/.pnpm/expo-constants@13.2.3_expo@46.0.9/node_modules/expo-constants/ios" EXFileSystem: - :path: "../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.2/node_modules/expo-file-system/ios" + :path: "../node_modules/.pnpm/expo-file-system@14.1.0_expo@46.0.9/node_modules/expo-file-system/ios" EXFont: - :path: "../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.2/node_modules/expo-font/ios" + :path: "../node_modules/.pnpm/expo-font@10.2.0_expo@46.0.9/node_modules/expo-font/ios" Expo: - :path: "../node_modules/.pnpm/expo@46.0.2_@babel+core@7.18.10/node_modules/expo" + :path: "../node_modules/.pnpm/expo@46.0.9_@babel+core@7.18.10/node_modules/expo" ExpoKeepAwake: - :path: "../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.2/node_modules/expo-keep-awake/ios" + :path: "../node_modules/.pnpm/expo-keep-awake@10.2.0_expo@46.0.9/node_modules/expo-keep-awake/ios" ExpoModulesCore: - :path: "../node_modules/.pnpm/expo-modules-core@0.11.3/node_modules/expo-modules-core/ios" + :path: "../node_modules/.pnpm/expo-modules-core@0.11.4/node_modules/expo-modules-core/ios" EXSplashScreen: - :path: "../node_modules/.pnpm/expo-splash-screen@0.16.1_expo@46.0.2/node_modules/expo-splash-screen/ios" + :path: "../node_modules/.pnpm/expo-splash-screen@0.16.2_expo@46.0.9/node_modules/expo-splash-screen/ios" FBLazyVector: :path: "../node_modules/react-native/Libraries/FBLazyVector" FBReactNativeSpec: @@ -511,6 +514,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" RNCAsyncStorage: :path: "../node_modules/@react-native-async-storage/async-storage" + RNCMaskedView: + :path: "../node_modules/@react-native-masked-view/masked-view" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNReanimated: @@ -529,50 +534,51 @@ SPEC CHECKSUMS: EXConstants: 75c40827af38bd6bfcf69f880a5b45037eeff9c9 EXFileSystem: 927e0a8885aa9c49e50fc38eaba2c2389f2f1019 EXFont: a5d80bd9b3452b2d5abbce2487da89b0150e6487 - Expo: a2d9d4d17b9c97beab797c54220b305708f60e87 + Expo: 73412414e62f5cbc6e713def821de70b92cd3ad6 ExpoKeepAwake: 0e8f18142e71bbf2c7f6aa66ebed249ba1420320 - ExpoModulesCore: 8303cc952788be09fc6eab62815d257016ae6dec - EXSplashScreen: 31ab6df6d23e97e074d1330224741979943f1d82 - FBLazyVector: 1d83d91816fa605d16227a83f1b2e71c8df09d22 - FBReactNativeSpec: 06454fe46192886c1bc472593057015292ba37ee + ExpoModulesCore: e281bb7b78ea47e227dd5af94d04b24d8b2e1255 + EXSplashScreen: 799bece80089219b2c989c1082d70f3b00995cda + FBLazyVector: c71b8c429a8af2aff1013934a7152e9d9d0c937d + FBReactNativeSpec: 3cc5cff7d792e74a875be91e56d6242335016f50 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a - hermes-engine: ff1ba576165861a94a0d101b0a351a8ca2149f36 + hermes-engine: 761a544537e62df2a37189389b9d2654dc1f75af libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a - RCTRequired: 66822c147facf02f7774af99825e0a31e39df42e - RCTTypeSafety: 309306c4e711b14a83c55c2816a6cc490ec19827 - React: a779632422a918b26db4f1b57225a41c14d20525 - React-bridging: 96055aa45f0417898d7833e251f4ae79d28acef7 - React-callinvoker: 02df4d620df286381ff3f99180fb24feceaf01cc - React-Codegen: 06613a5e753c3af2dca0d6e7dd02944a3d77c3f6 - React-Core: 638d54d64048aa635e7c583fb0d8425206f446b4 - React-CoreModules: f706ec2a1939387517cadc6ce0d2ef0f20fccb53 - React-cxxreact: ec183b7f6fec01e7167f38c1c64a03f68dca7fb2 - React-hermes: a97962948f74aaefffd4fe00bdafafbc245b08af - React-jsi: ed7dc77f5193dca9c73cec90bfec409e7ddfe401 - React-jsiexecutor: 1842ca163b160aeb224d2c65b2a60c393b273c67 - React-jsinspector: bb2605f98aada5d81f3494690da3ef3b4ff3b716 - React-logger: 23a50ef4c18bf9adbb51e2c979318e6b3a2e44a1 + RCTRequired: bd9d2ab0fda10171fcbcf9ba61a7df4dc15a28f4 + RCTTypeSafety: e44e139bf6ec8042db396201834fc2372f6a21cd + React: 482cd1ba23c471be1aed3800180be2427418d7be + React-bridging: c2ea4fed6fe4ed27c12fd71e88b5d5d3da107fde + React-callinvoker: d4d1f98163fb5e35545e910415ef6c04796bb188 + React-Codegen: ff35fb9c7f6ec2ed34fb6de2e1099d88dfb25f2f + React-Core: 4d3443a45b67c71d74d7243ddde9569d1e4f4fad + React-CoreModules: 70be25399366b5632ab18ecf6fe444a8165a7bea + React-cxxreact: 822d3794fc0bf206f4691592f90e086dd4f92228 + React-hermes: 7f67b8363288258c3b0cd4aef5975cb7f0b9549a + React-jsi: ffa51cbc9a78cc156cf61f79ed52ecb76dc6013b + React-jsiexecutor: a27badbbdbc0ff781813370736a2d1c7261181d4 + React-jsinspector: 8a3d3f5dcd23a91e8c80b1bf0e96902cd1dca999 + React-logger: 1088859f145b8f6dd0d3ed051a647ef0e3e80fad react-native-safe-area-context: 6c12e3859b6f27b25de4fee8201cfb858432d8de - React-perflogger: 39d2ba8cbcac54d1bb1d9a980dab348e96aef467 - React-RCTActionSheet: b1ad907a2c8f8e4d037148ca507b7f2d6ab1c66d - React-RCTAnimation: 914a9ba46fb6e7376f7709c7ce825d53b47ca2ee - React-RCTBlob: de62fd5edc5c36951f0b113bf252eb43b7131f79 - React-RCTImage: aa0749a8d748b34942c7e71ac5d9f42be8b70cf3 - React-RCTLinking: 595a9f8fbf4d6634bff28d1175b3523b61466612 - React-RCTNetwork: 0559fd0fccb01f89c638baa43c8d185dc8008626 - React-RCTSettings: 8e492a25a62f1ef6323f82ce652ae87fa59c82ca - React-RCTText: 17457cde6ef8832ba43c886baebb6627c5d7ed18 - React-RCTVibration: dd8099eb46e9cee4692934bc8cbe5e9a4f5e8d31 - React-runtimeexecutor: 607eb048e22a16388c908ee1f6644200e8d1e19b - ReactCommon: af7636436b382db7cde4583bbd642f0978e6e3ed + React-perflogger: cb386fd44c97ec7f8199c04c12b22066b0f2e1e0 + React-RCTActionSheet: f803a85e46cf5b4066c2ac5e122447f918e9c6e5 + React-RCTAnimation: 19c80fa950ccce7f4db76a2a7f2cf79baae07fc7 + React-RCTBlob: f36ab97e2d515c36df14a1571e50056be80413d5 + React-RCTImage: 2c8f0a329a116248e82f8972ffe806e47c6d1cfa + React-RCTLinking: 670f0223075aff33be3b89714f1da4f5343fc4af + React-RCTNetwork: 09385b73f4ff1f46bd5d749540fb33f69a7e5908 + React-RCTSettings: 33b12d3ac7a1f2eba069ec7bd1b84345263b3bbe + React-RCTText: a1a3ea902403bd9ae4cf6f7960551dc1d25711b5 + React-RCTVibration: 9adb4a3cbb598d1bbd46a05256f445e4b8c70603 + React-runtimeexecutor: 61ee22a8cdf8b6bb2a7fb7b4ba2cc763e5285196 + ReactCommon: 8f67bd7e0a6afade0f20718f859dc8c2275f2e83 RNCAsyncStorage: d81ee5c3db1060afd49ea7045ad460eff82d2b7d + RNCMaskedView: cb9670ea9239998340eaab21df13fa12a1f9de15 RNGestureHandler: bad495418bcbd3ab47017a38d93d290ebd406f50 RNReanimated: 2cf7451318bb9cc430abeec8d67693f9cf4e039c RNScreens: 4a1af06327774490d97342c00aee0c2bafb497b7 - RNSVG: f3b60aeeaa81960e2e0536c3a9eef50b667ef3a9 - Yoga: 44c64131616253fa83366295acdbce3d14926041 + RNSVG: 42a0c731b11179ebbd27a3eeeafa7201ebb476ff + Yoga: ff994563b2fd98c982ca58e8cd9db2cdaf4dda74 PODFILE CHECKSUM: b77befb1871220c1a94408eeae0857d78b685698 diff --git a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj index 3a8ac6a59..4299b55a6 100644 --- a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj @@ -3,36 +3,38 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ + 08C620EC6F30A0663310BBC1 /* libPods-Spacedrive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 080FB394AD883D9705C115A6 /* libPods-Spacedrive.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; - 519D1250147D911454D7DB76 /* libPods-Spacedrive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F3F276D840CDBF9D0D9D548D /* libPods-Spacedrive.a */; }; + 5574975428A2496C00851D5A /* SDCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 5574975328A2496C00851D5A /* SDCore.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; C95AE27BB525EFF3F02CEC11 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E71A6EFA9EA8F4C11F42FA /* ExpoModulesProvider.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 080FB394AD883D9705C115A6 /* libPods-Spacedrive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Spacedrive.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* Spacedrive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Spacedrive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 5574975328A2496C00851D5A /* SDCore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDCore.m; sourceTree = ""; }; + 5574975528A2498000851D5A /* SDCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDCore.h; sourceTree = ""; }; + 5574975628A24E0D00851D5A /* sdcore-universal-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "sdcore-universal-ios.a"; path = "../../../target/sdcore-universal-ios.a"; sourceTree = ""; }; 56E71A6EFA9EA8F4C11F42FA /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Spacedrive/ExpoModulesProvider.swift"; sourceTree = ""; }; - 6C2E3173556A471DD304B334 /* Pods-mobilenew.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobilenew.debug.xcconfig"; path = "Target Support Files/Pods-mobilenew/Pods-mobilenew.debug.xcconfig"; sourceTree = ""; }; - 78E58D73F5113B1BD543EE4D /* Pods-Spacedrive.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.debug.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.debug.xcconfig"; sourceTree = ""; }; - 7A4D352CD337FB3A3BF06240 /* Pods-mobilenew.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-mobilenew.release.xcconfig"; path = "Target Support Files/Pods-mobilenew/Pods-mobilenew.release.xcconfig"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Expo.plist; path = Supporting/Expo.plist; sourceTree = ""; }; - D8F0094C07EEE7528760BD08 /* Pods-Spacedrive.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.release.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.release.xcconfig"; sourceTree = ""; }; + CD5534017C1F13AB6E57EC53 /* Pods-Spacedrive.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.debug.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.debug.xcconfig"; sourceTree = ""; }; + E661E5E8069E06E621509713 /* Pods-Spacedrive.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spacedrive.release.xcconfig"; path = "Target Support Files/Pods-Spacedrive/Pods-Spacedrive.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - F3F276D840CDBF9D0D9D548D /* libPods-Spacedrive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Spacedrive.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -40,7 +42,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 519D1250147D911454D7DB76 /* libPods-Spacedrive.a in Frameworks */, + 08C620EC6F30A0663310BBC1 /* libPods-Spacedrive.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -58,6 +60,8 @@ 13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB71A68108700A75B9A /* main.m */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, + 5574975328A2496C00851D5A /* SDCore.m */, + 5574975528A2498000851D5A /* SDCore.h */, ); path = Spacedrive; sourceTree = ""; @@ -73,8 +77,9 @@ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { isa = PBXGroup; children = ( + 5574975628A24E0D00851D5A /* sdcore-universal-ios.a */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - F3F276D840CDBF9D0D9D548D /* libPods-Spacedrive.a */, + 080FB394AD883D9705C115A6 /* libPods-Spacedrive.a */, ); name = Frameworks; sourceTree = ""; @@ -120,10 +125,8 @@ D65327D7A22EEC0BE12398D9 /* Pods */ = { isa = PBXGroup; children = ( - 6C2E3173556A471DD304B334 /* Pods-mobilenew.debug.xcconfig */, - 7A4D352CD337FB3A3BF06240 /* Pods-mobilenew.release.xcconfig */, - 78E58D73F5113B1BD543EE4D /* Pods-Spacedrive.debug.xcconfig */, - D8F0094C07EEE7528760BD08 /* Pods-Spacedrive.release.xcconfig */, + CD5534017C1F13AB6E57EC53 /* Pods-Spacedrive.debug.xcconfig */, + E661E5E8069E06E621509713 /* Pods-Spacedrive.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -143,14 +146,15 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Spacedrive" */; buildPhases = ( - 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, + 9F553C6F8AA059AB72DAA720 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, + 55B1130D28AB3061006C377F /* Build Spacedrive Core */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, - 0D580678BEFB3E7EF09A2CFE /* [CP] Embed Pods Frameworks */, + C93832A891603A9ED877B5D2 /* [CP] Embed Pods Frameworks */, + 20C3591E751EF7109AA55859 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -221,7 +225,50 @@ 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"; }; - 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = { + 20C3591E751EF7109AA55859 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 55B1130D28AB3061006C377F /* Build Spacedrive Core */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "../../../core/src/*.rs", + "../rust/src/*.rs", + "../../../core/src/*/*.rs", + ); + name = "Build Spacedrive Core"; + outputFileListPaths = ( + ); + outputPaths = ( + "../../../target/sdcore-universal-ios.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/zsh; + shellScript = "set -e\n\nif [[ -n \"${DEVELOPER_SDK_DIR:-}\" ]]; then\n # Assume we're in Xcode, which means we're probably cross-compiling.\n # In this case, we need to add an extra library search path for build scripts and proc-macros,\n # which run on the host instead of the target.\n # (macOS Big Sur does not have linkable libraries in /usr/lib/.)\n export LIBRARY_PATH=\"${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}\"\nfi\n\nCARGO_FLAGS=\nif [[ \"$BUILDVARIANT\" != \"debug\" ]]; then\n CARGO_FLAGS=--release\nfi\n\nTARGET_DIRECTORY=../../../target\nif [[ $PLATFORM_NAME = \"iphonesimulator\" ]]\nthen\n cargo build -p sdcore-lib $CARGO_FLAGS --lib --target aarch64-apple-ios-sim\n lipo -create -output $TARGET_DIRECTORY/libsdcore-iossim.a $TARGET_DIRECTORY/aarch64-apple-ios-sim/release/libsdcore.a\nelse\n cargo build -p sdcore-lib $CARGO_FLAGS --lib --target aarch64-apple-ios\n lipo -create -output $TARGET_DIRECTORY/libsdcore-ios.a $TARGET_DIRECTORY/aarch64-apple-ios/release/libsdcore.a\nfi\n"; + }; + 9F553C6F8AA059AB72DAA720 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -243,7 +290,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; }; - 0D580678BEFB3E7EF09A2CFE /* [CP] Embed Pods Frameworks */ = { + C93832A891603A9ED877B5D2 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -261,26 +308,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -310,6 +337,7 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, C95AE27BB525EFF3F02CEC11 /* ExpoModulesProvider.swift in Sources */, + 5574975428A2496C00851D5A /* SDCore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -318,7 +346,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 78E58D73F5113B1BD543EE4D /* Pods-Spacedrive.debug.xcconfig */; + baseConfigurationReference = CD5534017C1F13AB6E57EC53 /* Pods-Spacedrive.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -333,12 +361,73 @@ ); INFOPLIST_FILE = Spacedrive/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/EXFont\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Expo\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ExpoKeepAwake\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ExpoModulesCore\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RCTTypeSafety\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNCMaskedView\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNGestureHandler\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNReanimated\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNScreens\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Core\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-CoreModules\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTAnimation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTBlob\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTImage\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTLinking\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTNetwork\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTSettings\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTText\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTVibration\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-bridging\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-hermes\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-jsi\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-jsiexecutor\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-jsinspector\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-logger\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-perflogger\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Yoga\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/fmt\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/glog\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/libevent\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/react-native-safe-area-context\"", + /usr/lib/swift, + ../../../target, + ); OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); + "OTHER_LDFLAGS[sdk=iphoneos*]" = ( + "$(inherited)", + "-ObjC", + "-lc++", + "-lsdcore-ios", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "$(inherited)", + "-ObjC", + "-lc++", + "-lsdcore-iossim", + ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.spacedrive.app; PRODUCT_NAME = Spacedrive; @@ -351,7 +440,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D8F0094C07EEE7528760BD08 /* Pods-Spacedrive.release.xcconfig */; + baseConfigurationReference = E661E5E8069E06E621509713 /* Pods-Spacedrive.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; @@ -361,12 +450,73 @@ DEVELOPMENT_TEAM = 72SE38W7T9; INFOPLIST_FILE = Spacedrive/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/EXFont\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Expo\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ExpoKeepAwake\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ExpoModulesCore\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RCTTypeSafety\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNCMaskedView\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNGestureHandler\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNReanimated\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RNScreens\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Core\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-CoreModules\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTAnimation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTBlob\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTImage\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTLinking\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTNetwork\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTSettings\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTText\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTVibration\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-bridging\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-hermes\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-jsi\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-jsiexecutor\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-jsinspector\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-logger\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/React-perflogger\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Yoga\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/fmt\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/glog\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/libevent\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/react-native-safe-area-context\"", + /usr/lib/swift, + ../../../target, + ); OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); + "OTHER_LDFLAGS[sdk=iphoneos*]" = ( + "$(inherited)", + "-ObjC", + "-lc++", + "-lsdcore-ios", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "$(inherited)", + "-ObjC", + "-lc++", + "-lsdcore-iossim", + ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.spacedrive.app; PRODUCT_NAME = Spacedrive; @@ -425,7 +575,10 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); LIBRARY_SEARCH_PATHS = "\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -476,7 +629,10 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); LIBRARY_SEARCH_PATHS = "\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = NO; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; diff --git a/apps/mobile/ios/Spacedrive/Info.plist b/apps/mobile/ios/Spacedrive/Info.plist index 05ff2178f..d9d78a1b6 100644 --- a/apps/mobile/ios/Spacedrive/Info.plist +++ b/apps/mobile/ios/Spacedrive/Info.plist @@ -1,77 +1,81 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Spacedrive - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 0.0.1 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - spacedrive - com.spacedrive.app - - - - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSExceptionDomains - - localhost - - NSExceptionAllowsInsecureHTTPLoads - - - - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - armv7 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - \ No newline at end of file + + UIBackgroundModes + + remote-notification + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Spacedrive + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 0.0.1 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + spacedrive + com.spacedrive.app + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + armv7 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + + + diff --git a/apps/mobile/ios/Spacedrive/SDCore.h b/apps/mobile/ios/Spacedrive/SDCore.h new file mode 100644 index 000000000..cdcac491d --- /dev/null +++ b/apps/mobile/ios/Spacedrive/SDCore.h @@ -0,0 +1,17 @@ +// +// SDCore.h +// Spacedrive +// +// Created by Oscar Beaumont on 9/8/2022. +// + +#import +#import + +#ifndef SDCore_h +#define SDCore_h + +@interface SDCore : RCTEventEmitter +@end + +#endif /* SDCore_h */ diff --git a/apps/mobile/ios/Spacedrive/SDCore.m b/apps/mobile/ios/Spacedrive/SDCore.m new file mode 100644 index 000000000..969b2c106 --- /dev/null +++ b/apps/mobile/ios/Spacedrive/SDCore.m @@ -0,0 +1,81 @@ +// +// SDCore.m +// Spacedrive +// +// This file will not work unless ARC is disabled. Do this by setting the compiler flag '-fno-objc-arc' on this file in Settings > Build Phases > Compile Sources. +// This file also expects the Spacedrive Rust library to be linked in Settings > Build Phases > Link Binary with Libraries. This is the crude way, you should link the core with custom linker flags so that you can do it conditonally based on target OS. +// This file also expects a Build Phase to be setup which compiles the Rust prior to linking the sdcore library to the IOS build. +// This file also expects you to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist +// +// Created by Oscar Beaumont on 9/8/2022. +// + +#import "SDCore.h" +#import + +// is a function defined in Rust which starts a listener for Rust events. +void register_core_event_listener(id objc_class); + +// is a function defined in Rust which is responsible for handling messages from the frontend. +void sd_core_msg(const char* query, void* resolve); + +// is called by Rust to determine the base directory to store data in. This is only done when initialising the Node. +const char* get_data_directory(void) +{ + NSArray *dirPaths = dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, + NSUserDomainMask, YES); + const char *docDir = [ [dirPaths objectAtIndex:0] UTF8String]; + return docDir; +} + +// is called by Rust with a void* to the resolve function (of type RCTPromiseResolveBlock) to call it. +// Due to 'RCTPromiseResolveBlock' being an Objective-C block it is hard to call from Rust. +void call_resolve(void *resolvePtr, const char* resultRaw) +{ + RCTPromiseResolveBlock resolve = (__bridge RCTPromiseResolveBlock) resolvePtr; + NSString *result = [NSString stringWithUTF8String:resultRaw]; + resolve(result); + [result release]; +} + +@implementation SDCore +{ + bool registeredWithRust; + bool hasListeners; +} + +-(void)startObserving { + if (!registeredWithRust) + { + register_core_event_listener(self); + registeredWithRust = true; + } + + hasListeners = YES; +} + +-(void)stopObserving { + hasListeners = NO; +} + +- (void)sendCoreEvent: (NSString*)query { + if (hasListeners) { + [self sendEventWithName:@"SDCoreEvent" body:query]; + } +} + +- (NSArray *)supportedEvents { + return @[@"SDCoreEvent"]; +} + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(sd_core_msg: (NSString *)queryRaw + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + const char *query = [queryRaw UTF8String]; + sd_core_msg(query, (__bridge void*) [resolve retain]); +} + +@end diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index c7768fd74..286802d1b 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -3,7 +3,6 @@ const MetroSymlinksResolver = require('@rnx-kit/metro-resolver-symlinks'); // Might not need these anymore. const [SDAssetsPath, SDAssetsPathExclude] = resolveUniqueModule('@sd/assets', '.'); -const [SDCorePath, SDCorePathExclude] = resolveUniqueModule('@sd/core', '.'); const [babelRuntimePath, babelRuntimeExclude] = resolveUniqueModule('@babel/runtime'); const [reactPath, reactExclude] = resolveUniqueModule('react'); @@ -21,7 +20,6 @@ const metroConfig = makeMetroConfig({ extraNodeModules: { '@babel/runtime': babelRuntimePath, '@sd/assets': SDAssetsPath, - '@sd/core': SDCorePath, 'react': reactPath, 'react-native-svg': reactSVGPath }, @@ -29,7 +27,6 @@ const metroConfig = makeMetroConfig({ blockList: exclusionList([ babelRuntimeExclude, SDAssetsPathExclude, - SDCorePathExclude, reactExclude, reactSVGExclude ]), diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 9fe4e2594..711926bd7 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -4,7 +4,7 @@ "main": "index.js", "license": "GPL-3.0-only", "scripts": { - "dev": "expo start --dev-client", + "start": "expo start --dev-client", "android": "expo run:android", "ios": "expo run:ios", "lint": "eslint src/**/*.{ts,tsx} && tsc --noEmit" @@ -12,42 +12,45 @@ "dependencies": { "@expo/vector-icons": "^13.0.0", "@gorhom/bottom-sheet": "^4.4.3", - "@react-native-async-storage/async-storage": "^1.17.7", - "@react-navigation/bottom-tabs": "^6.3.2", - "@react-navigation/drawer": "^6.4.3", - "@react-navigation/native": "^6.0.11", - "@react-navigation/native-stack": "^6.7.0", + "@react-native-async-storage/async-storage": "~1.17.3", + "@react-native-masked-view/masked-view": "0.2.7", + "@react-navigation/bottom-tabs": "^6.3.3", + "@react-navigation/drawer": "^6.4.4", + "@react-navigation/native": "^6.0.12", + "@react-navigation/stack": "^6.2.3", + "@rspc/client": "^0.0.5", "@sd/assets": "file:../../packages/assets", - "@sd/core": "file:../../core", + "@tanstack/react-query": "^4.2.3", "byte-size": "^8.1.0", "class-variance-authority": "^0.2.3", - "expo": "~46.0.2", - "expo-font": "^10.2.0", - "expo-linking": "^3.2.2", - "expo-splash-screen": "~0.16.1", + "date-fns": "^2.29.2", + "expo": "~46.0.9", + "expo-font": "~10.2.0", + "expo-linking": "~3.2.2", + "expo-splash-screen": "~0.16.2", "expo-status-bar": "~1.4.0", "intl": "^1.2.5", "moti": "^0.18.0", "phosphor-react-native": "^1.1.2", - "react": "18.2.0", - "react-native": "0.69.3", - "react-native-gesture-handler": "^2.5.0", + "react": "18.0.0", + "react-native": "0.69.4", + "react-native-gesture-handler": "~2.5.0", "react-native-heroicons": "^2.2.0", - "react-native-reanimated": "^2.9.1", - "react-native-safe-area-context": "^4.3.1", - "react-native-screens": "^3.15.0", - "react-native-svg": "^12.3.0", + "react-native-reanimated": "~2.9.1", + "react-native-safe-area-context": "4.3.1", + "react-native-screens": "~3.15.0", + "react-native-svg": "13.0.0", "twrnc": "^3.4.0", "use-count-up": "^3.0.1", - "zustand": "^4.0.0" + "zustand": "^4.1.1" }, "devDependencies": { - "@babel/core": "^7.12.9", + "@babel/core": "^7.18.6", "@babel/runtime": "^7.18.9", "@rnx-kit/metro-config": "^1.2.36", "@rnx-kit/metro-resolver-symlinks": "^0.1.21", - "@types/react": "~18.0.15", - "@types/react-native": "~0.69.5", + "@types/react": "~18.0.0", + "@types/react-native": "~0.69.1", "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", "eslint": "^8.21.0", @@ -55,8 +58,7 @@ "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-native": "^4.0.0", - "metro-minify-terser": "^0.72.0", - "react-native-svg": "^12.4.3", + "metro-minify-terser": "^0.72.1", "react-native-svg-transformer": "^1.0.0", "typescript": "^4.7.4" }, diff --git a/apps/mobile/pnpm-lock.yaml b/apps/mobile/pnpm-lock.yaml index 1cf083f98ee040cefbbe37285f0b834a66c23d33..75295d7a7edc621dca97cf736398368a1f2d7935 100644 GIT binary patch delta 12747 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 delta 10117 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 diff --git a/apps/mobile/rust/Cargo.toml b/apps/mobile/rust/Cargo.toml new file mode 100644 index 000000000..d2140b3c6 --- /dev/null +++ b/apps/mobile/rust/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "sdcore-lib" +version = "0.1.0" +edition = "2021" +rust-version = "1.63.0" + +[lib] +name = "sdcore" +crate-type = ["staticlib", "cdylib"] # staticlib for IOS and cdylib for Android + +[dependencies] +once_cell = "1.13.0" +sdcore = { path = "../../../core", features = ["mobile", "p2p"], default-features = false } +rspc = { version = "0.0.4", features = [] } +serde_json = "1.0.83" +tokio = "1.20.1" +openssl = { version = "0.10.41", features = ["vendored"] } # Override features of transitive dependencies +openssl-sys = { version = "0.9.75", features = ["vendored"] } # Override features of transitive dependencies to support IOS Simulator on M1 + +[target.'cfg(target_os = "ios")'.dependencies] +objc = "0.2.7" +objc_id = "0.1.1" +objc-foundation = "0.1.1" + +# This is `not(ios)` instead of `android` because of https://github.com/mozilla/rust-android-gradle/issues/93 +[target.'cfg(not(target_os = "ios"))'.dependencies] +jni = "0.19.0" diff --git a/apps/mobile/rust/src/android.rs b/apps/mobile/rust/src/android.rs new file mode 100644 index 000000000..3f51a5d49 --- /dev/null +++ b/apps/mobile/rust/src/android.rs @@ -0,0 +1,111 @@ +use crate::{CLIENT_CONTEXT, EVENT_SENDER, NODE, RUNTIME}; +use jni::objects::{JClass, JObject, JString}; +use jni::JNIEnv; +use rspc::Request; +use sdcore::Node; +use tokio::sync::mpsc::unbounded_channel; + +#[no_mangle] +pub extern "system" fn Java_com_spacedrive_app_SDCore_registerCoreEventListener( + env: JNIEnv, + class: JClass, +) { + let jvm = env.get_java_vm().unwrap(); + let class = env.new_global_ref(class).unwrap(); + let (tx, mut rx) = unbounded_channel(); + let _ = EVENT_SENDER.set(tx); + + RUNTIME.spawn(async move { + while let Some(event) = rx.recv().await { + let data = match serde_json::to_string(&event) { + Ok(json) => json, + Err(err) => { + println!("Failed to serialize event: {}", err); + continue; + }, + }; + + let env = jvm.attach_current_thread().unwrap(); + env.call_method( + &class, + "sendCoreEvent", + "(Ljava/lang/String;)V", + &[env + .new_string(data) + .expect("Couldn't create java string!") + .into()], + ) + .unwrap(); + } + }); +} + +#[no_mangle] +pub extern "system" fn Java_com_spacedrive_app_SDCore_handleCoreMsg( + env: JNIEnv, + class: JClass, + query: JString, + callback: JObject, +) { + let jvm = env.get_java_vm().unwrap(); + let query: String = env + .get_string(query) + .expect("Couldn't get java string!") + .into(); + let class = env.new_global_ref(class).unwrap(); + let callback = env.new_global_ref(callback).unwrap(); + + RUNTIME.spawn(async move { + let request: Request = serde_json::from_str(&query).unwrap(); + + let node = &mut *NODE.lock().await; + let (node, router) = match node { + Some(node) => node.clone(), + None => { + let data_dir: String = { + let env = jvm.attach_current_thread().unwrap(); + let data_dir = env + .call_method( + &class, + "getDataDirectory", + "()Ljava/lang/String;", + &[], + ) + .unwrap() + .l() + .unwrap(); + + env.get_string(data_dir.into()).unwrap().into() + }; + + let new_node = Node::new(data_dir).await; + node.replace(new_node.clone()); + new_node + }, + }; + + let resp = serde_json::to_string( + &request + .handle( + node.get_request_context(), + &router, + &CLIENT_CONTEXT, + EVENT_SENDER.get(), + ) + .await, + ) + .unwrap(); + + let env = jvm.attach_current_thread().unwrap(); + env.call_method( + &callback, + "resolve", + "(Ljava/lang/Object;)V", + &[env + .new_string(resp) + .expect("Couldn't create java string!") + .into()], + ) + .unwrap(); + }); +} diff --git a/apps/mobile/rust/src/ios.rs b/apps/mobile/rust/src/ios.rs new file mode 100644 index 000000000..9abcc7c87 --- /dev/null +++ b/apps/mobile/rust/src/ios.rs @@ -0,0 +1,96 @@ +use crate::{CLIENT_CONTEXT, EVENT_SENDER, NODE, RUNTIME}; +use std::{ + ffi::{CStr, CString}, + os::raw::{c_char, c_void}, +}; +use tokio::sync::mpsc::unbounded_channel; + +use objc::{class, msg_send, runtime::Object, sel, sel_impl}; +use objc_foundation::{INSString, NSString}; +use objc_id::Id; +use rspc::Request; +use sdcore::Node; + +extern "C" { + fn get_data_directory() -> *const c_char; + fn call_resolve(resolve: *const c_void, result: *const c_char); +} + +// This struct wraps the function pointer which represent a Javascript Promise. We wrap the +// function pointers in a struct so we can unsafely assert to Rust that they are `Send`. +// We know they are send as we have ensured Objective-C won't deallocate the function pointer +// until `call_resolve` is called. +struct RNPromise(*const c_void); + +unsafe impl Send for RNPromise {} + +impl RNPromise { + // resolve the promise + unsafe fn resolve(self, result: CString) { + call_resolve(self.0, result.as_ptr()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn register_core_event_listener(id: *mut Object) { + let id = Id::::from_ptr(id); + + let (tx, mut rx) = unbounded_channel(); + let _ = EVENT_SENDER.set(tx); + + RUNTIME.spawn(async move { + while let Some(event) = rx.recv().await { + let data = match serde_json::to_string(&event) { + Ok(json) => json, + Err(err) => { + println!("Failed to serialize event: {}", err); + continue; + }, + }; + let data = NSString::from_str(&data); + let _: () = msg_send![id, sendCoreEvent: data]; + } + }); +} + +#[no_mangle] +pub unsafe extern "C" fn sd_core_msg(query: *const c_char, resolve: *const c_void) { + // This string is cloned to the Rust heap. This is important as Objective-C may remove the query once this function completions but prior to the async block finishing. + let query = CStr::from_ptr(query).to_str().unwrap().to_string(); + + let resolve = RNPromise(resolve); + RUNTIME.spawn(async move { + let request: Request = serde_json::from_str(&query).unwrap(); + + let node = &mut *NODE.lock().await; + let (node, router) = match node { + Some(node) => node.clone(), + None => { + let doc_dir = CStr::from_ptr(get_data_directory()) + .to_str() + .unwrap() + .to_string(); + let new_node = Node::new(doc_dir).await; + node.replace(new_node.clone()); + new_node + }, + }; + + resolve.resolve( + CString::new( + serde_json::to_vec( + &request + .handle( + node.get_request_context(), + &router, + &CLIENT_CONTEXT, + EVENT_SENDER.get(), + ) + .await, + ) + .unwrap(), + ) + .unwrap(), + ) + }); +} diff --git a/apps/mobile/rust/src/lib.rs b/apps/mobile/rust/src/lib.rs new file mode 100644 index 000000000..10c476149 --- /dev/null +++ b/apps/mobile/rust/src/lib.rs @@ -0,0 +1,31 @@ +use std::sync::Arc; + +use once_cell::sync::{Lazy, OnceCell}; +use rspc::{ClientContext, Response}; +use sdcore::{api::Router, Node}; +use tokio::{ + runtime::Runtime, + sync::{mpsc::UnboundedSender, Mutex}, +}; + +#[allow(dead_code)] +pub(crate) static RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); + +#[allow(dead_code)] +pub(crate) static NODE: Lazy, Arc)>>> = + Lazy::new(|| Mutex::new(None)); + +#[allow(dead_code)] +pub(crate) static CLIENT_CONTEXT: Lazy = Lazy::new(|| ClientContext { + subscriptions: Default::default(), +}); + +#[allow(dead_code)] +pub(crate) static EVENT_SENDER: OnceCell> = OnceCell::new(); + +#[cfg(target_os = "ios")] +mod ios; + +/// This is `not(ios)` instead of `android` because of https://github.com/mozilla/rust-android-gradle/issues/93 +#[cfg(not(target_os = "ios"))] +mod android; diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index 2995de696..14794ffd7 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -1,18 +1,26 @@ +import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; import { DefaultTheme, NavigationContainer, Theme } from '@react-navigation/native'; +import { createClient } from '@rspc/client'; import { StatusBar } from 'expo-status-bar'; import React, { useEffect } from 'react'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { useDeviceContext } from 'twrnc'; +import { GlobalModals } from './components/modals/GlobalModals'; +import { ReactNativeTransport, queryClient, rspc, useInvalidateQuery } from './hooks/rspc'; import useCachedResources from './hooks/useCachedResources'; import { getItemFromStorage } from './lib/storage'; import tw from './lib/tailwind'; import RootNavigator from './navigation'; import OnboardingNavigator from './navigation/OnboardingNavigator'; import { useOnboardingStore } from './stores/useOnboardingStore'; +import type { Operations } from './types/bindings'; + +const client = createClient({ + transport: new ReactNativeTransport() +}); -// const NavigatorTheme: Theme = { ...DefaultTheme, colors: { @@ -41,14 +49,27 @@ export default function App() { return null; } else { return ( - - - - {showOnboarding ? : } - - - - + + <> + + + + + + + {showOnboarding ? : } + + + + + + + ); } } + +function InvalidateQuery() { + useInvalidateQuery(); + return null; +} diff --git a/apps/mobile/src/assets/temp/folder-white.svg b/apps/mobile/src/assets/temp/folder-white.svg deleted file mode 100644 index 069ff8b01..000000000 --- a/apps/mobile/src/assets/temp/folder-white.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/apps/mobile/src/assets/temp/folder.svg b/apps/mobile/src/assets/temp/folder.svg deleted file mode 100644 index 5b9be3c5e..000000000 --- a/apps/mobile/src/assets/temp/folder.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/apps/mobile/src/assets/temp/logo.png b/apps/mobile/src/assets/temp/logo.png deleted file mode 100644 index 37c70ec1be732b99b2519176b8e76405e8edfb16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129137 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 diff --git a/apps/mobile/src/components/base/Divider.tsx b/apps/mobile/src/components/base/Divider.tsx new file mode 100644 index 000000000..f936c2884 --- /dev/null +++ b/apps/mobile/src/components/base/Divider.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { StyleProp, Text, View, ViewStyle } from 'react-native'; + +import tw from '../../lib/tailwind'; + +type DividerProps = { + style?: StyleProp; +}; + +const Divider = ({ style }: DividerProps) => { + return ; +}; + +export default Divider; diff --git a/apps/mobile/src/components/browse/BrowseLocationItem.tsx b/apps/mobile/src/components/browse/BrowseLocationItem.tsx index 383b50624..2298925c9 100644 --- a/apps/mobile/src/components/browse/BrowseLocationItem.tsx +++ b/apps/mobile/src/components/browse/BrowseLocationItem.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Pressable, Text, View } from 'react-native'; import tw from '../../lib/tailwind'; -import FolderIcon from '../icons/Folder'; +import FolderIcon from '../icons/FolderIcon'; interface BrowseLocationItemProps { folderName: string; diff --git a/apps/mobile/src/components/device/Device.tsx b/apps/mobile/src/components/device/Device.tsx index 3eb626d82..d8a24afad 100644 --- a/apps/mobile/src/components/device/Device.tsx +++ b/apps/mobile/src/components/device/Device.tsx @@ -1,19 +1,12 @@ -import { FilePath } from '@sd/core'; import { Cloud, Desktop, DeviceMobileCamera, Laptop } from 'phosphor-react-native'; import React from 'react'; import { FlatList, Text, View } from 'react-native'; import { LockClosedIcon } from 'react-native-heroicons/solid'; import tw from '../../lib/tailwind'; +import { FilePath } from '../../types/bindings'; import FileItem from '../file/FileItem'; -export interface DeviceProps { - name: string; - size: string; - type: 'laptop' | 'desktop' | 'phone' | 'server'; - locations: Array<{ name: string; folder?: boolean; format?: string; icon?: string }>; -} - const placeholderFileItems: FilePath[] = [ { is_dir: true, @@ -272,10 +265,18 @@ const placeholderFileItems: FilePath[] = [ } ]; +export interface DeviceProps { + name: string; + size: string; + type: 'laptop' | 'desktop' | 'phone' | 'server'; + locations: { name: string; folder?: boolean; format?: string; icon?: string }[]; + runningJob?: { amount: number; task: string }; +} + const Device = ({ name, locations, size, type }: DeviceProps) => { return ( - + {type === 'phone' && ( @@ -299,7 +300,7 @@ const Device = ({ name, locations, size, type }: DeviceProps) => { renderItem={({ item }) => } keyExtractor={(item) => item.id.toString()} horizontal - contentContainerStyle={tw`mt-4 ml-2`} + contentContainerStyle={tw`mt-3 mb-5`} showsHorizontalScrollIndicator={false} /> diff --git a/apps/mobile/src/components/drawer/DrawerContent.tsx b/apps/mobile/src/components/drawer/DrawerContent.tsx index 9f57c21d9..0a727600b 100644 --- a/apps/mobile/src/components/drawer/DrawerContent.tsx +++ b/apps/mobile/src/components/drawer/DrawerContent.tsx @@ -1,56 +1,110 @@ import { DrawerContentScrollView } from '@react-navigation/drawer'; import { DrawerContentComponentProps } from '@react-navigation/drawer/lib/typescript/src/types'; -import { House } from 'phosphor-react-native'; +import { getFocusedRouteNameFromRoute } from '@react-navigation/native'; import React from 'react'; -import { Pressable, Text, View } from 'react-native'; +import { ColorValue, Platform, Pressable, Text, View } from 'react-native'; import { CogIcon } from 'react-native-heroicons/solid'; import Layout from '../../constants/Layout'; import tw from '../../lib/tailwind'; -import type { DrawerNavParamList } from '../../navigation/DrawerNavigator'; -import { valueof } from '../../types/helper'; -import DrawerItem from './DrawerItem'; +import CollapsibleView from '../layout/CollapsibleView'; +import DrawerLocationItem from './DrawerLocationItem'; +import DrawerLogo from './DrawerLogo'; +import DrawerTagItem from './DrawerTagItem'; -const drawerHeight = Layout.window.height * 0.85; +const placeholderLocationData = [ + { + id: 1, + name: 'Spacedrive' + }, + { + id: 2, + name: 'Content' + } +]; +const placeholderTagsData = [ + { + id: 1, + name: 'Funny', + color: tw.color('blue-500') + }, + { + id: 2, + name: 'Twitch', + color: tw.color('purple-500') + }, + { + id: 3, + name: 'BlackMagic', + color: tw.color('red-500') + } +]; -// This is a hacky way to get the active route name and params but it works and it's typed... +const drawerHeight = Platform.select({ + ios: Layout.window.height * 0.85, + android: Layout.window.height * 0.9 +}); -interface ActiveRoute { - key: string; - name: keyof DrawerNavParamList; - params: valueof>; -} - -const getActiveRouteState = function (state: any): ActiveRoute { +const getActiveRouteState = function (state: any) { if (!state.routes || state.routes.length === 0 || state.index >= state.routes.length) { return state; } - const childActiveRoute = state.routes[state.index]; return getActiveRouteState(childActiveRoute); }; -// Overriding the default to add typing for our params. -// interface DrawerContentComponentProps { -// state: DrawerNavigationState; -// navigation: NavigationHelpers & -// DrawerActionHelpers; -// // descriptors type is generic -// descriptors: DrawerDescriptorMap; -// } +const DrawerContent = ({ navigation, state }: DrawerContentComponentProps) => { + const stackName = getFocusedRouteNameFromRoute(getActiveRouteState(state)) ?? 'OverviewStack'; -const DrawerContent = ({ descriptors, navigation, state }: DrawerContentComponentProps) => { return ( - + - TODO: Library Selection - } - onPress={() => navigation.jumpTo('Home')} - isSelected={getActiveRouteState(state).name === 'Home'} - /> + + TODO: Library Selection + {/* Locations */} + + {placeholderLocationData.map((location) => ( + + navigation.navigate(stackName, { + screen: 'Location', + params: { id: location.id } + }) + } + /> + ))} + {/* Add Location */} + + + Add Location + + + + {/* Tags */} + + {placeholderTagsData.map((tag) => ( + + navigation.navigate(stackName, { + screen: 'Tag', + params: { id: tag.id } + }) + } + tagColor={tag.color as ColorValue} + /> + ))} + {/* Settings */} navigation.navigate('Settings')}> diff --git a/apps/mobile/src/components/drawer/DrawerItem.tsx b/apps/mobile/src/components/drawer/DrawerItem.tsx deleted file mode 100644 index 99daf69e9..000000000 --- a/apps/mobile/src/components/drawer/DrawerItem.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { Pressable, Text, View } from 'react-native'; - -import tw from '../../lib/tailwind'; - -interface DrawerProps { - label: string; - onPress: () => void; - icon: JSX.Element; - isSelected: boolean; -} - -const DrawerItem: React.FC = (props) => { - const { label, icon, onPress, isSelected } = props; - return ( - - - {icon} - - {label} - - - - ); -}; - -export default DrawerItem; diff --git a/apps/mobile/src/components/drawer/DrawerLocationItem.tsx b/apps/mobile/src/components/drawer/DrawerLocationItem.tsx new file mode 100644 index 000000000..a9f74ae74 --- /dev/null +++ b/apps/mobile/src/components/drawer/DrawerLocationItem.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Pressable, Text, View } from 'react-native'; + +import tw from '../../lib/tailwind'; +import FolderIcon from '../icons/FolderIcon'; + +interface DrawerLocationItemProps { + folderName: string; + onPress: () => void; +} + +const DrawerLocationItem: React.FC = (props) => { + const { folderName, onPress } = props; + return ( + + + + + {folderName} + + + + ); +}; + +export default DrawerLocationItem; diff --git a/apps/mobile/src/components/drawer/DrawerLogo.tsx b/apps/mobile/src/components/drawer/DrawerLogo.tsx new file mode 100644 index 000000000..afe1ce54c --- /dev/null +++ b/apps/mobile/src/components/drawer/DrawerLogo.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Image, Text, View } from 'react-native'; + +import tw from '../../lib/tailwind'; +import Divider from '../base/Divider'; + +const DrawerLogo = () => { + return ( + <> + + + Spacedrive + + + + ); +}; + +export default DrawerLogo; diff --git a/apps/mobile/src/components/drawer/DrawerScreenWrapper.tsx b/apps/mobile/src/components/drawer/DrawerScreenWrapper.tsx deleted file mode 100644 index 3bc677b40..000000000 --- a/apps/mobile/src/components/drawer/DrawerScreenWrapper.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useDrawerProgress } from '@react-navigation/drawer'; -import React from 'react'; -import Animated, { Extrapolate, interpolate, useAnimatedStyle } from 'react-native-reanimated'; -import { SafeAreaView } from 'react-native-safe-area-context'; - -import tw from '../../lib/tailwind'; - -// NOTE: Only wrap screens that need the drawer to be open. (Only Overview Screen for now.) -const DrawerScreenWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const progress: any = useDrawerProgress(); - - const style = useAnimatedStyle(() => { - // TODO: Fix this, it looks weird. Especially on Android. translateX/Y might be the cause. - const scale = interpolate(progress.value, [0, 1], [1, 0.88], Extrapolate.CLAMP); - const translateX = interpolate(progress.value, [0, 1], [0, -12], Extrapolate.CLAMP); - const translateY = interpolate(progress.value, [0, 1], [0, 12], Extrapolate.CLAMP); - const borderRadius = interpolate(progress.value, [0, 1], [0, 16], Extrapolate.CLAMP); - return { - transform: [{ scale }, { translateX }, { translateY }], - borderRadius - }; - }, []); - - return ( - - {children} - - ); -}; - -export default DrawerScreenWrapper; diff --git a/apps/mobile/src/components/drawer/DrawerTagItem.tsx b/apps/mobile/src/components/drawer/DrawerTagItem.tsx new file mode 100644 index 000000000..210b04876 --- /dev/null +++ b/apps/mobile/src/components/drawer/DrawerTagItem.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { ColorValue, Pressable, Text, View } from 'react-native'; + +import tw from '../../lib/tailwind'; + +type DrawerTagItemProps = { + tagName: string; + tagColor: ColorValue; + onPress: () => void; +}; + +const DrawerTagItem: React.FC = (props) => { + const { tagName, tagColor, onPress } = props; + return ( + + + + + {tagName} + + + + ); +}; + +export default DrawerTagItem; diff --git a/apps/mobile/src/components/file/FileIcon.tsx b/apps/mobile/src/components/file/FileIcon.tsx new file mode 100644 index 000000000..101f78dc9 --- /dev/null +++ b/apps/mobile/src/components/file/FileIcon.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { Text, View } from 'react-native'; +import Svg, { Path } from 'react-native-svg'; + +import icons from '../../assets/icons/file'; +import tw from '../../lib/tailwind'; +import { FilePath } from '../../types/bindings'; +import FolderIcon from '../icons/FolderIcon'; + +type FileIconProps = { + file?: FilePath | null; + /** + * This is multiplier for calculating icon size + * default: `1` + */ + size?: number; +}; + +const FileIcon = ({ file, size = 1 }: FileIconProps) => { + return ( + + {file?.is_dir ? ( + + + + ) : file?.file?.has_thumbnail ? ( + <>{/* TODO */} + ) : ( + + + + + + + + {/* File Icon & Extension */} + + {file?.extension && + icons[file.extension] && + (() => { + const Icon = icons[file.extension]; + return ; + })()} + + {file?.extension} + + + + )} + + ); +}; + +export default FileIcon; diff --git a/apps/mobile/src/components/file/FileItem.tsx b/apps/mobile/src/components/file/FileItem.tsx index 629fc489e..5bc5d8a03 100644 --- a/apps/mobile/src/components/file/FileItem.tsx +++ b/apps/mobile/src/components/file/FileItem.tsx @@ -1,76 +1,48 @@ -import { FilePath } from '@sd/core'; +import { useNavigation } from '@react-navigation/native'; import React from 'react'; -import { Text, View } from 'react-native'; -import Svg, { Path } from 'react-native-svg'; +import { Pressable, Text, View } from 'react-native'; -import icons from '../../assets/icons/file'; import tw from '../../lib/tailwind'; -import FolderIcon from '../icons/Folder'; +import { SharedScreenProps } from '../../navigation/SharedScreens'; +import { useFileModalStore } from '../../stores/useModalStore'; +import { FilePath } from '../../types/bindings'; +import FileIcon from './FileIcon'; type FileItemProps = { file?: FilePath | null; }; // TODO: Menu for file actions (File details, Share etc.) -// TODO: Sheet Modal for file details const FileItem = ({ file }: FileItemProps) => { + const fileRef = useFileModalStore((state) => state.fileRef); + const setData = useFileModalStore((state) => state.setData); + + const navigation = useNavigation['navigation']>(); + + function handlePress() { + if (!file) return; + + if (file.is_dir) { + navigation.navigate('Location', { id: file.location_id }); + } else { + setData(file); + fileRef.current.present(); + } + } + return ( - - + + {/* Folder Icons/Thumbnail etc. */} - - {file?.is_dir ? ( - - - - ) : file?.file?.has_thumbnail ? ( - <>{/* TODO */} - ) : ( - - - - - - - - {/* File Icon & Extension */} - - {file?.extension && icons[file.extension] ? ( - (() => { - const Icon = icons[file.extension]; - return ; - })() - ) : ( - <> - )} - - {file?.extension} - - - - )} - - {/* Name */} + {file?.name} - + ); }; diff --git a/apps/mobile/src/components/header/Header.tsx b/apps/mobile/src/components/header/Header.tsx new file mode 100644 index 000000000..8838ade88 --- /dev/null +++ b/apps/mobile/src/components/header/Header.tsx @@ -0,0 +1,41 @@ +import { useDrawerStatus } from '@react-navigation/drawer'; +import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types'; +import { useNavigation } from '@react-navigation/native'; +import { MotiView } from 'moti'; +import { List } from 'phosphor-react-native'; +import React from 'react'; +import { Pressable, Text, View } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import tw from '../../lib/tailwind'; + +const Header = () => { + const navigation = useNavigation(); + + const { top } = useSafeAreaInsets(); + + const isDrawerOpen = useDrawerStatus() === 'open'; + + return ( + + + navigation.openDrawer()}> + + + + + navigation.navigate('Search')} + > + Search + + + + ); +}; + +export default Header; diff --git a/apps/mobile/src/components/icons/Folder.tsx b/apps/mobile/src/components/icons/FolderIcon.tsx similarity index 81% rename from apps/mobile/src/components/icons/Folder.tsx rename to apps/mobile/src/components/icons/FolderIcon.tsx index 0fa11688f..311544248 100644 --- a/apps/mobile/src/components/icons/Folder.tsx +++ b/apps/mobile/src/components/icons/FolderIcon.tsx @@ -1,9 +1,8 @@ +import FolderWhite from '@sd/assets/svgs/folder-white.svg'; +import Folder from '@sd/assets/svgs/folder.svg'; import React from 'react'; import { SvgProps } from 'react-native-svg'; -import FolderWhite from '../../assets/temp/folder-white.svg'; -import Folder from '../../assets/temp/folder.svg'; - type FolderProps = { /** * Render a white folder icon diff --git a/apps/mobile/src/components/modals/FileDetails.tsx b/apps/mobile/src/components/modals/FileDetails.tsx deleted file mode 100644 index 1f4c77b84..000000000 --- a/apps/mobile/src/components/modals/FileDetails.tsx +++ /dev/null @@ -1 +0,0 @@ -// Only mount this modal if `FileItem` gets used in screen. diff --git a/apps/mobile/src/components/modals/FileModal.tsx b/apps/mobile/src/components/modals/FileModal.tsx new file mode 100644 index 000000000..e5658ee1d --- /dev/null +++ b/apps/mobile/src/components/modals/FileModal.tsx @@ -0,0 +1,128 @@ +import { BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { format } from 'date-fns'; +import React, { useRef } from 'react'; +import { Button, Pressable, Text, View } from 'react-native'; +import { ChevronLeftIcon } from 'react-native-heroicons/outline'; + +import tw from '../../lib/tailwind'; +import { useFileModalStore } from '../../stores/useModalStore'; +import Divider from '../base/Divider'; +import FileIcon from '../file/FileIcon'; +import ModalBackdrop from './layout/ModalBackdrop'; +import ModalHandle from './layout/ModalHandle'; + +/* +https://github.com/software-mansion/react-native-reanimated/issues/3296 +https://github.com/gorhom/react-native-bottom-sheet/issues/925 +https://github.com/gorhom/react-native-bottom-sheet/issues/1036 + +Reanimated has a bug where it sometimes doesn't animate on mount (IOS only?), doing a console.log() seems to do a re-render and fix the issue. +We can't do this for production obvs but until then they might fix it so, let's not try weird hacks for now and live with the logs. +*/ + +interface MetaItemProps { + title: string; + value: string; +} + +function MetaItem({ title, value }: MetaItemProps) { + return ( + + {title} + {value} + + ); +} + +export const FileModal = () => { + const { fileRef, data } = useFileModalStore(); + + const fileDetailsRef = useRef(null); + + return ( + <> + console.log(from, to)} + > + {data && ( + + {/* File Icon / Name */} + + + {/* File Name, Details etc. */} + + {data?.name} + + 5 MB, + + {data?.extension.toUpperCase()}, + + 15 Aug + + fileDetailsRef.current.present()}> + More + + + + {/* Divider */} + + {/* Buttons */} + + - - {/* Stats */} - - - {/* Devices */} - index.toString()} - renderItem={({ item }) => ( - - )} - /> - - - + + + {/* Stats */} + + {/* Spacing */} + + {/* Devices */} + index.toString()} + renderItem={({ item }) => ( + + )} + /> + + ); } diff --git a/apps/mobile/src/screens/Photos.tsx b/apps/mobile/src/screens/Photos.tsx index ca3f4e7c6..5742265ea 100644 --- a/apps/mobile/src/screens/Photos.tsx +++ b/apps/mobile/src/screens/Photos.tsx @@ -2,9 +2,9 @@ import React from 'react'; import { Text, View } from 'react-native'; import tw from '../lib/tailwind'; -import type { TabScreenProps } from '../navigation/TabNavigator'; +import { PhotosStackScreenProps } from '../navigation/tabs/PhotosStack'; -export default function PhotosScreen({ navigation }: TabScreenProps<'Photos'>) { +export default function PhotosScreen({ navigation }: PhotosStackScreenProps<'Photos'>) { return ( Photos diff --git a/apps/mobile/src/screens/Spaces.tsx b/apps/mobile/src/screens/Spaces.tsx index 4b3481acf..219041ddc 100644 --- a/apps/mobile/src/screens/Spaces.tsx +++ b/apps/mobile/src/screens/Spaces.tsx @@ -1,14 +1,13 @@ import React from 'react'; -import { Text } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; +import { Text, View } from 'react-native'; import tw from '../lib/tailwind'; -import type { TabScreenProps } from '../navigation/TabNavigator'; +import { SpacesStackScreenProps } from '../navigation/tabs/SpacesStack'; -export default function SpacesScreen({ navigation }: TabScreenProps<'Spaces'>) { +export default function SpacesScreen({ navigation }: SpacesStackScreenProps<'Spaces'>) { return ( - + Spaces - + ); } diff --git a/apps/mobile/src/screens/Tag.tsx b/apps/mobile/src/screens/Tag.tsx index ea41fac20..7401157cc 100644 --- a/apps/mobile/src/screens/Tag.tsx +++ b/apps/mobile/src/screens/Tag.tsx @@ -2,8 +2,9 @@ import React from 'react'; import { Text, View } from 'react-native'; import tw from '../lib/tailwind'; +import { SharedScreenProps } from '../navigation/SharedScreens'; -export default function TagScreen({ navigation, route }: any) { +export default function TagScreen({ navigation, route }: SharedScreenProps<'Tag'>) { const { id } = route.params; return ( diff --git a/apps/mobile/src/screens/modals/Search.tsx b/apps/mobile/src/screens/modals/Search.tsx new file mode 100644 index 000000000..2654cf46b --- /dev/null +++ b/apps/mobile/src/screens/modals/Search.tsx @@ -0,0 +1,57 @@ +import { MagnifyingGlass } from 'phosphor-react-native'; +import React, { useState } from 'react'; +import { ActivityIndicator, Pressable, Text, TextInput, View } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import { Button } from '../../components/base/Button'; +import tw from '../../lib/tailwind'; +import { RootStackScreenProps } from '../../navigation'; + +const SearchScreen = ({ navigation }: RootStackScreenProps<'Search'>) => { + const { top } = useSafeAreaInsets(); + + const [loading, setLoading] = useState(false); + + return ( + + {/* Header */} + + {/* Search Input */} + + + + {loading ? ( + + ) : ( + + )} + + + + + {/* Cancel Button */} + navigation.goBack()}> + Cancel + + + {/* Content */} + + + + + ); +}; + +export default SearchScreen; diff --git a/apps/mobile/src/screens/modals/settings/Settings.tsx b/apps/mobile/src/screens/modals/settings/Settings.tsx index e6e747bb3..73fd7d643 100644 --- a/apps/mobile/src/screens/modals/settings/Settings.tsx +++ b/apps/mobile/src/screens/modals/settings/Settings.tsx @@ -6,7 +6,7 @@ import { RootStackScreenProps } from '../../../navigation'; export default function SettingsScreen({ navigation }: RootStackScreenProps<'Settings'>) { return ( - + Settings diff --git a/apps/mobile/src/screens/onboarding/Onboarding.tsx b/apps/mobile/src/screens/onboarding/Onboarding.tsx index f656bb835..a56eed859 100644 --- a/apps/mobile/src/screens/onboarding/Onboarding.tsx +++ b/apps/mobile/src/screens/onboarding/Onboarding.tsx @@ -12,10 +12,6 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding const { hideOnboarding } = useOnboardingStore(); function onButtonPress() { - // Persist onboarding state only when app in production for now. - // if (process.env.NODE_ENV === 'production') { - // setItemToStorage('@onboarding', '1'); - // } setItemToStorage('@onboarding', '1'); // TODO: Add a loading indicator to button as this takes a second or so. hideOnboarding(); @@ -26,8 +22,7 @@ const OnboardingScreen = ({ navigation }: OnboardingStackScreenProps<'Onboarding {/* Logo */} - {/* TODO: Change this to @sd/assets. */} - + {/* Text */} diff --git a/apps/mobile/src/stores/useLibraryStore.ts b/apps/mobile/src/stores/useLibraryStore.ts new file mode 100644 index 000000000..77294c4cc --- /dev/null +++ b/apps/mobile/src/stores/useLibraryStore.ts @@ -0,0 +1,13 @@ +import create from 'zustand'; + +interface LibraryStore { + currentLibraryUuid: string | null; + switchLibrary: (id: string) => void; +} + +export const useLibraryStore = create()((set) => ({ + currentLibraryUuid: null, + switchLibrary: (uuid) => { + set((state) => ({ currentLibraryUuid: uuid })); + } +})); diff --git a/apps/mobile/src/stores/useModalStore.ts b/apps/mobile/src/stores/useModalStore.ts new file mode 100644 index 000000000..3c3c3f6bd --- /dev/null +++ b/apps/mobile/src/stores/useModalStore.ts @@ -0,0 +1,19 @@ +import { BottomSheetModal } from '@gorhom/bottom-sheet'; +import React from 'react'; +import create from 'zustand'; + +import { FilePath } from '../types/bindings'; + +interface FileModalState { + fileRef: React.RefObject; + data: FilePath | null; + setData: (data: FilePath) => void; + clearData: () => void; +} + +export const useFileModalStore = create((set) => ({ + fileRef: React.createRef(), + data: null, + setData: (data: FilePath) => set((_) => ({ data })), + clearData: () => set((_) => ({ data: null })) +})); diff --git a/apps/mobile/src/types/bindings.ts b/apps/mobile/src/types/bindings.ts new file mode 100644 index 000000000..858a453e3 --- /dev/null +++ b/apps/mobile/src/types/bindings.ts @@ -0,0 +1,117 @@ +// This file was generated by [rspc](https://github.com/oscartbeaumont/rspc). Do not edit this file manually. + +export type Operations = { + queries: + { key: ["files.readMetadata", LibraryArgs], result: null } | + { key: ["jobs.getHistory", LibraryArgs], result: Array } | + { key: ["jobs.getRunning", LibraryArgs], result: Array } | + { key: ["library.get"], result: Array } | + { key: ["locations.getExplorerDir", LibraryArgs], result: DirectoryWithContents } | + { key: ["locations.getById", LibraryArgs], result: Location | null } | + { key: ["getNode"], result: NodeState } | + { key: ["version"], result: string } | + { key: ["volumes.get"], result: Array } | + { key: ["tags.getFilesForTag", LibraryArgs], result: Tag | null } | + { key: ["tags.get", LibraryArgs], result: Array } | + { key: ["library.getStatistics", LibraryArgs], result: Statistics } | + { key: ["locations.get", LibraryArgs], result: Array }, + mutations: + { key: ["jobs.generateThumbsForLocation", LibraryArgs], result: null } | + { key: ["files.setNote", LibraryArgs], result: null } | + { key: ["library.create", string], result: null } | + { key: ["library.delete", string], result: null } | + { key: ["files.setFavorite", LibraryArgs], result: null } | + { key: ["locations.update", LibraryArgs], result: null } | + { key: ["tags.update", LibraryArgs], result: null } | + { key: ["tags.assign", LibraryArgs], result: null } | + { key: ["tags.delete", LibraryArgs], result: null } | + { key: ["library.edit", EditLibraryArgs], result: null } | + { key: ["jobs.identifyUniqueFiles", LibraryArgs], result: null } | + { key: ["locations.create", LibraryArgs], result: Location } | + { key: ["locations.quickRescan", LibraryArgs], result: null } | + { key: ["files.delete", LibraryArgs], result: null } | + { key: ["locations.delete", LibraryArgs], result: null } | + { key: ["locations.fullRescan", LibraryArgs], result: null } | + { key: ["tags.create", LibraryArgs], result: Tag }, + subscriptions: + { key: ["jobs.newThumbnail", LibraryArgs], result: string } | + { key: ["invalidateQuery"], result: InvalidateOperationEvent } +}; + +export interface GenerateThumbsForLocationArgs { id: number, path: string } + +export interface SyncEvent { id: number, node_id: number, timestamp: string, record_id: Array, kind: number, column: string | null, value: string, node: Node | null } + +export interface Location { id: number, pub_id: Array, node_id: number | null, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, filesystem: string | null, disk_type: number | null, is_removable: boolean | null, is_online: boolean, date_created: string, node: Node | null | null, file_paths: Array | null } + +export interface TagAssignArgs { file_id: number, tag_id: number } + +export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array | null, file_paths: Array | null } + +export interface DirectoryWithContents { directory: FilePath, contents: Array } + +export interface ConfigMetadata { version: string | null } + +export interface TagUpdateArgs { id: number, name: string | null, color: string | null } + +export interface LibraryConfig { version: string | null, name: string, description: string } + +export interface IdentifyUniqueFilesArgs { id: number, path: string } + +export interface FilePath { id: number, is_dir: boolean, location_id: number | null, materialized_path: string, name: string, extension: string | null, file_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, file: File | null | null, location: Location | null | null, key: Key | null | null } + +export interface Album { id: number, pub_id: Array, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array | null } + +export interface SetFavoriteArgs { id: number, favorite: boolean } + +export interface LocationUpdateArgs { id: number, name: string | null } + +export interface Job { id: Array, name: string, node_id: number, action: number, status: number, data: Array | null, task_count: number, completed_task_count: number, date_created: string, date_modified: string, seconds_elapsed: number, nodes: Node | null } + +export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string } + +export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null } + +export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null } + +export interface TagCreateArgs { name: string, color: string } + +export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null } + +export interface Label { id: number, pub_id: Array, name: string | null, date_created: string, date_modified: string, label_files: Array | null } + +export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null } + +export interface EditLibraryArgs { id: string, name: string | null, description: string | null } + +export interface Space { id: number, pub_id: Array, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array | null } + +export interface Comment { id: number, pub_id: Array, content: string, date_created: string, date_modified: string, file_id: number | null, file: File | null | null } + +export interface GetExplorerDirArgs { location_id: number, path: string, limit: number } + +export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused" + +export interface JobReport { id: string, name: string, data: Array | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number } + +export interface Tag { id: number, pub_id: Array, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array | null } + +export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig } + +export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null } + +export interface InvalidateOperationEvent { key: string, arg: any } + +export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null } + +export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string } + +export interface LibraryArgs { library_id: string, arg: T } + +export interface Node { id: number, pub_id: Array, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string, sync_events: Array | null, jobs: Array | null, Location: Array | null } + +export interface File { id: number, cas_id: string, integrity_checksum: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array | null, labels: Array | null, albums: Array | null, spaces: Array | null, paths: Array | null, comments: Array | null, media_data: MediaData | null | null, key: Key | null | null } + +export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } + +export interface SetNoteArgs { id: number, note: string | null } diff --git a/apps/mobile/src/types/declarations.d.ts b/apps/mobile/src/types/declarations.d.ts index 793f67889..1eae33fe9 100644 --- a/apps/mobile/src/types/declarations.d.ts +++ b/apps/mobile/src/types/declarations.d.ts @@ -4,11 +4,3 @@ declare module '*.svg' { const content: React.FC; export default content; } - -// This declaration is used by useNavigation, Link, ref etc. -declare global { - namespace ReactNavigation { - // eslint-disable-next-line @typescript-eslint/no-empty-interface - interface RootParamList extends RootStackParamList {} - } -} diff --git a/apps/server/Cargo.toml b/apps/server/Cargo.toml index 2a512ff97..4a8dc55cf 100644 --- a/apps/server/Cargo.toml +++ b/apps/server/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] sdcore = { path = "../../core", features = [] } +rspc = { version = "0.0.4", features = ["axum"] } axum = "0.5.13" tokio = { version = "1.17.0", features = ["sync", "rt-multi-thread", "signal"] } tracing = "0.1.35" diff --git a/core/Cargo.toml b/core/Cargo.toml index f199eefdd..5a309048b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -6,10 +6,13 @@ authors = ["Spacedrive Technology Inc."] license = "GNU GENERAL PUBLIC LICENSE" repository = "https://github.com/spacedriveapp/spacedrive" edition = "2021" +rust-version = "1.63.0" [features] -p2p = [ -] # This feature controlls whether the Spacedrive Core contains the Peer to Peer syncing engine (It isn't required for the hosted core so we can disable it). +default = ["p2p"] +p2p = [] # This feature controlls whether the Spacedrive Core contains the Peer to Peer syncing engine (It isn't required for the hosted core so we can disable it). +mobile = [] # This feature allows features to be disabled when the Core is running on mobile. +ffmpeg = ["dep:ffmpeg-next"] # This feature controls whether the Spacedrive Core contains functionality which requires FFmpeg. [dependencies] hostname = "0.3.1" @@ -32,8 +35,6 @@ prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust "sqlite-create-many", ] } rspc = { version = "0.0.4", features = [ - "axum", - "tauri", "uuid", "chrono", "tracing", @@ -42,14 +43,13 @@ walkdir = "^2.3.2" uuid = { version = "1.1.2", features = ["v4", "serde"] } sysinfo = "0.23.9" thiserror = "1.0.30" -core-derive = { path = "./derive" } tokio = { version = "1.17.0", features = ["sync", "rt-multi-thread"] } include_dir = { version = "0.7.2", features = ["glob"] } async-trait = "^0.1.52" image = "0.24.1" webp = "0.2.2" -ffmpeg-next = "5.0.3" +ffmpeg-next = { version = "5.0.3", optional = true, features = [] } fs_extra = "1.2.0" tracing = "0.1.35" tracing-subscriber = { version = "0.3.14", features = ["env-filter"] } diff --git a/core/derive/Cargo.toml b/core/derive/Cargo.toml deleted file mode 100644 index dea7baf4a..000000000 --- a/core/derive/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "core-derive" -version = "0.1.0" -edition = "2021" - -[lib] -proc-macro = true - -[dependencies] -quote = "1.0.18" -syn = "1.0.91" \ No newline at end of file diff --git a/core/derive/src/lib.rs b/core/derive/src/lib.rs deleted file mode 100644 index 2d107dab2..000000000 --- a/core/derive/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput}; - -/// This macro must be executed in a file with `PropertyOperationCtx` defined and in the same package as the SyncContext is defined. -/// The creates: -/// ```rust -/// impl PropertyOperation { -/// fn apply(operation: PropertyOperationCtx, ctx: SyncContext) { -/// match operation.resource { -/// PropertyOperation::Tag(method) => method.apply(ctx), -/// }; -/// } -/// } -/// ``` -#[proc_macro_derive(PropertyOperationApply)] -pub fn property_operation_apply(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, .. } = parse_macro_input!(input); - - if let Data::Enum(data) = data { - let impls = data.variants.iter().map(|variant| { - let variant_ident = &variant.ident; - quote! { - #ident::#variant_ident(method) => method.apply(ctx), - } - }); - - let expanded = quote! { - impl #ident { - fn apply(operation: CrdtCtx, ctx: self::engine::SyncContext) { - match operation.resource { - #(#impls)* - }; - } - } - }; - - TokenStream::from(expanded) - } else { - panic!("The 'PropertyOperationApply' macro can only be used on enums!"); - } -} diff --git a/core/index.ts b/core/index.ts index c70c78aea..858a453e3 100644 --- a/core/index.ts +++ b/core/index.ts @@ -1,118 +1,117 @@ -/* eslint-disable */ // This file was generated by [rspc](https://github.com/oscartbeaumont/rspc). Do not edit this file manually. export type Operations = { queries: - { key: ["getNode"], result: NodeState } | - { key: ["version"], result: string } | - { key: ["tags.getFilesForTag", LibraryArgs], result: Tag | null } | - { key: ["locations.getById", LibraryArgs], result: Location | null } | { key: ["files.readMetadata", LibraryArgs], result: null } | - { key: ["locations.get", LibraryArgs], result: Array } | + { key: ["jobs.getHistory", LibraryArgs], result: Array } | { key: ["jobs.getRunning", LibraryArgs], result: Array } | { key: ["library.get"], result: Array } | { key: ["locations.getExplorerDir", LibraryArgs], result: DirectoryWithContents } | + { key: ["locations.getById", LibraryArgs], result: Location | null } | + { key: ["getNode"], result: NodeState } | + { key: ["version"], result: string } | + { key: ["volumes.get"], result: Array } | + { key: ["tags.getFilesForTag", LibraryArgs], result: Tag | null } | { key: ["tags.get", LibraryArgs], result: Array } | - { key: ["jobs.getHistory", LibraryArgs], result: Array } | { key: ["library.getStatistics", LibraryArgs], result: Statistics } | - { key: ["volumes.get"], result: Array }, + { key: ["locations.get", LibraryArgs], result: Array }, mutations: + { key: ["jobs.generateThumbsForLocation", LibraryArgs], result: null } | + { key: ["files.setNote", LibraryArgs], result: null } | + { key: ["library.create", string], result: null } | + { key: ["library.delete", string], result: null } | { key: ["files.setFavorite", LibraryArgs], result: null } | - { key: ["tags.assign", LibraryArgs], result: null } | { key: ["locations.update", LibraryArgs], result: null } | + { key: ["tags.update", LibraryArgs], result: null } | + { key: ["tags.assign", LibraryArgs], result: null } | + { key: ["tags.delete", LibraryArgs], result: null } | + { key: ["library.edit", EditLibraryArgs], result: null } | + { key: ["jobs.identifyUniqueFiles", LibraryArgs], result: null } | + { key: ["locations.create", LibraryArgs], result: Location } | + { key: ["locations.quickRescan", LibraryArgs], result: null } | + { key: ["files.delete", LibraryArgs], result: null } | { key: ["locations.delete", LibraryArgs], result: null } | { key: ["locations.fullRescan", LibraryArgs], result: null } | - { key: ["tags.delete", LibraryArgs], result: null } | - { key: ["jobs.identifyUniqueFiles", LibraryArgs], result: null } | - { key: ["library.delete", string], result: null } | - { key: ["files.delete", LibraryArgs], result: null } | - { key: ["locations.quickRescan", LibraryArgs], result: null } | - { key: ["locations.create", LibraryArgs], result: Location } | - { key: ["files.setNote", LibraryArgs], result: null } | - { key: ["library.edit", EditLibraryArgs], result: null } | - { key: ["tags.create", LibraryArgs], result: Tag } | - { key: ["library.create", string], result: null } | - { key: ["jobs.generateThumbsForLocation", LibraryArgs], result: null } | - { key: ["tags.update", LibraryArgs], result: null }, + { key: ["tags.create", LibraryArgs], result: Tag }, subscriptions: { key: ["jobs.newThumbnail", LibraryArgs], result: string } | { key: ["invalidateQuery"], result: InvalidateOperationEvent } }; -export interface Tag { id: number, pub_id: Array, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array | null } +export interface GenerateThumbsForLocationArgs { id: number, path: string } -export interface SetFavoriteArgs { id: number, favorite: boolean } - -export interface LibraryConfig { version: string | null, name: string, description: string } - -export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null } - -export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string } - -export interface GetExplorerDirArgs { location_id: number, path: string, limit: number } - -export interface JobReport { id: string, name: string, data: Array | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number } - -export interface Album { id: number, pub_id: Array, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array | null } - -export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null } +export interface SyncEvent { id: number, node_id: number, timestamp: string, record_id: Array, kind: number, column: string | null, value: string, node: Node | null } export interface Location { id: number, pub_id: Array, node_id: number | null, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, filesystem: string | null, disk_type: number | null, is_removable: boolean | null, is_online: boolean, date_created: string, node: Node | null | null, file_paths: Array | null } -export interface Label { id: number, pub_id: Array, name: string | null, date_created: string, date_modified: string, label_files: Array | null } +export interface TagAssignArgs { file_id: number, tag_id: number } -export interface InvalidateOperationEvent { key: string, arg: any } - -export interface LibraryArgs { library_id: string, arg: T } - -export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null } - -export interface GenerateThumbsForLocationArgs { id: number, path: string } - -export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } +export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array | null, file_paths: Array | null } export interface DirectoryWithContents { directory: FilePath, contents: Array } -export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null } +export interface ConfigMetadata { version: string | null } -export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null } +export interface TagUpdateArgs { id: number, name: string | null, color: string | null } -export interface File { id: number, cas_id: string, integrity_checksum: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array | null, labels: Array | null, albums: Array | null, spaces: Array | null, paths: Array | null, comments: Array | null, media_data: MediaData | null | null, key: Key | null | null } - -export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string } - -export interface SetNoteArgs { id: number, note: string | null } +export interface LibraryConfig { version: string | null, name: string, description: string } export interface IdentifyUniqueFilesArgs { id: number, path: string } -export interface Space { id: number, pub_id: Array, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array | null } +export interface FilePath { id: number, is_dir: boolean, location_id: number | null, materialized_path: string, name: string, extension: string | null, file_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, file: File | null | null, location: Location | null | null, key: Key | null | null } + +export interface Album { id: number, pub_id: Array, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array | null } + +export interface SetFavoriteArgs { id: number, favorite: boolean } export interface LocationUpdateArgs { id: number, name: string | null } export interface Job { id: Array, name: string, node_id: number, action: number, status: number, data: Array | null, task_count: number, completed_task_count: number, date_created: string, date_modified: string, seconds_elapsed: number, nodes: Node | null } -export interface TagAssignArgs { file_id: number, tag_id: number } +export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string } -export interface FilePath { id: number, is_dir: boolean, location_id: number | null, materialized_path: string, name: string, extension: string | null, file_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, file: File | null | null, location: Location | null | null, key: Key | null | null } - -export interface TagCreateArgs { name: string, color: string } - -export interface EditLibraryArgs { id: string, name: string | null, description: string | null } - -export interface TagUpdateArgs { id: number, name: string | null, color: string | null } - -export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused" +export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null } export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null } +export interface TagCreateArgs { name: string, color: string } + +export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null } + +export interface Label { id: number, pub_id: Array, name: string | null, date_created: string, date_modified: string, label_files: Array | null } + +export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null } + +export interface EditLibraryArgs { id: string, name: string | null, description: string | null } + +export interface Space { id: number, pub_id: Array, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array | null } + export interface Comment { id: number, pub_id: Array, content: string, date_created: string, date_modified: string, file_id: number | null, file: File | null | null } +export interface GetExplorerDirArgs { location_id: number, path: string, limit: number } + +export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused" + +export interface JobReport { id: string, name: string, data: Array | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number } + +export interface Tag { id: number, pub_id: Array, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array | null } + export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig } -export interface SyncEvent { id: number, node_id: number, timestamp: string, record_id: Array, kind: number, column: string | null, value: string, node: Node | null } +export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null } -export interface ConfigMetadata { version: string | null } +export interface InvalidateOperationEvent { key: string, arg: any } + +export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null } + +export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string } + +export interface LibraryArgs { library_id: string, arg: T } export interface Node { id: number, pub_id: Array, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string, sync_events: Array | null, jobs: Array | null, Location: Array | null } -export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array | null, file_paths: Array | null } +export interface File { id: number, cas_id: string, integrity_checksum: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array | null, labels: Array | null, albums: Array | null, spaces: Array | null, paths: Array | null, comments: Array | null, media_data: MediaData | null | null, key: Key | null | null } + +export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } + +export interface SetNoteArgs { id: number, note: string | null } diff --git a/core/src/api/mod.rs b/core/src/api/mod.rs index ad1c649f1..0ffa98e5a 100644 --- a/core/src/api/mod.rs +++ b/core/src/api/mod.rs @@ -16,7 +16,7 @@ use crate::{ use utils::{InvalidRequests, InvalidateOperationEvent}; -pub(crate) type Router = rspc::Router; +pub type Router = rspc::Router; pub(crate) type RouterBuilder = rspc::RouterBuilder; /// Represents an internal core event, these are exposed to client via a rspc subscription. @@ -133,5 +133,9 @@ mod tests { let r = super::mount(); r.export_ts(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("./index.ts")) .expect("Error exporting rspc Typescript bindings!"); + r.export_ts( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../apps/mobile/src/types/bindings.ts"), + ) + .expect("Error exporting rspc Typescript bindings!"); } } diff --git a/core/src/api/utils/invalidate.rs b/core/src/api/utils/invalidate.rs index 1c9d1e312..df397577d 100644 --- a/core/src/api/utils/invalidate.rs +++ b/core/src/api/utils/invalidate.rs @@ -1,5 +1,9 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; +#[cfg(debug_assertions)] +use std::sync::Mutex; + +#[cfg(debug_assertions)] use once_cell::sync::OnceCell; use rspc::{internal::specta::DataType, Type}; use serde::Serialize; @@ -27,6 +31,7 @@ impl InvalidateOperationEvent { /// a request to invalidate a specific resource #[derive(Debug)] +#[allow(dead_code)] pub(crate) struct InvalidationRequest { pub key: &'static str, pub arg_ty: DataType, @@ -35,6 +40,7 @@ pub(crate) struct InvalidationRequest { /// invalidation request for a specific resource #[derive(Debug, Default)] +#[allow(dead_code)] pub(crate) struct InvalidRequests { pub queries: Vec, } diff --git a/core/src/encode/metadata.rs b/core/src/encode/metadata.rs index 9c86229c2..d22fd5317 100644 --- a/core/src/encode/metadata.rs +++ b/core/src/encode/metadata.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "ffmpeg")] use ffmpeg_next::format; #[derive(Default, Debug)] @@ -21,9 +22,10 @@ pub struct Stream { } #[derive(Debug)] +#[allow(dead_code)] // TODO: Remove this when we start using ffmpeg pub enum StreamKind { - // Video(VideoStream), - // Audio(AudioStream), + Video(VideoStream), + Audio(AudioStream), } #[derive(Debug)] @@ -31,6 +33,7 @@ pub struct VideoStream { pub width: u32, pub height: u32, pub aspect_ratio: String, + #[cfg(feature = "ffmpeg")] pub format: format::Pixel, pub bitrate: usize, } @@ -38,6 +41,7 @@ pub struct VideoStream { #[derive(Debug)] pub struct AudioStream { pub channels: u16, + #[cfg(feature = "ffmpeg")] pub format: format::Sample, pub bitrate: usize, pub rate: u32, diff --git a/core/src/lib.rs b/core/src/lib.rs index bef89167b..e69cd9b06 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,9 +9,7 @@ use tracing_subscriber::{filter::LevelFilter, fmt, prelude::*, EnvFilter}; use tokio::{fs, sync::broadcast}; -pub use rspc; // We expose rspc so we can access it in the Desktop app - -pub(crate) mod api; +pub mod api; pub(crate) mod encode; pub(crate) mod file; pub(crate) mod job; diff --git a/core/src/tag/mod.rs b/core/src/tag/mod.rs deleted file mode 100644 index 9cf480010..000000000 --- a/core/src/tag/mod.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::{ - file::File, - library::LibraryContext, - prisma::{self, file, tag, tag_on_file}, - ClientQuery, CoreError, CoreEvent, CoreResponse, LibraryQuery, -}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use ts_rs::TS; -use uuid::Uuid; - -#[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts(export)] -pub struct Tag { - pub id: i32, - pub pub_id: Uuid, - pub name: Option, - pub color: Option, - - pub total_files: Option, - pub redundancy_goal: Option, - - pub date_created: chrono::DateTime, - pub date_modified: chrono::DateTime, -} - -#[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts(export)] -pub struct TagOnFile { - pub tag_id: i32, - pub tag: Option, - - pub file_id: i32, - pub file: Option, - - pub date_created: chrono::DateTime, -} - -impl From for Tag { - fn from(data: tag::Data) -> Self { - Self { - id: data.id, - pub_id: Uuid::from_slice(&data.pub_id).unwrap(), - name: data.name, - color: data.color, - total_files: data.total_files, - redundancy_goal: data.redundancy_goal, - date_created: data.date_created.into(), - date_modified: data.date_modified.into(), - } - } -} - -impl From for TagOnFile { - fn from(data: tag_on_file::Data) -> Self { - Self { - tag_id: data.tag_id, - tag: data.tag.map(|t| (*t).into()), - file_id: data.file_id, - file: data.file.map(|f| (*f).into()), - date_created: data.date_created.into(), - } - } -} - -#[derive(Serialize, Deserialize, TS, Debug)] -#[ts(export)] -pub struct TagWithFiles { - pub tag: Tag, - pub files_with_tag: Vec, -} - -#[derive(Error, Debug)] -pub enum TagError { - // #[error("Tag not found")] - // TagNotFound(i32), - #[error("Database error")] - DatabaseError(#[from] prisma::QueryError), -} - -pub async fn create_tag( - ctx: LibraryContext, - name: String, - color: String, -) -> Result { - let created_tag = ctx - .db - .tag() - .create( - tag::pub_id::set(Uuid::new_v4().as_bytes().to_vec()), - vec![tag::name::set(Some(name)), tag::color::set(Some(color))], - ) - .exec() - .await - .unwrap(); - - ctx.emit(CoreEvent::InvalidateQuery(ClientQuery::LibraryQuery { - library_id: ctx.id, - query: LibraryQuery::GetTags, - })) - .await; - - Ok(CoreResponse::TagCreateResponse(created_tag.into())) -} - -pub async fn update_tag( - ctx: LibraryContext, - id: i32, - name: Option, - color: Option, -) -> Result { - ctx.db - .tag() - .find_unique(tag::id::equals(id)) - .update(vec![tag::name::set(name), tag::color::set(color)]) - .exec() - .await - .unwrap(); - - ctx.emit(CoreEvent::InvalidateQuery(ClientQuery::LibraryQuery { - library_id: ctx.id, - query: LibraryQuery::GetTags, - })) - .await; - - Ok(CoreResponse::Success(())) -} - -pub async fn tag_assign( - ctx: LibraryContext, - file_id: i32, - tag_id: i32, -) -> Result { - ctx.db.tag_on_file().create( - tag_on_file::tag::link(tag::UniqueWhereParam::IdEquals(tag_id)), - tag_on_file::file::link(file::UniqueWhereParam::IdEquals(file_id)), - vec![], - ); - - Ok(CoreResponse::Success(())) -} - -pub async fn tag_delete(ctx: LibraryContext, id: i32) -> Result { - ctx.db - .tag() - .find_unique(tag::id::equals(id)) - .delete() - .exec() - .await? - .unwrap(); - - ctx.emit(CoreEvent::InvalidateQuery(ClientQuery::LibraryQuery { - library_id: ctx.id, - query: LibraryQuery::GetTags, - })) - .await; - - Ok(CoreResponse::Success(())) -} - -pub async fn get_files_for_tag(ctx: LibraryContext, id: i32) -> Result { - let tag: Option = ctx - .db - .tag() - .find_unique(tag::id::equals(id)) - .exec() - .await? - .map(Into::into); - - Ok(CoreResponse::GetTag(tag)) -} - -pub async fn get_all_tags(ctx: LibraryContext) -> Result { - let tags: Vec = ctx - .db - .tag() - .find_many(vec![]) - .exec() - .await? - .into_iter() - .map(Into::into) - .collect(); - - Ok(CoreResponse::GetTags(tags)) -} diff --git a/lefthook.yml b/lefthook.yml index 4ffa59ced..7a14eae9c 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -7,7 +7,7 @@ pre-push: parallel: true commands: type-check: - glob: "*.{ts,tsx}" + glob: '*.{ts,tsx}' run: pnpm typecheck lint: glob: '*.{ts,tsx}' @@ -22,7 +22,5 @@ pre-push: run: cargo clippy --package spacedrive -- -D warnings rust-lint-core: run: cargo clippy --package sdcore --lib -- -D warnings - rust-lint-core-derive: - run: cargo clippy --package core-derive --lib -- -D warnings rust-lint-server: run: cargo clippy --package server -- -D warnings diff --git a/package.json b/package.json index 2c1dbe5bb..e3406c04f 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,9 @@ "version": "0.0.0", "private": true, "scripts": { - "prep": "pnpm db:gen", + "prep": "pnpm db:gen && pnpm core codegen", + "build": "turbo run build", + "landing-web": "turbo run dev --parallel --filter=@sd/landing --filter=@sd/web", "db:migrate": "pnpm core prisma migrate dev", "db:gen": "pnpm core prisma generate", "format": "prettier --config .prettierrc.cli.js --write \"**/*.{ts,tsx,html,scss,json,yml,md}\"", diff --git a/apps/landing/src/assets/images/logo.png b/packages/assets/images/logo.png similarity index 100% rename from apps/landing/src/assets/images/logo.png rename to packages/assets/images/logo.png diff --git a/packages/assets/svgs/folder-white.svg b/packages/assets/svgs/folder-white.svg new file mode 100644 index 000000000..730602c10 --- /dev/null +++ b/packages/assets/svgs/folder-white.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/assets/svgs/folder.svg b/packages/assets/svgs/folder.svg new file mode 100644 index 000000000..96896b72f --- /dev/null +++ b/packages/assets/svgs/folder.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/client/package.json b/packages/client/package.json index d5c4dd2fd..fbdb41eed 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -18,8 +18,8 @@ }, "dependencies": { "@rspc/client": "^0.0.5", - "@sd/config": "workspace:*", "@sd/core": "workspace:*", + "@sd/config": "workspace:*", "@sd/interface": "workspace:*", "@tanstack/react-query": "^4.0.10", "eventemitter3": "^4.0.7", diff --git a/packages/interface/src/assets/svg/folder-white.svg b/packages/interface/src/assets/svg/folder-white.svg deleted file mode 100644 index 069ff8b01..000000000 --- a/packages/interface/src/assets/svg/folder-white.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/interface/src/assets/svg/folder.svg b/packages/interface/src/assets/svg/folder.svg deleted file mode 100644 index 5b9be3c5e..000000000 --- a/packages/interface/src/assets/svg/folder.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/packages/interface/src/components/file/FileItem.tsx b/packages/interface/src/components/file/FileItem.tsx index f402f42b9..8662c2ff6 100644 --- a/packages/interface/src/components/file/FileItem.tsx +++ b/packages/interface/src/components/file/FileItem.tsx @@ -1,10 +1,10 @@ +import { ReactComponent as Folder } from '@sd/assets/svgs/folder.svg'; import { LocationContext } from '@sd/client'; import { FilePath } from '@sd/core'; import clsx from 'clsx'; import React, { useContext } from 'react'; import icons from '../../assets/icons'; -import { ReactComponent as Folder } from '../../assets/svg/folder.svg'; import FileThumb from './FileThumb'; interface Props extends React.HTMLAttributes { diff --git a/packages/interface/src/components/icons/Folder.tsx b/packages/interface/src/components/icons/Folder.tsx index 41f6568b7..71b6b30fe 100644 --- a/packages/interface/src/components/icons/Folder.tsx +++ b/packages/interface/src/components/icons/Folder.tsx @@ -1,8 +1,7 @@ +import folderWhiteSvg from '@sd/assets/svgs/folder-white.svg'; +import folderSvg from '@sd/assets/svgs/folder.svg'; import React from 'react'; -import folderWhiteSvg from '../../assets/svg/folder-white.svg'; -import folderSvg from '../../assets/svg/folder.svg'; - interface FolderProps { /** * Append additional classes to the underlying SVG diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5adb32412510c4ec5efca44657dc5c2e76feadd..ae1e6663037add6fb16be299d44a7744840ecb18 100644 GIT binary patch delta 207 zcmcb$TVu~2jSXR3Y>CCisU^je7Yd0_-lQcwd7h=f`U zpsEv^oGT*&mA7i1$iICeKjRPm>1*s66{fe_Gb*>Av1bHgrtN3!nGah|H@(2AIeo(t zR^j&RmCQiQ0>rF9%m&2lK+LiIdL`$4!RZPC%pBAGrPxHamvwWRJA?I2UeKU8z5XmG j&vyRvoMLR#_gv!S+wORkvx;eY+g?uL?FX)NO1S|5M;lLk delta 160 zcmdn9N8{FRjSXR3lYd(ZPUaGpo_s<^97yv_p1@|knV&nHW%6km5vYJw^Lzg7@A(;j z=uhW#U{sv`)SgkPUCV(Hh?%x)IWQl#oLs{xIQ@hXqeZ(`6*CaC05K~NvjH(X5OZwT ys^Xj{xcx~tr@1p&@rtvYfs?mO@@=m_&&k0yo&N@>@b;dooXt$zWp8lGy8!@STsI#8