Skip to content

Commit 3dc490d

Browse files
committed
✨ feat: doing camera custom
1 parent bd8b219 commit 3dc490d

File tree

8 files changed

+142
-46
lines changed

8 files changed

+142
-46
lines changed

example/src/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ export default function App() {
9595

9696
const onCamera = async () => {
9797
try {
98-
const response = await openCamera()
98+
const response = await openCamera({
99+
crop: false,
100+
})
99101

100102
console.log('response: ', response)
101103

ios/HybridMultipleImagePicker+Camera.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ extension HybridMultipleImagePicker {
2727
cameraConfig.languageType = setLocale(language: config.language)
2828
cameraConfig.isSaveSystemAlbum = config.isSaveSystemAlbum ?? false
2929
cameraConfig.allowLocation = config.allowLocation ?? true
30+
cameraConfig.sessionPreset = .hd1920x1080
31+
cameraConfig.aspectRatio = .fullScreen
32+
33+
if let color = config.color, let focusColor = getReactColor(Int(color)) {
34+
cameraConfig.focusColor = focusColor
35+
}
3036

3137
switch Int(config.cameraDevice.rawValue) {
3238
case 0:

nitrogen/generated/android/c++/JNitroCameraConfig.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ namespace margelo::nitro::multipleimagepicker {
5656
jni::local_ref<jni::JBoolean> isSaveSystemAlbum = this->getFieldValue(fieldIsSaveSystemAlbum);
5757
static const auto fieldAllowLocation = clazz->getField<jni::JBoolean>("allowLocation");
5858
jni::local_ref<jni::JBoolean> allowLocation = this->getFieldValue(fieldAllowLocation);
59+
static const auto fieldColor = clazz->getField<jni::JDouble>("color");
60+
jni::local_ref<jni::JDouble> color = this->getFieldValue(fieldColor);
5961
static const auto fieldCameraDevice = clazz->getField<JCameraDevice>("cameraDevice");
6062
jni::local_ref<JCameraDevice> cameraDevice = this->getFieldValue(fieldCameraDevice);
6163
static const auto fieldVideoMaximumDuration = clazz->getField<jni::JDouble>("videoMaximumDuration");
@@ -67,6 +69,7 @@ namespace margelo::nitro::multipleimagepicker {
6769
crop != nullptr ? std::make_optional(crop->toCpp()) : std::nullopt,
6870
isSaveSystemAlbum != nullptr ? std::make_optional(static_cast<bool>(isSaveSystemAlbum->value())) : std::nullopt,
6971
allowLocation != nullptr ? std::make_optional(static_cast<bool>(allowLocation->value())) : std::nullopt,
72+
color != nullptr ? std::make_optional(color->value()) : std::nullopt,
7073
cameraDevice->toCpp(),
7174
videoMaximumDuration != nullptr ? std::make_optional(videoMaximumDuration->value()) : std::nullopt
7275
);
@@ -85,6 +88,7 @@ namespace margelo::nitro::multipleimagepicker {
8588
value.crop.has_value() ? JPickerCropConfig::fromCpp(value.crop.value()) : nullptr,
8689
value.isSaveSystemAlbum.has_value() ? jni::JBoolean::valueOf(value.isSaveSystemAlbum.value()) : nullptr,
8790
value.allowLocation.has_value() ? jni::JBoolean::valueOf(value.allowLocation.value()) : nullptr,
91+
value.color.has_value() ? jni::JDouble::valueOf(value.color.value()) : nullptr,
8892
JCameraDevice::fromCpp(value.cameraDevice),
8993
value.videoMaximumDuration.has_value() ? jni::JDouble::valueOf(value.videoMaximumDuration.value()) : nullptr
9094
);

nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroCameraConfig.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ data class NitroCameraConfig(
2323
val crop: PickerCropConfig?,
2424
val isSaveSystemAlbum: Boolean?,
2525
val allowLocation: Boolean?,
26+
val color: Double?,
2627
val cameraDevice: CameraDevice,
2728
val videoMaximumDuration: Double?
2829
)

nitrogen/generated/ios/swift/NitroCameraConfig.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public extension NitroCameraConfig {
1818
/**
1919
* Create a new instance of `NitroCameraConfig`.
2020
*/
21-
init(mediaType: MediaType, presentation: Presentation, language: Language, crop: PickerCropConfig?, isSaveSystemAlbum: Bool?, allowLocation: Bool?, cameraDevice: CameraDevice, videoMaximumDuration: Double?) {
21+
init(mediaType: MediaType, presentation: Presentation, language: Language, crop: PickerCropConfig?, isSaveSystemAlbum: Bool?, allowLocation: Bool?, color: Double?, cameraDevice: CameraDevice, videoMaximumDuration: Double?) {
2222
self.init(mediaType, presentation, language, { () -> bridge.std__optional_PickerCropConfig_ in
2323
if let __unwrappedValue = crop {
2424
return bridge.create_std__optional_PickerCropConfig_(__unwrappedValue)
@@ -37,6 +37,12 @@ public extension NitroCameraConfig {
3737
} else {
3838
return .init()
3939
}
40+
}(), { () -> bridge.std__optional_double_ in
41+
if let __unwrappedValue = color {
42+
return bridge.create_std__optional_double_(__unwrappedValue)
43+
} else {
44+
return .init()
45+
}
4046
}(), cameraDevice, { () -> bridge.std__optional_double_ in
4147
if let __unwrappedValue = videoMaximumDuration {
4248
return bridge.create_std__optional_double_(__unwrappedValue)
@@ -136,6 +142,23 @@ public extension NitroCameraConfig {
136142
}
137143
}
138144

145+
var color: Double? {
146+
@inline(__always)
147+
get {
148+
return self.__color.value
149+
}
150+
@inline(__always)
151+
set {
152+
self.__color = { () -> bridge.std__optional_double_ in
153+
if let __unwrappedValue = newValue {
154+
return bridge.create_std__optional_double_(__unwrappedValue)
155+
} else {
156+
return .init()
157+
}
158+
}()
159+
}
160+
}
161+
139162
var cameraDevice: CameraDevice {
140163
@inline(__always)
141164
get {

nitrogen/generated/shared/c++/NitroCameraConfig.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ namespace margelo::nitro::multipleimagepicker {
4949
std::optional<PickerCropConfig> crop SWIFT_PRIVATE;
5050
std::optional<bool> isSaveSystemAlbum SWIFT_PRIVATE;
5151
std::optional<bool> allowLocation SWIFT_PRIVATE;
52+
std::optional<double> color SWIFT_PRIVATE;
5253
CameraDevice cameraDevice SWIFT_PRIVATE;
5354
std::optional<double> videoMaximumDuration SWIFT_PRIVATE;
5455

5556
public:
56-
explicit NitroCameraConfig(MediaType mediaType, Presentation presentation, Language language, std::optional<PickerCropConfig> crop, std::optional<bool> isSaveSystemAlbum, std::optional<bool> allowLocation, CameraDevice cameraDevice, std::optional<double> videoMaximumDuration): mediaType(mediaType), presentation(presentation), language(language), crop(crop), isSaveSystemAlbum(isSaveSystemAlbum), allowLocation(allowLocation), cameraDevice(cameraDevice), videoMaximumDuration(videoMaximumDuration) {}
57+
explicit NitroCameraConfig(MediaType mediaType, Presentation presentation, Language language, std::optional<PickerCropConfig> crop, std::optional<bool> isSaveSystemAlbum, std::optional<bool> allowLocation, std::optional<double> color, CameraDevice cameraDevice, std::optional<double> videoMaximumDuration): mediaType(mediaType), presentation(presentation), language(language), crop(crop), isSaveSystemAlbum(isSaveSystemAlbum), allowLocation(allowLocation), color(color), cameraDevice(cameraDevice), videoMaximumDuration(videoMaximumDuration) {}
5758
};
5859

5960
} // namespace margelo::nitro::multipleimagepicker
@@ -74,6 +75,7 @@ namespace margelo::nitro {
7475
JSIConverter<std::optional<PickerCropConfig>>::fromJSI(runtime, obj.getProperty(runtime, "crop")),
7576
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, "isSaveSystemAlbum")),
7677
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, "allowLocation")),
78+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, "color")),
7779
JSIConverter<CameraDevice>::fromJSI(runtime, obj.getProperty(runtime, "cameraDevice")),
7880
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, "videoMaximumDuration"))
7981
);
@@ -86,6 +88,7 @@ namespace margelo::nitro {
8688
obj.setProperty(runtime, "crop", JSIConverter<std::optional<PickerCropConfig>>::toJSI(runtime, arg.crop));
8789
obj.setProperty(runtime, "isSaveSystemAlbum", JSIConverter<std::optional<bool>>::toJSI(runtime, arg.isSaveSystemAlbum));
8890
obj.setProperty(runtime, "allowLocation", JSIConverter<std::optional<bool>>::toJSI(runtime, arg.allowLocation));
91+
obj.setProperty(runtime, "color", JSIConverter<std::optional<double>>::toJSI(runtime, arg.color));
8992
obj.setProperty(runtime, "cameraDevice", JSIConverter<CameraDevice>::toJSI(runtime, arg.cameraDevice));
9093
obj.setProperty(runtime, "videoMaximumDuration", JSIConverter<std::optional<double>>::toJSI(runtime, arg.videoMaximumDuration));
9194
return obj;
@@ -101,6 +104,7 @@ namespace margelo::nitro {
101104
if (!JSIConverter<std::optional<PickerCropConfig>>::canConvert(runtime, obj.getProperty(runtime, "crop"))) return false;
102105
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, "isSaveSystemAlbum"))) return false;
103106
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, "allowLocation"))) return false;
107+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, "color"))) return false;
104108
if (!JSIConverter<CameraDevice>::canConvert(runtime, obj.getProperty(runtime, "cameraDevice"))) return false;
105109
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, "videoMaximumDuration"))) return false;
106110
return true;

src/index.ts

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import {
1414
CropResult,
1515
CropConfig,
1616
NitroCropConfig,
17-
CropRatio,
1817
PreviewConfig,
1918
NitroPreviewConfig,
2019
MediaPreview,
2120
CameraConfig,
2221
NitroCameraConfig,
2322
CameraResult,
23+
Language,
2424
} from './types'
2525
import { CropError } from './types/error'
2626

@@ -45,11 +45,13 @@ export async function openPicker<T extends Config>(
4545
config.theme = theme
4646
}
4747

48-
if (config?.language && !LANGUAGES.includes(config.language)) {
49-
config.language = 'system'
48+
config.language = validateLanguage(config.language)
49+
50+
if (typeof config.crop === 'boolean') {
51+
config.crop = config.crop ? { ratio: [] } : undefined
5052
}
5153

52-
if (config.crop) config.crop.ratio = config.crop.ratio ?? []
54+
if (config.crop) config.crop.ratio = config.crop?.ratio ?? []
5355

5456
return Picker.openPicker(
5557
config,
@@ -75,9 +77,7 @@ export async function openCropper(
7577
...config,
7678
} as NitroCropConfig
7779

78-
if (config?.language && !LANGUAGES.includes(config.language)) {
79-
config.language = 'system'
80-
}
80+
cropConfig.language = validateLanguage(cropConfig.language)
8181

8282
return Picker.openCrop(
8383
image,
@@ -126,16 +126,18 @@ export async function openCamera(config?: CameraConfig): Promise<CameraResult> {
126126
mediaType: 'all',
127127
allowLocation: true,
128128
isSaveSystemAlbum: false,
129-
crop: {},
129+
color: processColor(config?.color ?? primaryColor) as any,
130130
...config,
131131
} as NitroCameraConfig
132132

133-
if (config?.language && !LANGUAGES.includes(config.language)) {
134-
config.language = 'system'
133+
cameraConfig.language = validateLanguage(cameraConfig.language)
134+
135+
if (typeof cameraConfig.crop === 'boolean') {
136+
cameraConfig.crop = cameraConfig.crop ? { ratio: [] } : undefined
135137
}
136138

137-
if (cameraConfig.crop)
138-
cameraConfig.crop.ratio = cameraConfig.crop?.ratio ?? []
139+
if (cameraConfig.crop && !cameraConfig.crop?.ratio)
140+
cameraConfig.crop.ratio = []
139141

140142
return Picker.openCamera(
141143
cameraConfig,
@@ -151,12 +153,37 @@ export async function openCamera(config?: CameraConfig): Promise<CameraResult> {
151153

152154
const DEFAULT_COUNT = 20
153155

154-
export const DEFAULT_RATIO: CropRatio[] = []
156+
// Xử lý language với type checking
157+
const validateLanguage = (language?: Language): Language => {
158+
if (!language || !LANGUAGES.includes(language)) {
159+
return 'system'
160+
}
161+
return language
162+
}
163+
164+
const processCropConfig = (
165+
crop?: boolean | CropConfig
166+
): CropConfig | undefined => {
167+
if (typeof crop === 'boolean') {
168+
return crop ? { ratio: [] } : undefined
169+
}
170+
171+
if (crop && typeof crop === 'object') {
172+
return {
173+
...crop,
174+
ratio: crop.ratio || [], // Changed from ?? to || to ensure array
175+
}
176+
}
177+
178+
return undefined
179+
}
180+
181+
const primaryColor = '#FB9300'
155182

156183
export const defaultOptions: Config = {
157184
maxSelect: DEFAULT_COUNT,
158185
maxVideo: DEFAULT_COUNT,
159-
primaryColor: '#FB9300',
186+
primaryColor,
160187
backgroundDark: '#2f2f2f',
161188
allowedLimit: true,
162189
numberOfColumn: 3,

src/types/camera.ts

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ColorValue } from 'react-native'
12
import { CropRatio, Language, MediaType, Presentation } from './config'
23
import { PickerCropConfig } from './crop'
34

@@ -37,12 +38,19 @@ export interface NitroCameraConfig extends PickerCameraConfig {
3738
* @type {boolean}
3839
*/
3940
allowLocation?: boolean
41+
42+
color?: number
4043
}
4144

4245
export interface CameraConfig
4346
extends Omit<
4447
NitroCameraConfig,
45-
'cameraDevice' | 'mediaType' | 'language' | 'presentation' | 'crop'
48+
| 'cameraDevice'
49+
| 'mediaType'
50+
| 'language'
51+
| 'presentation'
52+
| 'crop'
53+
| 'color'
4654
> {
4755
/**
4856
* Type of camera
@@ -90,34 +98,55 @@ export interface CameraConfig
9098
*/
9199
language?: Language
92100

93-
crop?: Omit<PickerCropConfig, 'ratio'> & {
94-
/**
95-
* Array of aspect ratios for image cropping. The ratios will be inserted after the default ratios (Original and Square).
96-
* Android: Maximum: 4 items
97-
*
98-
* @platform ios, Android
99-
*
100-
* @property {Array<CropRatio>} ratio - Array of custom aspect ratios
101-
* @property {string} [ratio[].title] - Optional display title for the ratio (e.g., "16:9"). If not provided, will use "width/height"
102-
* @property {number} ratio[].width - Width value for aspect ratio
103-
* @property {number} ratio[].height - Height value for aspect ratio
104-
*
105-
* @example
106-
* ```ts
107-
* ratio: [
108-
* { title: "Instagram", width: 1, height: 1 },
109-
* { title: "Twitter", width: 16, height: 9 },
110-
* { width: 12, height: 11 }
111-
* ]
112-
* ```
113-
*/
114-
ratio?: CropRatio[]
115-
116-
/**
117-
* Camera configuration
118-
* @type {CameraConfig}
119-
*/
120-
}
101+
crop?:
102+
| boolean
103+
| (Omit<PickerCropConfig, 'ratio'> & {
104+
/**
105+
* Array of aspect ratios for image cropping. The ratios will be inserted after the default ratios (Original and Square).
106+
* Android: Maximum: 4 items
107+
*
108+
* @platform ios, Android
109+
*
110+
* @property {Array<CropRatio>} ratio - Array of custom aspect ratios
111+
* @property {string} [ratio[].title] - Optional display title for the ratio (e.g., "16:9"). If not provided, will use "width/height"
112+
* @property {number} ratio[].width - Width value for aspect ratio
113+
* @property {number} ratio[].height - Height value for aspect ratio
114+
*
115+
* @example
116+
* ```ts
117+
* ratio: [
118+
* { title: "Instagram", width: 1, height: 1 },
119+
* { title: "Twitter", width: 16, height: 9 },
120+
* { width: 12, height: 11 }
121+
* ]
122+
* ```
123+
*/
124+
ratio?: CropRatio[]
125+
126+
/**
127+
* Camera configuration
128+
* @type {CameraConfig}
129+
*/
130+
})
131+
132+
/**
133+
* Primary color for the picker UI elements.
134+
* Accepts various color formats:
135+
* - Hex strings: '#RGB', '#RGBA', '#RRGGBB', '#RRGGBBAA'
136+
* - RGB/RGBA strings: 'rgb(255, 0, 0)', 'rgba(255, 0, 0, 0.5)'
137+
* - Named colors: 'red', 'blue', etc.
138+
* - Numbers for RGB values
139+
*
140+
* @platform ios, android
141+
* @type {ColorValue}
142+
* @example
143+
* ```ts
144+
* primaryColor: '#FF0000'
145+
* primaryColor: 'rgb(255, 0, 0)'
146+
* primaryColor: 'red'
147+
* ```
148+
*/
149+
color?: ColorValue
121150
}
122151

123152
export interface CameraResult {

0 commit comments

Comments
 (0)