From 45f743e094631f5d99938f154f7847abbb3ac44f Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Sun, 10 Nov 2024 19:39:03 +0400 Subject: [PATCH 1/2] init --- example/tests/benchmark.tsx | 5 +- .../ios/HybridFileSystem.swift | 45 ++++++- .../generated/ios/FastIO-Swift-Cxx-Bridge.hpp | 40 ++++++ .../ios/FastIO-Swift-Cxx-Umbrella.hpp | 7 ++ .../ios/c++/HybridFileSystemSpecSwift.hpp | 19 ++- .../ios/swift/HybridFileSystemSpec.swift | 5 +- .../ios/swift/HybridFileSystemSpecCxx.swift | 25 +++- .../ios/swift/NativeFilePickerOptions.swift | 117 ++++++++++++++++++ .../ios/swift/WellKnownDirectory.swift | 56 +++++++++ .../shared/c++/HybridFileSystemSpec.cpp | 3 +- .../shared/c++/HybridFileSystemSpec.hpp | 12 +- .../shared/c++/NativeFilePickerOptions.hpp | 78 ++++++++++++ .../shared/c++/WellKnownDirectory.hpp | 94 ++++++++++++++ .../src/native/fs.nitro.ts | 14 ++- packages/react-native-fast-io/src/w3c/fs.d.ts | 6 +- packages/react-native-fast-io/src/w3c/fs.ts | 29 ++++- .../react-native-fast-io/src/w3c/network.ts | 2 - 17 files changed, 528 insertions(+), 29 deletions(-) create mode 100644 packages/react-native-fast-io/nitrogen/generated/ios/swift/NativeFilePickerOptions.swift create mode 100644 packages/react-native-fast-io/nitrogen/generated/ios/swift/WellKnownDirectory.swift create mode 100644 packages/react-native-fast-io/nitrogen/generated/shared/c++/NativeFilePickerOptions.hpp create mode 100644 packages/react-native-fast-io/nitrogen/generated/shared/c++/WellKnownDirectory.hpp diff --git a/example/tests/benchmark.tsx b/example/tests/benchmark.tsx index f25a86c..f80efba 100644 --- a/example/tests/benchmark.tsx +++ b/example/tests/benchmark.tsx @@ -445,7 +445,10 @@ const styles = StyleSheet.create({ // tbd: playground setTimeout(async () => { - const files = await showOpenFilePicker() + const files = await showOpenFilePicker({ + startIn: 'desktop', + types: [{ description: 'PDF files', accept: { 'text/plain': ['.txt'] } }], + }) if (!files[0]) { console.log('No file') diff --git a/packages/react-native-fast-io/ios/HybridFileSystem.swift b/packages/react-native-fast-io/ios/HybridFileSystem.swift index b419909..3deb451 100644 --- a/packages/react-native-fast-io/ios/HybridFileSystem.swift +++ b/packages/react-native-fast-io/ios/HybridFileSystem.swift @@ -6,6 +6,7 @@ // import Foundation +import UniformTypeIdentifiers import NitroModules import FastIOPrivate @@ -17,7 +18,7 @@ class HybridFileSystem : NSObject, UIDocumentPickerDelegate, HybridFileSystemSpe return HybridInputStream(stream: stream) } - func getFileMetadata(path: String) throws -> Metadata { + func getMetadata(path: String) throws -> Metadata { let attributes = try FileManager.default.attributesOfItem(atPath: path) let fileURL = URL(fileURLWithPath: path) @@ -30,8 +31,31 @@ class HybridFileSystem : NSObject, UIDocumentPickerDelegate, HybridFileSystemSpe ) } + func getWellKnownDirectoryPath(directory: WellKnownDirectory) throws -> String { + let url = switch directory { + case .desktop: + FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first + case .documents: + FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first + case .downloads: + FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first + case .music: + FileManager.default.urls(for: .musicDirectory, in: .userDomainMask).first + case .pictures: + FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first + case .videos: + FileManager.default.urls(for: .moviesDirectory, in: .userDomainMask).first + } + + guard let url else { + throw RuntimeError.error(withMessage: "Directory '\(directory)' is not available") + } + + return url.path + } + private var filePicker: (promise: Promise<[String]>, vc: UIDocumentPickerViewController)? - func showOpenFilePicker() throws -> Promise<[String]> { + func showOpenFilePicker(options: NativeFilePickerOptions?) throws -> Promise<[String]> { if filePicker != nil { return Promise.rejected(withError: RuntimeError.error(withMessage: "File picker already open")) } @@ -39,11 +63,26 @@ class HybridFileSystem : NSObject, UIDocumentPickerDelegate, HybridFileSystemSpe let promise = Promise<[String]>() DispatchQueue.main.async { + // Convert extensions to UTTypes, filtering out any that can't be converted + let utTypes: [UTType] = options?.extensions? + .compactMap { ext in + // Remove leading dot if present + let cleanExt = ext.hasPrefix(".") ? String(ext.dropFirst()) : ext + return UTType(filenameExtension: cleanExt) + } ?? [.item] + + let documentPicker = UIDocumentPickerViewController( - forOpeningContentTypes: [.item], + forOpeningContentTypes: utTypes, asCopy: true ) documentPicker.delegate = self + + documentPicker.allowsMultipleSelection = options?.multiple ?? false + + if let startIn = options?.startIn { + documentPicker.directoryURL = URL(fileURLWithPath: startIn, isDirectory: true) + } guard let vc = RCTUtilsWrapper.getPresentedViewController() else { promise.reject(withError: RuntimeError.error(withMessage: "Cannot present file picker")) diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Bridge.hpp b/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Bridge.hpp index e636d2b..7155761 100644 --- a/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Bridge.hpp +++ b/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Bridge.hpp @@ -26,6 +26,8 @@ namespace margelo::nitro::fastio { class HybridPassThroughStreamSpec; } namespace margelo::nitro::fastio { class HybridWebSocketManagerSpec; } // Forward declaration of `HybridWebSocketSpec` to properly resolve imports. namespace margelo::nitro::fastio { class HybridWebSocketSpec; } +// Forward declaration of `NativeFilePickerOptions` to properly resolve imports. +namespace margelo::nitro::fastio { struct NativeFilePickerOptions; } // Forward declarations of Swift defined types // Forward declaration of `HybridFileSystemSpecCxx` to properly resolve imports. @@ -51,12 +53,14 @@ namespace FastIO { class HybridWebSocketSpecCxx; } #include "HybridPassThroughStreamSpec.hpp" #include "HybridWebSocketManagerSpec.hpp" #include "HybridWebSocketSpec.hpp" +#include "NativeFilePickerOptions.hpp" #include #include #include #include #include #include +#include #include #include @@ -94,6 +98,42 @@ namespace margelo::nitro::fastio::bridge::swift { return PromiseHolder>(); } + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_bool_ = std::optional; + inline std::optional create_std__optional_bool_(const bool& value) { + return std::optional(value); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_std__string_ = std::optional; + inline std::optional create_std__optional_std__string_(const std::string& value) { + return std::optional(value); + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__vector_std__string__ = std::optional>; + inline std::optional> create_std__optional_std__vector_std__string__(const std::vector& value) { + return std::optional>(value); + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_NativeFilePickerOptions_ = std::optional; + inline std::optional create_std__optional_NativeFilePickerOptions_(const NativeFilePickerOptions& value) { + return std::optional(value); + } + // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Umbrella.hpp b/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Umbrella.hpp index 2bdb453..42fcc71 100644 --- a/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Umbrella.hpp +++ b/packages/react-native-fast-io/nitrogen/generated/ios/FastIO-Swift-Cxx-Umbrella.hpp @@ -26,10 +26,14 @@ namespace margelo::nitro::fastio { class HybridWebSocketManagerSpec; } namespace margelo::nitro::fastio { class HybridWebSocketSpec; } // Forward declaration of `Metadata` to properly resolve imports. namespace margelo::nitro::fastio { struct Metadata; } +// Forward declaration of `NativeFilePickerOptions` to properly resolve imports. +namespace margelo::nitro::fastio { struct NativeFilePickerOptions; } // Forward declaration of `RequestMethod` to properly resolve imports. namespace margelo::nitro::fastio { enum class RequestMethod; } // Forward declaration of `RequestOptions` to properly resolve imports. namespace margelo::nitro::fastio { struct RequestOptions; } +// Forward declaration of `WellKnownDirectory` to properly resolve imports. +namespace margelo::nitro::fastio { enum class WellKnownDirectory; } // Include C++ defined types #include "HybridFileSystemSpec.hpp" @@ -40,12 +44,15 @@ namespace margelo::nitro::fastio { struct RequestOptions; } #include "HybridWebSocketManagerSpec.hpp" #include "HybridWebSocketSpec.hpp" #include "Metadata.hpp" +#include "NativeFilePickerOptions.hpp" #include "RequestMethod.hpp" #include "RequestOptions.hpp" +#include "WellKnownDirectory.hpp" #include #include #include #include +#include #include #include diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/c++/HybridFileSystemSpecSwift.hpp b/packages/react-native-fast-io/nitrogen/generated/ios/c++/HybridFileSystemSpecSwift.hpp index 3daef48..6aa5af3 100644 --- a/packages/react-native-fast-io/nitrogen/generated/ios/c++/HybridFileSystemSpecSwift.hpp +++ b/packages/react-native-fast-io/nitrogen/generated/ios/c++/HybridFileSystemSpecSwift.hpp @@ -16,14 +16,21 @@ namespace FastIO { class HybridFileSystemSpecCxx; } namespace margelo::nitro::fastio { class HybridInputStreamSpec; } // Forward declaration of `Metadata` to properly resolve imports. namespace margelo::nitro::fastio { struct Metadata; } +// Forward declaration of `WellKnownDirectory` to properly resolve imports. +namespace margelo::nitro::fastio { enum class WellKnownDirectory; } +// Forward declaration of `NativeFilePickerOptions` to properly resolve imports. +namespace margelo::nitro::fastio { struct NativeFilePickerOptions; } #include #include "HybridInputStreamSpec.hpp" #include #include "Metadata.hpp" +#include "WellKnownDirectory.hpp" #include #include #include +#include +#include "NativeFilePickerOptions.hpp" #if __has_include() #include @@ -72,12 +79,16 @@ namespace margelo::nitro::fastio { auto __result = _swiftPart.createInputStream(path); return __result; } - inline Metadata getFileMetadata(const std::string& path) override { - auto __result = _swiftPart.getFileMetadata(path); + inline Metadata getMetadata(const std::string& path) override { + auto __result = _swiftPart.getMetadata(path); return __result; } - inline std::future> showOpenFilePicker() override { - auto __result = _swiftPart.showOpenFilePicker(); + inline std::string getWellKnownDirectoryPath(WellKnownDirectory directory) override { + auto __result = _swiftPart.getWellKnownDirectoryPath(static_cast(directory)); + return __result; + } + inline std::future> showOpenFilePicker(const std::optional& options) override { + auto __result = _swiftPart.showOpenFilePicker(options); return __result.getFuture(); } diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpec.swift b/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpec.swift index 67abaae..82ac174 100644 --- a/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpec.swift +++ b/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpec.swift @@ -33,6 +33,7 @@ public protocol HybridFileSystemSpec: AnyObject, HybridObjectSpec { // Methods func createInputStream(path: String) throws -> (any HybridInputStreamSpec) - func getFileMetadata(path: String) throws -> Metadata - func showOpenFilePicker() throws -> Promise<[String]> + func getMetadata(path: String) throws -> Metadata + func getWellKnownDirectoryPath(directory: WellKnownDirectory) throws -> String + func showOpenFilePicker(options: NativeFilePickerOptions?) throws -> Promise<[String]> } diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpecCxx.swift b/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpecCxx.swift index e9567c3..9abfd53 100644 --- a/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpecCxx.swift +++ b/packages/react-native-fast-io/nitrogen/generated/ios/swift/HybridFileSystemSpecCxx.swift @@ -114,9 +114,9 @@ public class HybridFileSystemSpecCxx { } @inline(__always) - public func getFileMetadata(path: std.string) -> Metadata { + public func getMetadata(path: std.string) -> Metadata { do { - let __result = try self.__implementation.getFileMetadata(path: String(path)) + let __result = try self.__implementation.getMetadata(path: String(path)) return __result } catch { let __message = "\(error.localizedDescription)" @@ -125,9 +125,26 @@ public class HybridFileSystemSpecCxx { } @inline(__always) - public func showOpenFilePicker() -> bridge.PromiseHolder_std__vector_std__string__ { + public func getWellKnownDirectoryPath(directory: Int32) -> std.string { do { - let __result = try self.__implementation.showOpenFilePicker() + let __result = try self.__implementation.getWellKnownDirectoryPath(directory: margelo.nitro.fastio.WellKnownDirectory(rawValue: directory)!) + return std.string(__result) + } catch { + let __message = "\(error.localizedDescription)" + fatalError("Swift errors can currently not be propagated to C++! See https://github.com/swiftlang/swift/issues/75290 (Error: \(__message))") + } + } + + @inline(__always) + public func showOpenFilePicker(options: bridge.std__optional_NativeFilePickerOptions_) -> bridge.PromiseHolder_std__vector_std__string__ { + do { + let __result = try self.__implementation.showOpenFilePicker(options: { () -> NativeFilePickerOptions? in + if let __unwrapped = options.value { + return __unwrapped + } else { + return nil + } + }()) return { () -> bridge.PromiseHolder_std__vector_std__string__ in let __promiseHolder = bridge.create_PromiseHolder_std__vector_std__string__() __result diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/swift/NativeFilePickerOptions.swift b/packages/react-native-fast-io/nitrogen/generated/ios/swift/NativeFilePickerOptions.swift new file mode 100644 index 0000000..2741611 --- /dev/null +++ b/packages/react-native-fast-io/nitrogen/generated/ios/swift/NativeFilePickerOptions.swift @@ -0,0 +1,117 @@ +/// +/// NativeFilePickerOptions.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `NativeFilePickerOptions`, backed by a C++ struct. + */ +public typealias NativeFilePickerOptions = margelo.nitro.fastio.NativeFilePickerOptions + +public extension NativeFilePickerOptions { + private typealias bridge = margelo.nitro.fastio.bridge.swift + + /** + * Create a new instance of `NativeFilePickerOptions`. + */ + init(multiple: Bool?, startIn: String?, extensions: [String]?) { + self.init({ () -> bridge.std__optional_bool_ in + if let __unwrappedValue = multiple { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = startIn { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__vector_std__string__ in + if let __unwrappedValue = extensions { + return bridge.create_std__optional_std__vector_std__string__({ () -> bridge.std__vector_std__string_ in + var __vector = bridge.create_std__vector_std__string_(__unwrappedValue.count) + for __item in __unwrappedValue { + __vector.push_back(std.string(__item)) + } + return __vector + }()) + } else { + return .init() + } + }()) + } + + var multiple: Bool? { + @inline(__always) + get { + return self.__multiple.value + } + @inline(__always) + set { + self.__multiple = { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }() + } + } + + var startIn: String? { + @inline(__always) + get { + return { () -> String? in + if let __unwrapped = self.__startIn.value { + return String(__unwrapped) + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__startIn = { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }() + } + } + + var extensions: [String]? { + @inline(__always) + get { + return { () -> [String]? in + if let __unwrapped = self.__extensions.value { + return __unwrapped.map({ __item in String(__item) }) + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__extensions = { () -> bridge.std__optional_std__vector_std__string__ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_std__vector_std__string__({ () -> bridge.std__vector_std__string_ in + var __vector = bridge.create_std__vector_std__string_(__unwrappedValue.count) + for __item in __unwrappedValue { + __vector.push_back(std.string(__item)) + } + return __vector + }()) + } else { + return .init() + } + }() + } + } +} diff --git a/packages/react-native-fast-io/nitrogen/generated/ios/swift/WellKnownDirectory.swift b/packages/react-native-fast-io/nitrogen/generated/ios/swift/WellKnownDirectory.swift new file mode 100644 index 0000000..c93e214 --- /dev/null +++ b/packages/react-native-fast-io/nitrogen/generated/ios/swift/WellKnownDirectory.swift @@ -0,0 +1,56 @@ +/// +/// WellKnownDirectory.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +/** + * Represents the JS union `WellKnownDirectory`, backed by a C++ enum. + */ +public typealias WellKnownDirectory = margelo.nitro.fastio.WellKnownDirectory + +public extension WellKnownDirectory { + /** + * Get a WellKnownDirectory for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "desktop": + self = .desktop + case "documents": + self = .documents + case "downloads": + self = .downloads + case "music": + self = .music + case "pictures": + self = .pictures + case "videos": + self = .videos + default: + return nil + } + } + + /** + * Get the String value this WellKnownDirectory represents. + */ + var stringValue: String { + switch self { + case .desktop: + return "desktop" + case .documents: + return "documents" + case .downloads: + return "downloads" + case .music: + return "music" + case .pictures: + return "pictures" + case .videos: + return "videos" + } + } +} diff --git a/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.cpp b/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.cpp index 6d2254c..2f01f35 100644 --- a/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.cpp +++ b/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.cpp @@ -15,7 +15,8 @@ namespace margelo::nitro::fastio { // load custom methods/properties registerHybrids(this, [](Prototype& prototype) { prototype.registerHybridMethod("createInputStream", &HybridFileSystemSpec::createInputStream); - prototype.registerHybridMethod("getFileMetadata", &HybridFileSystemSpec::getFileMetadata); + prototype.registerHybridMethod("getMetadata", &HybridFileSystemSpec::getMetadata); + prototype.registerHybridMethod("getWellKnownDirectoryPath", &HybridFileSystemSpec::getWellKnownDirectoryPath); prototype.registerHybridMethod("showOpenFilePicker", &HybridFileSystemSpec::showOpenFilePicker); }); } diff --git a/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.hpp b/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.hpp index 16d90eb..d03fc37 100644 --- a/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.hpp +++ b/packages/react-native-fast-io/nitrogen/generated/shared/c++/HybridFileSystemSpec.hpp @@ -17,13 +17,20 @@ namespace margelo::nitro::fastio { class HybridInputStreamSpec; } // Forward declaration of `Metadata` to properly resolve imports. namespace margelo::nitro::fastio { struct Metadata; } +// Forward declaration of `WellKnownDirectory` to properly resolve imports. +namespace margelo::nitro::fastio { enum class WellKnownDirectory; } +// Forward declaration of `NativeFilePickerOptions` to properly resolve imports. +namespace margelo::nitro::fastio { struct NativeFilePickerOptions; } #include #include "HybridInputStreamSpec.hpp" #include #include "Metadata.hpp" +#include "WellKnownDirectory.hpp" #include #include +#include +#include "NativeFilePickerOptions.hpp" namespace margelo::nitro::fastio { @@ -57,8 +64,9 @@ namespace margelo::nitro::fastio { public: // Methods virtual std::shared_ptr createInputStream(const std::string& path) = 0; - virtual Metadata getFileMetadata(const std::string& path) = 0; - virtual std::future> showOpenFilePicker() = 0; + virtual Metadata getMetadata(const std::string& path) = 0; + virtual std::string getWellKnownDirectoryPath(WellKnownDirectory directory) = 0; + virtual std::future> showOpenFilePicker(const std::optional& options) = 0; protected: // Hybrid Setup diff --git a/packages/react-native-fast-io/nitrogen/generated/shared/c++/NativeFilePickerOptions.hpp b/packages/react-native-fast-io/nitrogen/generated/shared/c++/NativeFilePickerOptions.hpp new file mode 100644 index 0000000..5ba0294 --- /dev/null +++ b/packages/react-native-fast-io/nitrogen/generated/shared/c++/NativeFilePickerOptions.hpp @@ -0,0 +1,78 @@ +/// +/// NativeFilePickerOptions.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include +#include +#include + +namespace margelo::nitro::fastio { + + /** + * A struct which can be represented as a JavaScript object (NativeFilePickerOptions). + */ + struct NativeFilePickerOptions { + public: + std::optional multiple SWIFT_PRIVATE; + std::optional startIn SWIFT_PRIVATE; + std::optional> extensions SWIFT_PRIVATE; + + public: + explicit NativeFilePickerOptions(std::optional multiple, std::optional startIn, std::optional> extensions): multiple(multiple), startIn(startIn), extensions(extensions) {} + }; + +} // namespace margelo::nitro::fastio + +namespace margelo::nitro { + + using namespace margelo::nitro::fastio; + + // C++ NativeFilePickerOptions <> JS NativeFilePickerOptions (object) + template <> + struct JSIConverter { + static inline NativeFilePickerOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return NativeFilePickerOptions( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "multiple")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "startIn")), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, "extensions")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const NativeFilePickerOptions& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "multiple", JSIConverter>::toJSI(runtime, arg.multiple)); + obj.setProperty(runtime, "startIn", JSIConverter>::toJSI(runtime, arg.startIn)); + obj.setProperty(runtime, "extensions", JSIConverter>>::toJSI(runtime, arg.extensions)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "multiple"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "startIn"))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, "extensions"))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/packages/react-native-fast-io/nitrogen/generated/shared/c++/WellKnownDirectory.hpp b/packages/react-native-fast-io/nitrogen/generated/shared/c++/WellKnownDirectory.hpp new file mode 100644 index 0000000..731308f --- /dev/null +++ b/packages/react-native-fast-io/nitrogen/generated/shared/c++/WellKnownDirectory.hpp @@ -0,0 +1,94 @@ +/// +/// WellKnownDirectory.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::fastio { + + /** + * An enum which can be represented as a JavaScript union (WellKnownDirectory). + */ + enum class WellKnownDirectory { + DESKTOP SWIFT_NAME(desktop) = 0, + DOCUMENTS SWIFT_NAME(documents) = 1, + DOWNLOADS SWIFT_NAME(downloads) = 2, + MUSIC SWIFT_NAME(music) = 3, + PICTURES SWIFT_NAME(pictures) = 4, + VIDEOS SWIFT_NAME(videos) = 5, + } CLOSED_ENUM; + +} // namespace margelo::nitro::fastio + +namespace margelo::nitro { + + using namespace margelo::nitro::fastio; + + // C++ WellKnownDirectory <> JS WellKnownDirectory (union) + template <> + struct JSIConverter { + static inline WellKnownDirectory fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + std::string unionValue = JSIConverter::fromJSI(runtime, arg); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("desktop"): return WellKnownDirectory::DESKTOP; + case hashString("documents"): return WellKnownDirectory::DOCUMENTS; + case hashString("downloads"): return WellKnownDirectory::DOWNLOADS; + case hashString("music"): return WellKnownDirectory::MUSIC; + case hashString("pictures"): return WellKnownDirectory::PICTURES; + case hashString("videos"): return WellKnownDirectory::VIDEOS; + default: [[unlikely]] + throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum WellKnownDirectory - invalid value!"); + } + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, WellKnownDirectory arg) { + switch (arg) { + case WellKnownDirectory::DESKTOP: return JSIConverter::toJSI(runtime, "desktop"); + case WellKnownDirectory::DOCUMENTS: return JSIConverter::toJSI(runtime, "documents"); + case WellKnownDirectory::DOWNLOADS: return JSIConverter::toJSI(runtime, "downloads"); + case WellKnownDirectory::MUSIC: return JSIConverter::toJSI(runtime, "music"); + case WellKnownDirectory::PICTURES: return JSIConverter::toJSI(runtime, "pictures"); + case WellKnownDirectory::VIDEOS: return JSIConverter::toJSI(runtime, "videos"); + default: [[unlikely]] + throw std::invalid_argument("Cannot convert WellKnownDirectory to JS - invalid value: " + + std::to_string(static_cast(arg)) + "!"); + } + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isString()) { + return false; + } + std::string unionValue = JSIConverter::fromJSI(runtime, value); + switch (hashString(unionValue.c_str(), unionValue.size())) { + case hashString("desktop"): + case hashString("documents"): + case hashString("downloads"): + case hashString("music"): + case hashString("pictures"): + case hashString("videos"): + return true; + default: + return false; + } + } + }; + +} // namespace margelo::nitro diff --git a/packages/react-native-fast-io/src/native/fs.nitro.ts b/packages/react-native-fast-io/src/native/fs.nitro.ts index 4f85f6f..9d95a98 100644 --- a/packages/react-native-fast-io/src/native/fs.nitro.ts +++ b/packages/react-native-fast-io/src/native/fs.nitro.ts @@ -10,11 +10,21 @@ export type Metadata = { lastModified: number } +type WellKnownDirectory = 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' + +export type NativeFilePickerOptions = { + multiple?: boolean + startIn?: string + extensions?: string[] +} + export interface FileSystem extends HybridObject<{ ios: 'swift' }> { createInputStream(path: string): InputStream - getFileMetadata(path: string): Metadata - showOpenFilePicker(): Promise + getMetadata(path: string): Metadata + getWellKnownDirectoryPath(directory: WellKnownDirectory): string + + showOpenFilePicker(options?: NativeFilePickerOptions): Promise } export const FileSystem = NitroModules.createHybridObject('FileSystem') diff --git a/packages/react-native-fast-io/src/w3c/fs.d.ts b/packages/react-native-fast-io/src/w3c/fs.d.ts index aaf792c..6666635 100644 --- a/packages/react-native-fast-io/src/w3c/fs.d.ts +++ b/packages/react-native-fast-io/src/w3c/fs.d.ts @@ -15,7 +15,7 @@ declare global { * @default "" */ description?: string | undefined - accept?: Record | undefined + accept: Record | undefined } interface FilePickerOptions { @@ -24,7 +24,7 @@ declare global { * @default false */ excludeAcceptAllOption?: boolean | undefined - startIn?: WellKnownDirectory | FileSystemHandle | undefined + startIn?: WellKnownDirectory | /* | FileSystemHandle */ undefined id?: string | undefined } @@ -43,7 +43,7 @@ declare global { interface DirectoryPickerOptions { id?: string | undefined - startIn?: WellKnownDirectory | FileSystemHandle | undefined + startIn?: WellKnownDirectory /* | FileSystemHandle */ | undefined /** * @default "read" */ diff --git a/packages/react-native-fast-io/src/w3c/fs.ts b/packages/react-native-fast-io/src/w3c/fs.ts index a6604dc..f8f4d5d 100644 --- a/packages/react-native-fast-io/src/w3c/fs.ts +++ b/packages/react-native-fast-io/src/w3c/fs.ts @@ -1,4 +1,4 @@ -import { FileSystem, Metadata } from '../native/fs.nitro' +import { FileSystem, Metadata, NativeFilePickerOptions } from '../native/fs.nitro' import { Blob } from './blob' import { toReadableStream } from './streams' @@ -54,7 +54,7 @@ class FileSystemFileHandle implements globalThis.FileSystemFileHandle { #metadata: Metadata constructor(path: string) { - this.#metadata = FileSystem.getFileMetadata(path) + this.#metadata = FileSystem.getMetadata(path) } async getFile() { @@ -79,10 +79,29 @@ class FileSystemFileHandle implements globalThis.FileSystemFileHandle { } export async function showOpenFilePicker( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - options?: OpenFilePickerOptions + options: OpenFilePickerOptions = {} ): Promise { - const paths = await FileSystem.showOpenFilePicker() + const nativePickerOptions: NativeFilePickerOptions = {} + + if (options.startIn) { + nativePickerOptions.startIn = FileSystem.getWellKnownDirectoryPath(options.startIn) + } + + if (options.multiple) { + nativePickerOptions.multiple = options.multiple + } + + if (options.types) { + nativePickerOptions.extensions = options.types?.reduce((acc, type) => { + if (!type.accept) { + return acc + } + return acc.concat(...Object.values(type.accept)) + }, []) + } + + const paths = await FileSystem.showOpenFilePicker(nativePickerOptions) + return paths.map((path) => new FileSystemFileHandle(path)) } diff --git a/packages/react-native-fast-io/src/w3c/network.ts b/packages/react-native-fast-io/src/w3c/network.ts index 930df7d..94a7fb0 100644 --- a/packages/react-native-fast-io/src/w3c/network.ts +++ b/packages/react-native-fast-io/src/w3c/network.ts @@ -1,5 +1,3 @@ -import { ReadableStream } from 'web-streams-polyfill' - import { Network, RequestMethod } from '../native/network.nitro' import { fromReadableStream } from './streams' From 6599ff86593ea43b26ac43403c3e21fbaaaadb7b Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Sun, 10 Nov 2024 19:40:18 +0400 Subject: [PATCH 2/2] chore --- packages/react-native-fast-io/ios/HybridFileSystem.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-native-fast-io/ios/HybridFileSystem.swift b/packages/react-native-fast-io/ios/HybridFileSystem.swift index 3deb451..d7de9d3 100644 --- a/packages/react-native-fast-io/ios/HybridFileSystem.swift +++ b/packages/react-native-fast-io/ios/HybridFileSystem.swift @@ -63,10 +63,8 @@ class HybridFileSystem : NSObject, UIDocumentPickerDelegate, HybridFileSystemSpe let promise = Promise<[String]>() DispatchQueue.main.async { - // Convert extensions to UTTypes, filtering out any that can't be converted let utTypes: [UTType] = options?.extensions? .compactMap { ext in - // Remove leading dot if present let cleanExt = ext.hasPrefix(".") ? String(ext.dropFirst()) : ext return UTType(filenameExtension: cleanExt) } ?? [.item]