react-native-picture-selector 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +627 -0
- package/android/CMakeLists.txt +30 -0
- package/android/build.gradle +79 -0
- package/android/proguard-rules.pro +21 -0
- package/android/src/main/AndroidManifest.xml +39 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/GlideEngine.kt +80 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/HybridPictureSelector.kt +138 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/LubanCompressEngine.kt +58 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/MediaAssetMapper.kt +69 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/NitroPictureSelectorPackage.kt +52 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/PictureSelectorOptionsMapper.kt +105 -0
- package/android/src/main/kotlin/com/margelo/pictureselector/UCropEngine.kt +57 -0
- package/android/src/main/res/xml/file_paths.xml +8 -0
- package/ios/HybridPictureSelector.swift +386 -0
- package/ios/NitroPictureSelector.podspec +39 -0
- package/lib/commonjs/PictureSelector.js +74 -0
- package/lib/commonjs/PictureSelector.js.map +1 -0
- package/lib/commonjs/index.js +39 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/PictureSelector.nitro.js +34 -0
- package/lib/commonjs/specs/PictureSelector.nitro.js.map +1 -0
- package/lib/commonjs/types.js +44 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/usePictureSelector.js +122 -0
- package/lib/commonjs/usePictureSelector.js.map +1 -0
- package/lib/module/PictureSelector.js +71 -0
- package/lib/module/PictureSelector.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/specs/PictureSelector.nitro.js +36 -0
- package/lib/module/specs/PictureSelector.nitro.js.map +1 -0
- package/lib/module/types.js +29 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/usePictureSelector.js +119 -0
- package/lib/module/usePictureSelector.js.map +1 -0
- package/lib/typescript/PictureSelector.d.ts +23 -0
- package/lib/typescript/PictureSelector.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +6 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/specs/PictureSelector.nitro.d.ts +96 -0
- package/lib/typescript/specs/PictureSelector.nitro.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +16 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/usePictureSelector.d.ts +26 -0
- package/lib/typescript/usePictureSelector.d.ts.map +1 -0
- package/nitro.json +11 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroPictureSelector+autolinking.cmake +81 -0
- package/nitrogen/generated/android/NitroPictureSelector+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroPictureSelectorOnLoad.cpp +41 -0
- package/nitrogen/generated/android/NitroPictureSelectorOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JCompressOptions.hpp +69 -0
- package/nitrogen/generated/android/c++/JCropOptions.hpp +73 -0
- package/nitrogen/generated/android/c++/JHybridHybridPictureSelectorSpec.cpp +125 -0
- package/nitrogen/generated/android/c++/JHybridHybridPictureSelectorSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JMediaAsset.hpp +98 -0
- package/nitrogen/generated/android/c++/JMediaType.hpp +61 -0
- package/nitrogen/generated/android/c++/JPickerTheme.hpp +64 -0
- package/nitrogen/generated/android/c++/JPictureSelectorOptions.hpp +121 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/CompressOptions.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/CropOptions.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/HybridHybridPictureSelectorSpec.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/MediaAsset.kt +68 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/MediaType.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/NitroPictureSelectorOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/PickerTheme.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/PictureSelectorOptions.kt +65 -0
- package/nitrogen/generated/ios/NitroPictureSelector+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroPictureSelector-Swift-Cxx-Bridge.cpp +49 -0
- package/nitrogen/generated/ios/NitroPictureSelector-Swift-Cxx-Bridge.hpp +270 -0
- package/nitrogen/generated/ios/NitroPictureSelector-Swift-Cxx-Umbrella.hpp +65 -0
- package/nitrogen/generated/ios/c++/HybridHybridPictureSelectorSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridHybridPictureSelectorSpecSwift.hpp +110 -0
- package/nitrogen/generated/ios/swift/CompressOptions.swift +83 -0
- package/nitrogen/generated/ios/swift/CropOptions.swift +101 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_MediaAsset_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridHybridPictureSelectorSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridHybridPictureSelectorSpec_cxx.swift +176 -0
- package/nitrogen/generated/ios/swift/MediaAsset.swift +118 -0
- package/nitrogen/generated/ios/swift/MediaType.swift +44 -0
- package/nitrogen/generated/ios/swift/PickerTheme.swift +48 -0
- package/nitrogen/generated/ios/swift/PictureSelectorOptions.swift +182 -0
- package/nitrogen/generated/shared/c++/CompressOptions.hpp +95 -0
- package/nitrogen/generated/shared/c++/CropOptions.hpp +99 -0
- package/nitrogen/generated/shared/c++/HybridHybridPictureSelectorSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridHybridPictureSelectorSpec.hpp +69 -0
- package/nitrogen/generated/shared/c++/MediaAsset.hpp +124 -0
- package/nitrogen/generated/shared/c++/MediaType.hpp +80 -0
- package/nitrogen/generated/shared/c++/PickerTheme.hpp +84 -0
- package/nitrogen/generated/shared/c++/PictureSelectorOptions.hpp +132 -0
- package/package.json +76 -0
- package/src/PictureSelector.ts +72 -0
- package/src/index.ts +16 -0
- package/src/specs/PictureSelector.nitro.ts +121 -0
- package/src/types.ts +38 -0
- package/src/usePictureSelector.ts +102 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// PickerTheme.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#if __has_include(<NitroModules/NitroHash.hpp>)
|
|
11
|
+
#include <NitroModules/NitroHash.hpp>
|
|
12
|
+
#else
|
|
13
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
14
|
+
#endif
|
|
15
|
+
#if __has_include(<NitroModules/JSIConverter.hpp>)
|
|
16
|
+
#include <NitroModules/JSIConverter.hpp>
|
|
17
|
+
#else
|
|
18
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
19
|
+
#endif
|
|
20
|
+
#if __has_include(<NitroModules/NitroDefines.hpp>)
|
|
21
|
+
#include <NitroModules/NitroDefines.hpp>
|
|
22
|
+
#else
|
|
23
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
namespace margelo::nitro::margelo::pictureselector {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* An enum which can be represented as a JavaScript union (PickerTheme).
|
|
30
|
+
*/
|
|
31
|
+
enum class PickerTheme {
|
|
32
|
+
DEFAULT SWIFT_NAME(default) = 0,
|
|
33
|
+
WECHAT SWIFT_NAME(wechat) = 1,
|
|
34
|
+
WHITE SWIFT_NAME(white) = 2,
|
|
35
|
+
DARK SWIFT_NAME(dark) = 3,
|
|
36
|
+
} CLOSED_ENUM;
|
|
37
|
+
|
|
38
|
+
} // namespace margelo::nitro::margelo::pictureselector
|
|
39
|
+
|
|
40
|
+
namespace margelo::nitro {
|
|
41
|
+
|
|
42
|
+
// C++ PickerTheme <> JS PickerTheme (union)
|
|
43
|
+
template <>
|
|
44
|
+
struct JSIConverter<margelo::nitro::margelo::pictureselector::PickerTheme> final {
|
|
45
|
+
static inline margelo::nitro::margelo::pictureselector::PickerTheme fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
46
|
+
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
|
|
47
|
+
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
48
|
+
case hashString("default"): return margelo::nitro::margelo::pictureselector::PickerTheme::DEFAULT;
|
|
49
|
+
case hashString("wechat"): return margelo::nitro::margelo::pictureselector::PickerTheme::WECHAT;
|
|
50
|
+
case hashString("white"): return margelo::nitro::margelo::pictureselector::PickerTheme::WHITE;
|
|
51
|
+
case hashString("dark"): return margelo::nitro::margelo::pictureselector::PickerTheme::DARK;
|
|
52
|
+
default: [[unlikely]]
|
|
53
|
+
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum PickerTheme - invalid value!");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::margelo::pictureselector::PickerTheme arg) {
|
|
57
|
+
switch (arg) {
|
|
58
|
+
case margelo::nitro::margelo::pictureselector::PickerTheme::DEFAULT: return JSIConverter<std::string>::toJSI(runtime, "default");
|
|
59
|
+
case margelo::nitro::margelo::pictureselector::PickerTheme::WECHAT: return JSIConverter<std::string>::toJSI(runtime, "wechat");
|
|
60
|
+
case margelo::nitro::margelo::pictureselector::PickerTheme::WHITE: return JSIConverter<std::string>::toJSI(runtime, "white");
|
|
61
|
+
case margelo::nitro::margelo::pictureselector::PickerTheme::DARK: return JSIConverter<std::string>::toJSI(runtime, "dark");
|
|
62
|
+
default: [[unlikely]]
|
|
63
|
+
throw std::invalid_argument("Cannot convert PickerTheme to JS - invalid value: "
|
|
64
|
+
+ std::to_string(static_cast<int>(arg)) + "!");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
68
|
+
if (!value.isString()) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
|
|
72
|
+
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
73
|
+
case hashString("default"):
|
|
74
|
+
case hashString("wechat"):
|
|
75
|
+
case hashString("white"):
|
|
76
|
+
case hashString("dark"):
|
|
77
|
+
return true;
|
|
78
|
+
default:
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// PictureSelectorOptions.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#if __has_include(<NitroModules/JSIConverter.hpp>)
|
|
11
|
+
#include <NitroModules/JSIConverter.hpp>
|
|
12
|
+
#else
|
|
13
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
14
|
+
#endif
|
|
15
|
+
#if __has_include(<NitroModules/NitroDefines.hpp>)
|
|
16
|
+
#include <NitroModules/NitroDefines.hpp>
|
|
17
|
+
#else
|
|
18
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
19
|
+
#endif
|
|
20
|
+
#if __has_include(<NitroModules/JSIHelpers.hpp>)
|
|
21
|
+
#include <NitroModules/JSIHelpers.hpp>
|
|
22
|
+
#else
|
|
23
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
|
+
#endif
|
|
25
|
+
#if __has_include(<NitroModules/PropNameIDCache.hpp>)
|
|
26
|
+
#include <NitroModules/PropNameIDCache.hpp>
|
|
27
|
+
#else
|
|
28
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
// Forward declaration of `MediaType` to properly resolve imports.
|
|
32
|
+
namespace margelo::nitro::margelo::pictureselector { enum class MediaType; }
|
|
33
|
+
// Forward declaration of `CropOptions` to properly resolve imports.
|
|
34
|
+
namespace margelo::nitro::margelo::pictureselector { struct CropOptions; }
|
|
35
|
+
// Forward declaration of `CompressOptions` to properly resolve imports.
|
|
36
|
+
namespace margelo::nitro::margelo::pictureselector { struct CompressOptions; }
|
|
37
|
+
// Forward declaration of `PickerTheme` to properly resolve imports.
|
|
38
|
+
namespace margelo::nitro::margelo::pictureselector { enum class PickerTheme; }
|
|
39
|
+
|
|
40
|
+
#include "MediaType.hpp"
|
|
41
|
+
#include <optional>
|
|
42
|
+
#include "CropOptions.hpp"
|
|
43
|
+
#include "CompressOptions.hpp"
|
|
44
|
+
#include "PickerTheme.hpp"
|
|
45
|
+
#include <string>
|
|
46
|
+
#include <vector>
|
|
47
|
+
|
|
48
|
+
namespace margelo::nitro::margelo::pictureselector {
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A struct which can be represented as a JavaScript object (PictureSelectorOptions).
|
|
52
|
+
*/
|
|
53
|
+
struct PictureSelectorOptions final {
|
|
54
|
+
public:
|
|
55
|
+
std::optional<MediaType> mediaType SWIFT_PRIVATE;
|
|
56
|
+
std::optional<double> maxCount SWIFT_PRIVATE;
|
|
57
|
+
std::optional<bool> enableCamera SWIFT_PRIVATE;
|
|
58
|
+
std::optional<CropOptions> crop SWIFT_PRIVATE;
|
|
59
|
+
std::optional<CompressOptions> compress SWIFT_PRIVATE;
|
|
60
|
+
std::optional<double> maxVideoDuration SWIFT_PRIVATE;
|
|
61
|
+
std::optional<double> minVideoDuration SWIFT_PRIVATE;
|
|
62
|
+
std::optional<PickerTheme> theme SWIFT_PRIVATE;
|
|
63
|
+
std::optional<std::string> themeColor SWIFT_PRIVATE;
|
|
64
|
+
std::optional<std::vector<std::string>> selectedAssets SWIFT_PRIVATE;
|
|
65
|
+
|
|
66
|
+
public:
|
|
67
|
+
PictureSelectorOptions() = default;
|
|
68
|
+
explicit PictureSelectorOptions(std::optional<MediaType> mediaType, std::optional<double> maxCount, std::optional<bool> enableCamera, std::optional<CropOptions> crop, std::optional<CompressOptions> compress, std::optional<double> maxVideoDuration, std::optional<double> minVideoDuration, std::optional<PickerTheme> theme, std::optional<std::string> themeColor, std::optional<std::vector<std::string>> selectedAssets): mediaType(mediaType), maxCount(maxCount), enableCamera(enableCamera), crop(crop), compress(compress), maxVideoDuration(maxVideoDuration), minVideoDuration(minVideoDuration), theme(theme), themeColor(themeColor), selectedAssets(selectedAssets) {}
|
|
69
|
+
|
|
70
|
+
public:
|
|
71
|
+
friend bool operator==(const PictureSelectorOptions& lhs, const PictureSelectorOptions& rhs) = default;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
} // namespace margelo::nitro::margelo::pictureselector
|
|
75
|
+
|
|
76
|
+
namespace margelo::nitro {
|
|
77
|
+
|
|
78
|
+
// C++ PictureSelectorOptions <> JS PictureSelectorOptions (object)
|
|
79
|
+
template <>
|
|
80
|
+
struct JSIConverter<margelo::nitro::margelo::pictureselector::PictureSelectorOptions> final {
|
|
81
|
+
static inline margelo::nitro::margelo::pictureselector::PictureSelectorOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
82
|
+
jsi::Object obj = arg.asObject(runtime);
|
|
83
|
+
return margelo::nitro::margelo::pictureselector::PictureSelectorOptions(
|
|
84
|
+
JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::MediaType>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mediaType"))),
|
|
85
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "maxCount"))),
|
|
86
|
+
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "enableCamera"))),
|
|
87
|
+
JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::CropOptions>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "crop"))),
|
|
88
|
+
JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::CompressOptions>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compress"))),
|
|
89
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "maxVideoDuration"))),
|
|
90
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "minVideoDuration"))),
|
|
91
|
+
JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::PickerTheme>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "theme"))),
|
|
92
|
+
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "themeColor"))),
|
|
93
|
+
JSIConverter<std::optional<std::vector<std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "selectedAssets")))
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::margelo::pictureselector::PictureSelectorOptions& arg) {
|
|
97
|
+
jsi::Object obj(runtime);
|
|
98
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "mediaType"), JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::MediaType>>::toJSI(runtime, arg.mediaType));
|
|
99
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "maxCount"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.maxCount));
|
|
100
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "enableCamera"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.enableCamera));
|
|
101
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "crop"), JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::CropOptions>>::toJSI(runtime, arg.crop));
|
|
102
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "compress"), JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::CompressOptions>>::toJSI(runtime, arg.compress));
|
|
103
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "maxVideoDuration"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.maxVideoDuration));
|
|
104
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "minVideoDuration"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.minVideoDuration));
|
|
105
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "theme"), JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::PickerTheme>>::toJSI(runtime, arg.theme));
|
|
106
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "themeColor"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.themeColor));
|
|
107
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "selectedAssets"), JSIConverter<std::optional<std::vector<std::string>>>::toJSI(runtime, arg.selectedAssets));
|
|
108
|
+
return obj;
|
|
109
|
+
}
|
|
110
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
111
|
+
if (!value.isObject()) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
jsi::Object obj = value.getObject(runtime);
|
|
115
|
+
if (!nitro::isPlainObject(runtime, obj)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (!JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::MediaType>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mediaType")))) return false;
|
|
119
|
+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "maxCount")))) return false;
|
|
120
|
+
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "enableCamera")))) return false;
|
|
121
|
+
if (!JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::CropOptions>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "crop")))) return false;
|
|
122
|
+
if (!JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::CompressOptions>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compress")))) return false;
|
|
123
|
+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "maxVideoDuration")))) return false;
|
|
124
|
+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "minVideoDuration")))) return false;
|
|
125
|
+
if (!JSIConverter<std::optional<margelo::nitro::margelo::pictureselector::PickerTheme>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "theme")))) return false;
|
|
126
|
+
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "themeColor")))) return false;
|
|
127
|
+
if (!JSIConverter<std::optional<std::vector<std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "selectedAssets")))) return false;
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
} // namespace margelo::nitro
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-picture-selector",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High-performance photo/video picker for React Native using Nitro Modules",
|
|
5
|
+
"main": "lib/commonjs/index.js",
|
|
6
|
+
"module": "lib/module/index.js",
|
|
7
|
+
"types": "lib/typescript/index.d.ts",
|
|
8
|
+
"react-native": "src/index.ts",
|
|
9
|
+
"source": "src/index.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"lib",
|
|
13
|
+
"android",
|
|
14
|
+
"ios",
|
|
15
|
+
"nitrogen",
|
|
16
|
+
"nitro.json",
|
|
17
|
+
"*.podspec"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"generate": "nitrogen src/specs",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"build": "bob build",
|
|
23
|
+
"prepack": "bob build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"react-native",
|
|
27
|
+
"photo",
|
|
28
|
+
"video",
|
|
29
|
+
"picker",
|
|
30
|
+
"nitro",
|
|
31
|
+
"ios",
|
|
32
|
+
"android",
|
|
33
|
+
"camera",
|
|
34
|
+
"gallery"
|
|
35
|
+
],
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/yourorg/react-native-picture-selector.git"
|
|
39
|
+
},
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": "*",
|
|
43
|
+
"react-native": "*",
|
|
44
|
+
"react-native-nitro-modules": ">=0.20.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"react-native-nitro-modules": {
|
|
48
|
+
"optional": false
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/react": "^19.0.0",
|
|
53
|
+
"nitrogen": "latest",
|
|
54
|
+
"react-native-builder-bob": "^0.30.0",
|
|
55
|
+
"react-native-nitro-modules": ">=0.20.0",
|
|
56
|
+
"typescript": "^5.3.0"
|
|
57
|
+
},
|
|
58
|
+
"nitro": {
|
|
59
|
+
"ios": {
|
|
60
|
+
"iosModuleName": "NitroPictureSelector"
|
|
61
|
+
},
|
|
62
|
+
"android": {
|
|
63
|
+
"androidNamespace": "com.nitro.pictureselector",
|
|
64
|
+
"androidCxxLibName": "NitroPictureSelector"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"react-native-builder-bob": {
|
|
68
|
+
"source": "src",
|
|
69
|
+
"output": "lib",
|
|
70
|
+
"targets": [
|
|
71
|
+
"commonjs",
|
|
72
|
+
"module",
|
|
73
|
+
"typescript"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { NitroModules } from 'react-native-nitro-modules'
|
|
2
|
+
import type { HybridPictureSelector } from './specs/PictureSelector.nitro'
|
|
3
|
+
import type { MediaAsset, PictureSelectorOptions } from './types'
|
|
4
|
+
import { MediaType, toPickerError } from './types'
|
|
5
|
+
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
// Lazy singleton — created once and reused across calls
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
let _native: HybridPictureSelector | null = null
|
|
11
|
+
|
|
12
|
+
function getNative(): HybridPictureSelector {
|
|
13
|
+
if (_native == null) {
|
|
14
|
+
_native = NitroModules.createHybridObject<HybridPictureSelector>(
|
|
15
|
+
'PictureSelector'
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
return _native
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Default options
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
const defaultOptions: PictureSelectorOptions = {
|
|
26
|
+
mediaType: MediaType.IMAGE,
|
|
27
|
+
maxCount: 1,
|
|
28
|
+
enableCamera: true,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Static API
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
export const PictureSelector = {
|
|
36
|
+
/**
|
|
37
|
+
* Open the gallery picker.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* const assets = await PictureSelector.openPicker({ maxCount: 9 })
|
|
41
|
+
*
|
|
42
|
+
* @throws PickerError with code CANCELLED when the user dismisses
|
|
43
|
+
* @throws PickerError with code PERMISSION_DENIED on permission failure
|
|
44
|
+
*/
|
|
45
|
+
async openPicker(
|
|
46
|
+
options: PictureSelectorOptions = {}
|
|
47
|
+
): Promise<MediaAsset[]> {
|
|
48
|
+
try {
|
|
49
|
+
return await getNative().openPicker({ ...defaultOptions, ...options })
|
|
50
|
+
} catch (err) {
|
|
51
|
+
throw toPickerError(err)
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Open the camera for capture.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const [asset] = await PictureSelector.openCamera({ mediaType: MediaType.VIDEO })
|
|
60
|
+
*
|
|
61
|
+
* @throws PickerError with code CANCELLED when the user dismisses
|
|
62
|
+
*/
|
|
63
|
+
async openCamera(
|
|
64
|
+
options: PictureSelectorOptions = {}
|
|
65
|
+
): Promise<MediaAsset[]> {
|
|
66
|
+
try {
|
|
67
|
+
return await getNative().openCamera({ ...defaultOptions, ...options })
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw toPickerError(err)
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { PictureSelector } from './PictureSelector'
|
|
2
|
+
export { usePictureSelector } from './usePictureSelector'
|
|
3
|
+
export type {
|
|
4
|
+
PictureSelectorState,
|
|
5
|
+
PictureSelectorActions,
|
|
6
|
+
UsePictureSelectorReturn,
|
|
7
|
+
} from './usePictureSelector'
|
|
8
|
+
export type {
|
|
9
|
+
MediaAsset,
|
|
10
|
+
PictureSelectorOptions,
|
|
11
|
+
CropOptions,
|
|
12
|
+
CompressOptions,
|
|
13
|
+
PickerResult,
|
|
14
|
+
PickerError,
|
|
15
|
+
} from './types'
|
|
16
|
+
export { MediaType, PickerTheme, toPickerError } from './types'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules'
|
|
2
|
+
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Enums
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export enum MediaType {
|
|
8
|
+
IMAGE = 'image',
|
|
9
|
+
VIDEO = 'video',
|
|
10
|
+
ALL = 'all',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export enum PickerTheme {
|
|
14
|
+
DEFAULT = 'default',
|
|
15
|
+
WECHAT = 'wechat',
|
|
16
|
+
WHITE = 'white',
|
|
17
|
+
DARK = 'dark',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Config sub-structures
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export interface CropOptions {
|
|
25
|
+
/** Enable cropping after selection */
|
|
26
|
+
enabled: boolean
|
|
27
|
+
/** Allow free-form aspect ratio. Default: false */
|
|
28
|
+
freeStyle?: boolean
|
|
29
|
+
/** Circular crop mask. iOS-only. Default: false */
|
|
30
|
+
circular?: boolean
|
|
31
|
+
/** Width part of the crop aspect ratio. Default: 1 */
|
|
32
|
+
ratioX?: number
|
|
33
|
+
/** Height part of the crop aspect ratio. Default: 1 */
|
|
34
|
+
ratioY?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CompressOptions {
|
|
38
|
+
/** Enable compression */
|
|
39
|
+
enabled: boolean
|
|
40
|
+
/** JPEG quality, 0.0–1.0. Default: 0.8 */
|
|
41
|
+
quality?: number
|
|
42
|
+
/** Max output width in pixels. Default: 1920 */
|
|
43
|
+
maxWidth?: number
|
|
44
|
+
/** Max output height in pixels. Default: 1920 */
|
|
45
|
+
maxHeight?: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
// Picker options
|
|
50
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
export interface PictureSelectorOptions {
|
|
53
|
+
/** Media type to display. Default: IMAGE */
|
|
54
|
+
mediaType?: MediaType
|
|
55
|
+
/** Maximum number of selectable items. Default: 1 */
|
|
56
|
+
maxCount?: number
|
|
57
|
+
/** Show camera button inside the picker. Default: true */
|
|
58
|
+
enableCamera?: boolean
|
|
59
|
+
/** Crop configuration. Only applies when maxCount === 1 */
|
|
60
|
+
crop?: CropOptions
|
|
61
|
+
/** Compression configuration */
|
|
62
|
+
compress?: CompressOptions
|
|
63
|
+
/** Max video duration in seconds */
|
|
64
|
+
maxVideoDuration?: number
|
|
65
|
+
/** Min video duration in seconds. Default: 0 */
|
|
66
|
+
minVideoDuration?: number
|
|
67
|
+
/** Picker UI theme */
|
|
68
|
+
theme?: PickerTheme
|
|
69
|
+
/** Accent color as hex string, e.g. "#007AFF". iOS: themeColor; Android: accent */
|
|
70
|
+
themeColor?: string
|
|
71
|
+
/** Pre-selected asset URIs (file:// URIs) */
|
|
72
|
+
selectedAssets?: string[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// Result structure
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export interface MediaAsset {
|
|
80
|
+
/** file:// URI of the final file (compressed or original) */
|
|
81
|
+
uri: string
|
|
82
|
+
/** "image" | "video" */
|
|
83
|
+
type: string
|
|
84
|
+
/** MIME type, e.g. "image/jpeg", "video/mp4" */
|
|
85
|
+
mimeType: string
|
|
86
|
+
/** Width in pixels */
|
|
87
|
+
width: number
|
|
88
|
+
/** Height in pixels */
|
|
89
|
+
height: number
|
|
90
|
+
/** Duration in milliseconds (0 for images) */
|
|
91
|
+
duration: number
|
|
92
|
+
/** Original filename with extension */
|
|
93
|
+
fileName: string
|
|
94
|
+
/** File size in bytes */
|
|
95
|
+
fileSize: number
|
|
96
|
+
/** file:// URI after crop or edit. Undefined if no edit was applied */
|
|
97
|
+
editedUri?: string
|
|
98
|
+
/** iOS: true if user tapped "Original" quality button */
|
|
99
|
+
isOriginal?: boolean
|
|
100
|
+
/** Android: album/bucket name the file belongs to */
|
|
101
|
+
bucketName?: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
// HybridObject — the main bridge
|
|
106
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
export interface HybridPictureSelector
|
|
109
|
+
extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
|
|
110
|
+
/**
|
|
111
|
+
* Open the photo/video gallery picker.
|
|
112
|
+
* Rejects with message "CANCELLED" when the user dismisses without selection.
|
|
113
|
+
*/
|
|
114
|
+
openPicker(options: PictureSelectorOptions): Promise<MediaAsset[]>
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Open the device camera for capture.
|
|
118
|
+
* Rejects with message "CANCELLED" when the user dismisses.
|
|
119
|
+
*/
|
|
120
|
+
openCamera(options: PictureSelectorOptions): Promise<MediaAsset[]>
|
|
121
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Re-export spec types for public consumption
|
|
2
|
+
export type {
|
|
3
|
+
MediaAsset,
|
|
4
|
+
PictureSelectorOptions,
|
|
5
|
+
CropOptions,
|
|
6
|
+
CompressOptions,
|
|
7
|
+
} from './specs/PictureSelector.nitro'
|
|
8
|
+
|
|
9
|
+
export { MediaType, PickerTheme } from './specs/PictureSelector.nitro'
|
|
10
|
+
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
// Convenience aliases
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export type PickerResult = import('./specs/PictureSelector.nitro').MediaAsset[]
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Errors thrown by openPicker / openCamera.
|
|
19
|
+
*
|
|
20
|
+
* code === 'CANCELLED' — user dismissed the picker
|
|
21
|
+
* code === 'PERMISSION_DENIED' — runtime permission not granted
|
|
22
|
+
* code === 'UNKNOWN' — any other native error
|
|
23
|
+
*/
|
|
24
|
+
export interface PickerError extends Error {
|
|
25
|
+
code: 'CANCELLED' | 'PERMISSION_DENIED' | 'UNKNOWN'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Normalise a raw native error to a typed PickerError. */
|
|
29
|
+
export function toPickerError(err: unknown): PickerError {
|
|
30
|
+
const base = err instanceof Error ? err : new Error(String(err))
|
|
31
|
+
const msg = base.message ?? ''
|
|
32
|
+
|
|
33
|
+
let code: PickerError['code'] = 'UNKNOWN'
|
|
34
|
+
if (msg.includes('CANCELLED')) code = 'CANCELLED'
|
|
35
|
+
else if (msg.includes('PERMISSION_DENIED')) code = 'PERMISSION_DENIED'
|
|
36
|
+
|
|
37
|
+
return Object.assign(base, { code }) as PickerError
|
|
38
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useState, useCallback, useRef } from 'react'
|
|
2
|
+
import { PictureSelector } from './PictureSelector'
|
|
3
|
+
import type { MediaAsset, PictureSelectorOptions, PickerError } from './types'
|
|
4
|
+
import { toPickerError } from './types'
|
|
5
|
+
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
// Hook state shape
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export interface PictureSelectorState {
|
|
11
|
+
assets: MediaAsset[]
|
|
12
|
+
loading: boolean
|
|
13
|
+
error: PickerError | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PictureSelectorActions {
|
|
17
|
+
/** Open gallery picker */
|
|
18
|
+
pick: (options?: PictureSelectorOptions) => Promise<MediaAsset[]>
|
|
19
|
+
/** Open camera */
|
|
20
|
+
shoot: (options?: PictureSelectorOptions) => Promise<MediaAsset[]>
|
|
21
|
+
/** Clear selected assets and error state */
|
|
22
|
+
clear: () => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type UsePictureSelectorReturn = PictureSelectorState &
|
|
26
|
+
PictureSelectorActions
|
|
27
|
+
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
29
|
+
// Hook
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* React hook that manages picker state.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const { assets, loading, pick, shoot, clear } = usePictureSelector({ maxCount: 9 })
|
|
37
|
+
*
|
|
38
|
+
* <Button onPress={() => pick()} title="Pick Photos" />
|
|
39
|
+
* {assets.map(a => <Image source={{ uri: a.uri }} />)}
|
|
40
|
+
*/
|
|
41
|
+
export function usePictureSelector(
|
|
42
|
+
defaultOptions?: PictureSelectorOptions
|
|
43
|
+
): UsePictureSelectorReturn {
|
|
44
|
+
const [state, setState] = useState<PictureSelectorState>({
|
|
45
|
+
assets: [],
|
|
46
|
+
loading: false,
|
|
47
|
+
error: null,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Stable ref to avoid re-creating callbacks when defaultOptions changes
|
|
51
|
+
const defaultOptionsRef = useRef(defaultOptions)
|
|
52
|
+
defaultOptionsRef.current = defaultOptions
|
|
53
|
+
|
|
54
|
+
const pick = useCallback(
|
|
55
|
+
async (options?: PictureSelectorOptions): Promise<MediaAsset[]> => {
|
|
56
|
+
setState((s) => ({ ...s, loading: true, error: null }))
|
|
57
|
+
try {
|
|
58
|
+
const merged = { ...defaultOptionsRef.current, ...options }
|
|
59
|
+
const results = await PictureSelector.openPicker(merged)
|
|
60
|
+
setState({ assets: results, loading: false, error: null })
|
|
61
|
+
return results
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const pickerErr = toPickerError(err)
|
|
64
|
+
// Do not surface CANCELLED as an error — just restore loading state
|
|
65
|
+
if (pickerErr.code === 'CANCELLED') {
|
|
66
|
+
setState((s) => ({ ...s, loading: false }))
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
69
|
+
setState((s) => ({ ...s, loading: false, error: pickerErr }))
|
|
70
|
+
throw pickerErr
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
[]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const shoot = useCallback(
|
|
77
|
+
async (options?: PictureSelectorOptions): Promise<MediaAsset[]> => {
|
|
78
|
+
setState((s) => ({ ...s, loading: true, error: null }))
|
|
79
|
+
try {
|
|
80
|
+
const merged = { ...defaultOptionsRef.current, ...options }
|
|
81
|
+
const results = await PictureSelector.openCamera(merged)
|
|
82
|
+
setState({ assets: results, loading: false, error: null })
|
|
83
|
+
return results
|
|
84
|
+
} catch (err) {
|
|
85
|
+
const pickerErr = toPickerError(err)
|
|
86
|
+
if (pickerErr.code === 'CANCELLED') {
|
|
87
|
+
setState((s) => ({ ...s, loading: false }))
|
|
88
|
+
return []
|
|
89
|
+
}
|
|
90
|
+
setState((s) => ({ ...s, loading: false, error: pickerErr }))
|
|
91
|
+
throw pickerErr
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const clear = useCallback(() => {
|
|
98
|
+
setState({ assets: [], loading: false, error: null })
|
|
99
|
+
}, [])
|
|
100
|
+
|
|
101
|
+
return { ...state, pick, shoot, clear }
|
|
102
|
+
}
|