diff --git a/apps/mobile/android/app/src/main/AndroidManifest.xml b/apps/mobile/android/app/src/main/AndroidManifest.xml index 93489fc80..bb2d8597e 100644 --- a/apps/mobile/android/app/src/main/AndroidManifest.xml +++ b/apps/mobile/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + diff --git a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj index 46c76ed5c..02a04ece6 100644 --- a/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Spacedrive.xcodeproj/project.pbxproj @@ -9,10 +9,10 @@ /* Begin PBXBuildFile section */ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; - 7CDE75B9D7E20774D9CF1E8E /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390E817FD38DD68785DD7F46 /* ExpoModulesProvider.swift */; }; - 8B92B5C3B5F4CB00CBA01D7B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = FCC488E672FE1D40B66D16D0 /* PrivacyInfo.xcprivacy */; }; - BA5DE8C0B7B409B9EE595756 /* libPods-Spacedrive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56F0D3B103AAADB203E5E955 /* libPods-Spacedrive.a */; }; + 97715081206FCB268DE6EF1C /* libPods-Spacedrive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D2433440EF620DAB0150F1F /* libPods-Spacedrive.a */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; + D7EEB2E25018FB2A40EE29D2 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25178F2D681F4D65F10117CC /* ExpoModulesProvider.swift */; }; + E48312EB51FF1B0800CD3FA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1F01297183477A50BDE411D2 /* PrivacyInfo.xcprivacy */; }; F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; }; /* End PBXBuildFile section */ @@ -20,16 +20,16 @@ 13B07F961A680F5B00A75B9A /* Spacedrive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Spacedrive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Spacedrive/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Spacedrive/Info.plist; sourceTree = ""; }; - 390E817FD38DD68785DD7F46 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Spacedrive/ExpoModulesProvider.swift"; sourceTree = ""; }; - 56F0D3B103AAADB203E5E955 /* libPods-Spacedrive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Spacedrive.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 5F62B24A166FED54E0D3DF77 /* 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 = ""; }; + 1F01297183477A50BDE411D2 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Spacedrive/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 22C87D25B25EC9845FB7C7F8 /* 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 = ""; }; + 25178F2D681F4D65F10117CC /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Spacedrive/ExpoModulesProvider.swift"; sourceTree = ""; }; + 6BF866963457A8D6655775AD /* 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 = ""; }; + 8D2433440EF620DAB0150F1F /* libPods-Spacedrive.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Spacedrive.a"; sourceTree = BUILT_PRODUCTS_DIR; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Spacedrive/SplashScreen.storyboard; sourceTree = ""; }; - ACA07CDC334C49B4BDAEDC2A /* 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 = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Spacedrive/AppDelegate.swift; sourceTree = ""; }; F11748442D0722820044C1D9 /* Spacedrive-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Spacedrive-Bridging-Header.h"; path = "Spacedrive/Spacedrive-Bridging-Header.h"; sourceTree = ""; }; - FCC488E672FE1D40B66D16D0 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Spacedrive/PrivacyInfo.xcprivacy; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -37,7 +37,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BA5DE8C0B7B409B9EE595756 /* libPods-Spacedrive.a in Frameworks */, + 97715081206FCB268DE6EF1C /* libPods-Spacedrive.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -53,7 +53,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, - FCC488E672FE1D40B66D16D0 /* PrivacyInfo.xcprivacy */, + 1F01297183477A50BDE411D2 /* PrivacyInfo.xcprivacy */, ); name = Spacedrive; sourceTree = ""; @@ -62,27 +62,27 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 56F0D3B103AAADB203E5E955 /* libPods-Spacedrive.a */, + 8D2433440EF620DAB0150F1F /* libPods-Spacedrive.a */, ); name = Frameworks; sourceTree = ""; }; - 680EC520E5F61C7DD3F56683 /* Pods */ = { + 42798537B08B5D76DFB457E7 /* Pods */ = { isa = PBXGroup; children = ( - 5F62B24A166FED54E0D3DF77 /* Pods-Spacedrive.debug.xcconfig */, - ACA07CDC334C49B4BDAEDC2A /* Pods-Spacedrive.release.xcconfig */, + 22C87D25B25EC9845FB7C7F8 /* Pods-Spacedrive.debug.xcconfig */, + 6BF866963457A8D6655775AD /* Pods-Spacedrive.release.xcconfig */, ); name = Pods; path = Pods; sourceTree = ""; }; - 7269C5DA8A3CBBD856E842F5 /* ExpoModulesProviders */ = { + 4FB1CBF77585F74645A1185E /* Spacedrive */ = { isa = PBXGroup; children = ( - E3FEA3520BE76C73533FE1C2 /* Spacedrive */, + 25178F2D681F4D65F10117CC /* ExpoModulesProvider.swift */, ); - name = ExpoModulesProviders; + name = Spacedrive; sourceTree = ""; }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { @@ -99,8 +99,8 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */, 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, - 680EC520E5F61C7DD3F56683 /* Pods */, - 7269C5DA8A3CBBD856E842F5 /* ExpoModulesProviders */, + B72BBC0A0CC1879D53F63DAB /* ExpoModulesProviders */, + 42798537B08B5D76DFB457E7 /* Pods */, ); indentWidth = 2; sourceTree = ""; @@ -115,6 +115,14 @@ name = Products; sourceTree = ""; }; + B72BBC0A0CC1879D53F63DAB /* ExpoModulesProviders */ = { + isa = PBXGroup; + children = ( + 4FB1CBF77585F74645A1185E /* Spacedrive */, + ); + name = ExpoModulesProviders; + sourceTree = ""; + }; BB2F792B24A3F905000567C9 /* Supporting */ = { isa = PBXGroup; children = ( @@ -124,14 +132,6 @@ path = Spacedrive/Supporting; sourceTree = ""; }; - E3FEA3520BE76C73533FE1C2 /* Spacedrive */ = { - isa = PBXGroup; - children = ( - 390E817FD38DD68785DD7F46 /* ExpoModulesProvider.swift */, - ); - name = Spacedrive; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -139,14 +139,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Spacedrive" */; buildPhases = ( - 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, - 060F37A74F1B325C73E68238 /* [Expo] Configure project */, + 52BA9D16F880670D9D7E318C /* [CP] Check Pods Manifest.lock */, + AFC9D743E8C8086DE4D89015 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, - A0C13D4FA5244C863B10C876 /* [CP] Embed Pods Frameworks */, + 4F55279F996324975C6F08DE /* [CP] Embed Pods Frameworks */, + 31F4BA6009A034F8388CF793 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -196,7 +196,7 @@ BB2F792D24A3F905000567C9 /* Expo.plist in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, - 8B92B5C3B5F4CB00CBA01D7B /* PrivacyInfo.xcprivacy in Resources */, + E48312EB51FF1B0800CD3FA4 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -220,53 +220,7 @@ shellPath = /bin/sh; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; }; - 060F37A74F1B325C73E68238 /* [Expo] Configure project */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/.xcode.env", - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/Spacedrive/Spacedrive.entitlements", - "$(SRCROOT)/Pods/Target Support Files/Pods-Spacedrive/expo-configure-project.sh", - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(SRCROOT)/Pods/Target Support Files/Pods-Spacedrive/ExpoModulesProvider.swift", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Spacedrive/expo-configure-project.sh\"\n"; - }; - 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Spacedrive-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - 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; - }; - 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = { + 31F4BA6009A034F8388CF793 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -302,7 +256,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-resources.sh\"\n"; showEnvVarsInLog = 0; }; - A0C13D4FA5244C863B10C876 /* [CP] Embed Pods Frameworks */ = { + 4F55279F996324975C6F08DE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -324,6 +278,52 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spacedrive/Pods-Spacedrive-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 52BA9D16F880670D9D7E318C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Spacedrive-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + 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; + }; + AFC9D743E8C8086DE4D89015 /* [Expo] Configure project */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env", + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/Spacedrive/Spacedrive.entitlements", + "$(SRCROOT)/Pods/Target Support Files/Pods-Spacedrive/expo-configure-project.sh", + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(SRCROOT)/Pods/Target Support Files/Pods-Spacedrive/ExpoModulesProvider.swift", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Spacedrive/expo-configure-project.sh\"\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -332,7 +332,7 @@ buildActionMask = 2147483647; files = ( F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */, - 7CDE75B9D7E20774D9CF1E8E /* ExpoModulesProvider.swift in Sources */, + D7EEB2E25018FB2A40EE29D2 /* ExpoModulesProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -341,7 +341,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5F62B24A166FED54E0D3DF77 /* Pods-Spacedrive.debug.xcconfig */; + baseConfigurationReference = 22C87D25B25EC9845FB7C7F8 /* Pods-Spacedrive.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -378,7 +378,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = ACA07CDC334C49B4BDAEDC2A /* Pods-Spacedrive.release.xcconfig */; + baseConfigurationReference = 6BF866963457A8D6655775AD /* Pods-Spacedrive.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/apps/mobile/ios/Spacedrive/Info.plist b/apps/mobile/ios/Spacedrive/Info.plist index 429dceb96..6d24a22f8 100644 --- a/apps/mobile/ios/Spacedrive/Info.plist +++ b/apps/mobile/ios/Spacedrive/Info.plist @@ -51,6 +51,10 @@ NSAllowsLocalNetworking + NSCameraUsageDescription + Allow $(PRODUCT_NAME) to access your camera + NSMicrophoneUsageDescription + Allow $(PRODUCT_NAME) to access your microphone RCTNewArchEnabled UILaunchStoryboardName @@ -59,8 +63,6 @@ arm64 - NSCameraUsageDescription - Camera access is required to scan QR codes for device pairing UIRequiresFullScreen UIStatusBarStyle diff --git a/apps/mobile/modules/sd-mobile-core/ios/SDMobileCoreModule.swift b/apps/mobile/modules/sd-mobile-core/ios/SDMobileCoreModule.swift index c5603f44a..0381a0665 100644 --- a/apps/mobile/modules/sd-mobile-core/ios/SDMobileCoreModule.swift +++ b/apps/mobile/modules/sd-mobile-core/ios/SDMobileCoreModule.swift @@ -17,6 +17,12 @@ func spawn_core_event_listener( callback_data: UnsafeMutableRawPointer? ) +@_silgen_name("spawn_core_log_listener") +func spawn_core_log_listener( + callback: @convention(c) (UnsafeMutableRawPointer?, UnsafePointer) -> Void, + callback_data: UnsafeMutableRawPointer? +) + @_silgen_name("shutdown_core") func shutdown_core() @@ -46,29 +52,72 @@ private func eventCallback(data: UnsafeMutableRawPointer?, event: UnsafePointer< } } +// Callback for logs +private func logCallback(data: UnsafeMutableRawPointer?, log: UnsafePointer) { + guard let data = data else { return } + let module = Unmanaged.fromOpaque(data).takeUnretainedValue() + let logStr = String(cString: log) + if module.logListeners > 0 { + module.sendEvent("SDCoreLog", ["body": logStr]) + } +} + // Expo Module public class SDMobileCoreModule: Module { var listeners = 0 + var logListeners = 0 private var registeredWithRust = false + private var logRegisteredWithRust = false public func definition() -> ModuleDefinition { Name("SDMobileCore") - Events("SDCoreEvent") + Events("SDCoreEvent", "SDCoreLog") - OnStartObserving { + OnStartObserving("SDCoreEvent") { + NSLog("[SDMobileCore] 📡 OnStartObserving SDCoreEvent triggered") + + // Register event listener if not already done if !self.registeredWithRust { + NSLog("[SDMobileCore] 🚀 Registering event listener...") spawn_core_event_listener( callback: eventCallback, callback_data: Unmanaged.passUnretained(self).toOpaque() ) self.registeredWithRust = true + NSLog("[SDMobileCore] ✅ Event listener registered with Rust") } + self.listeners += 1 + NSLog("[SDMobileCore] 📊 SDCoreEvent listeners: \(self.listeners)") } - OnStopObserving { + OnStopObserving("SDCoreEvent") { self.listeners -= 1 + NSLog("[SDMobileCore] 📉 SDCoreEvent listeners: \(self.listeners)") + } + + OnStartObserving("SDCoreLog") { + NSLog("[SDMobileCore] 📡 OnStartObserving SDCoreLog triggered") + + // Register log listener if not already done + if !self.logRegisteredWithRust { + NSLog("[SDMobileCore] 🚀 Registering log listener...") + spawn_core_log_listener( + callback: logCallback, + callback_data: Unmanaged.passUnretained(self).toOpaque() + ) + self.logRegisteredWithRust = true + NSLog("[SDMobileCore] ✅ Log listener registered with Rust") + } + + self.logListeners += 1 + NSLog("[SDMobileCore] 📊 SDCoreLog listeners: \(self.logListeners)") + } + + OnStopObserving("SDCoreLog") { + self.logListeners -= 1 + NSLog("[SDMobileCore] 📉 SDCoreLog listeners: \(self.logListeners)") } Function("initialize") { (dataDir: String?, deviceName: String?) throws -> Int in diff --git a/apps/mobile/src/client/hooks/useClient.tsx b/apps/mobile/src/client/hooks/useClient.tsx index ced102b4f..a8fea0745 100644 --- a/apps/mobile/src/client/hooks/useClient.tsx +++ b/apps/mobile/src/client/hooks/useClient.tsx @@ -1,42 +1,17 @@ import React, { - createContext, - useContext, useEffect, useState, - useMemo, ReactNode, } from "react"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { SpacedriveClientContext, queryClient, useSpacedriveClient } from "@sd/ts-client/src/hooks/useClient"; import { SpacedriveClient } from "../SpacedriveClient"; import { View, Text, ActivityIndicator, StyleSheet } from "react-native"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import { SDMobileCore } from "sd-mobile-core"; -// Context for the Spacedrive client -const ClientContext = createContext(null); - -/** - * Hook to access the Spacedrive client. - * Must be used within a SpacedriveProvider. - */ -export function useSpacedriveClient(): SpacedriveClient { - const client = useContext(ClientContext); - if (!client) { - throw new Error( - "useSpacedriveClient must be used within SpacedriveProvider", - ); - } - return client; -} - -// Create a stable QueryClient instance -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 30 * 1000, // 30 seconds - retry: 2, - }, - }, -}); +// Re-export the shared hook +export { useSpacedriveClient }; interface SpacedriveProviderProps { children: ReactNode; @@ -57,11 +32,25 @@ export function SpacedriveProvider({ useEffect(() => { let mounted = true; + let unsubscribeLogs: (() => void) | null = null; async function init() { try { await client.initialize(deviceName ?? "Spacedrive Mobile"); + // Subscribe to core logs AFTER core is initialized + console.log("[SpacedriveProvider] 🔌 Subscribing to core logs..."); + unsubscribeLogs = SDMobileCore.addLogListener((log) => { + console.log("[SpacedriveProvider] 📝 RAW LOG RECEIVED:", log); + try { + const logData = JSON.parse(log.body); + console.log(`[CORE ${logData.level}] ${logData.target}: ${logData.message}`); + } catch (e) { + console.error("[SpacedriveProvider] Failed to parse log:", log.body); + } + }); + console.log("[SpacedriveProvider] ✅ Log listener subscribed"); + // Load persisted library ID from storage const storedData = await AsyncStorage.getItem("spacedrive-sidebar"); let libraryIdSet = false; @@ -119,6 +108,7 @@ export function SpacedriveProvider({ return () => { mounted = false; + if (unsubscribeLogs) unsubscribeLogs(); client.destroy(); }; }, [client, deviceName]); @@ -144,11 +134,11 @@ export function SpacedriveProvider({ } return ( - - + + {children} - - + + ); } diff --git a/apps/mobile/src/client/index.ts b/apps/mobile/src/client/index.ts index c0bd39218..59c885e6f 100644 --- a/apps/mobile/src/client/index.ts +++ b/apps/mobile/src/client/index.ts @@ -10,3 +10,6 @@ export { useCoreAction, useLibraryAction, } from "./hooks/useQuery"; + +// Re-export shared hooks from ts-client +export { useNormalizedQuery } from "@sd/ts-client/src/hooks/useNormalizedQuery"; diff --git a/packages/interface/src/components/Explorer/ExplorerView.tsx b/packages/interface/src/components/Explorer/ExplorerView.tsx index 8071f0577..e24e6f2a5 100644 --- a/packages/interface/src/components/Explorer/ExplorerView.tsx +++ b/packages/interface/src/components/Explorer/ExplorerView.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; import { useParams, useSearchParams } from "react-router-dom"; import { useExplorer } from "./context"; -import { useNormalizedCache } from "../../context"; +import { useNormalizedQuery } from "../../context"; import { GridView } from "./views/GridView"; import { ListView } from "./views/ListView"; import { MediaView } from "./views/MediaView"; @@ -50,7 +50,7 @@ export function ExplorerView() { const isPreviewActive = !!quickPreviewFileId; // Fetch locations to get the SdPath for this locationId - const locationsQuery = useNormalizedCache({ + const locationsQuery = useNormalizedQuery({ wireMethod: "query:locations.list", input: null, resourceType: "location", diff --git a/packages/interface/src/components/Explorer/ViewSettings.tsx b/packages/interface/src/components/Explorer/ViewSettings.tsx index f08435fab..07aa04f6a 100644 --- a/packages/interface/src/components/Explorer/ViewSettings.tsx +++ b/packages/interface/src/components/Explorer/ViewSettings.tsx @@ -180,6 +180,30 @@ export function ViewSettings({ className }: ViewSettingsProps) { /> + + {/* Folders First Toggle */} +
+ + +
, document.body diff --git a/packages/interface/src/components/Explorer/components/PathBar.tsx b/packages/interface/src/components/Explorer/components/PathBar.tsx index a09bb3242..8fb5599ac 100644 --- a/packages/interface/src/components/Explorer/components/PathBar.tsx +++ b/packages/interface/src/components/Explorer/components/PathBar.tsx @@ -6,7 +6,7 @@ import type { SdPath, LibraryDeviceInfo } from "@sd/ts-client"; import { getDeviceIconBySlug } from "@sd/ts-client"; import { sdPathToUri } from "../utils"; import LaptopIcon from "@sd/assets/icons/Laptop.png"; -import { useNormalizedCache } from "@sd/ts-client"; +import { useNormalizedQuery } from "@sd/ts-client"; interface PathBarProps { path: SdPath; diff --git a/packages/interface/src/components/Explorer/views/MediaView/MediaView.tsx b/packages/interface/src/components/Explorer/views/MediaView/MediaView.tsx index e24de37d0..b24058c1a 100644 --- a/packages/interface/src/components/Explorer/views/MediaView/MediaView.tsx +++ b/packages/interface/src/components/Explorer/views/MediaView/MediaView.tsx @@ -9,7 +9,7 @@ import { } from "react"; import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; -import { useNormalizedCache } from "../../../../context"; +import { useNormalizedQuery } from "../../../../context"; import { MediaViewItem } from "./MediaViewItem"; import { DateHeader, DATE_HEADER_HEIGHT } from "./DateHeader"; import { formatDate, getItemDate, normalizeDateToMidnight } from "./utils"; @@ -109,7 +109,7 @@ export function MediaView() { }, []); // Query for all media files from current path with descendants - const mediaQuery = useNormalizedCache({ + const mediaQuery = useNormalizedQuery({ wireMethod: "query:files.media_listing", input: currentPath ? { diff --git a/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx b/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx index 9a3cb5999..ca2f08f60 100644 --- a/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx +++ b/packages/interface/src/components/Explorer/views/SizeView/SizeView.tsx @@ -3,7 +3,7 @@ import * as d3 from "d3"; import type { File, DirectorySortBy } from "@sd/ts-client"; import { useExplorer } from "../../context"; import { useSelection } from "../../SelectionContext"; -import { useNormalizedCache } from "../../../../context"; +import { useNormalizedQuery } from "../../../../context"; import { formatBytes } from "../../utils"; import { TopBarButton, TopBarButtonGroup } from "@sd/ui"; import { ArrowsOut, ArrowCounterClockwise, Plus, Minus } from "@phosphor-icons/react"; @@ -81,10 +81,10 @@ function getFileType(file: File): string { } export function SizeView() { - const { currentPath, sortBy, setCurrentPath } = useExplorer(); + const { currentPath, sortBy, setCurrentPath, viewSettings } = useExplorer(); const { selectedFiles, selectFile } = useSelection(); - const directoryQuery = useNormalizedCache({ + const directoryQuery = useNormalizedQuery({ wireMethod: "query:files.directory_listing", input: currentPath ? { @@ -92,6 +92,7 @@ export function SizeView() { limit: null, include_hidden: false, sort_by: sortBy as DirectorySortBy, + folders_first: viewSettings.foldersFirst, } : null!, resourceType: "file", diff --git a/packages/interface/src/components/QuickPreview/QuickPreview.tsx b/packages/interface/src/components/QuickPreview/QuickPreview.tsx index a8f06179f..3b8cfbec6 100644 --- a/packages/interface/src/components/QuickPreview/QuickPreview.tsx +++ b/packages/interface/src/components/QuickPreview/QuickPreview.tsx @@ -1,4 +1,4 @@ -import { useNormalizedCache } from "../../context"; +import { useNormalizedQuery } from "../../context"; import { usePlatform } from "../../platform"; import type { File } from "@sd/ts-client"; import { useEffect, useState } from "react"; @@ -71,7 +71,7 @@ export function QuickPreview() { } }, [platform]); - const { data: file, isLoading, error } = useNormalizedCache<{ file_id: string }, File>({ + const { data: file, isLoading, error } = useNormalizedQuery<{ file_id: string }, File>({ wireMethod: "query:files.by_id", input: { file_id: fileId! }, resourceType: "file", diff --git a/packages/interface/src/components/QuickPreview/QuickPreviewFullscreen.tsx b/packages/interface/src/components/QuickPreview/QuickPreviewFullscreen.tsx index 53c02aa4f..9482a8e8a 100644 --- a/packages/interface/src/components/QuickPreview/QuickPreviewFullscreen.tsx +++ b/packages/interface/src/components/QuickPreview/QuickPreviewFullscreen.tsx @@ -3,7 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion'; import { X, ArrowLeft, ArrowRight } from '@phosphor-icons/react'; import { useEffect, useState } from 'react'; import type { File } from '@sd/ts-client'; -import { useNormalizedCache } from '../../context'; +import { useNormalizedQuery } from '../../context'; import { ContentRenderer } from './ContentRenderer'; import { TopBarPortal } from '../../TopBar'; @@ -40,7 +40,7 @@ export function QuickPreviewFullscreen({ setIsZoomed(false); }, [fileId]); - const { data: file, isLoading, error } = useNormalizedCache<{ file_id: string }, File>({ + const { data: file, isLoading, error } = useNormalizedQuery<{ file_id: string }, File>({ wireMethod: 'query:files.by_id', input: { file_id: fileId }, resourceType: 'file', diff --git a/packages/interface/src/components/SpacesSidebar/LocationsGroup.tsx b/packages/interface/src/components/SpacesSidebar/LocationsGroup.tsx index b5775f7d1..c706deadb 100644 --- a/packages/interface/src/components/SpacesSidebar/LocationsGroup.tsx +++ b/packages/interface/src/components/SpacesSidebar/LocationsGroup.tsx @@ -1,7 +1,7 @@ import { CaretRight, Folder } from "@phosphor-icons/react"; import clsx from "clsx"; import { useNavigate } from "react-router-dom"; -import { useNormalizedCache } from "@sd/ts-client"; +import { useNormalizedQuery } from "@sd/ts-client"; import { SpaceItem } from "./SpaceItem"; interface LocationsGroupProps { @@ -12,7 +12,7 @@ interface LocationsGroupProps { export function LocationsGroup({ isCollapsed, onToggle }: LocationsGroupProps) { const navigate = useNavigate(); - const { data: locationsData } = useNormalizedCache({ + const { data: locationsData } = useNormalizedQuery({ wireMethod: "query:locations.list", input: null, // Unit struct serializes as null, not {} resourceType: "location",