diff --git a/android/src/main/java/com/margelo/nitro/multipleimagepicker/CropEngine.kt b/android/src/main/java/com/margelo/nitro/multipleimagepicker/CropEngine.kt index fa0da9b0..1aff0e71 100644 --- a/android/src/main/java/com/margelo/nitro/multipleimagepicker/CropEngine.kt +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/CropEngine.kt @@ -51,8 +51,9 @@ class CropImageEngine : UCropImageEngine { } } -class CropEngine(cropOption: UCrop.Options) : CropFileEngine { - private val options: UCrop.Options = cropOption +class CropEngine(cropOption: Options) : CropFileEngine { + private val options: Options = cropOption + override fun onStartCrop( fragment: Fragment, srcUri: Uri?, @@ -78,6 +79,7 @@ class MediaEditInterceptListener( val inputUri = if (PictureMimeType.isContent(currentEditPath)) Uri.parse(currentEditPath) else Uri.fromFile(File(currentEditPath)) + val destinationUri = Uri.fromFile( File(outputCropPath, DateUtils.getCreateFileName("CROP_") + ".jpeg") ) diff --git a/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt b/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt index 2d92b068..84c2be77 100644 --- a/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt @@ -1,12 +1,9 @@ package com.margelo.nitro.multipleimagepicker import com.margelo.nitro.NitroModules -import com.margelo.nitro.multipleimagepicker.HybridMultipleImagePickerSpec -import com.margelo.nitro.multipleimagepicker.NitroConfig -import com.margelo.nitro.multipleimagepicker.Result -class MultipleImagePicker: HybridMultipleImagePickerSpec() { +class MultipleImagePicker : HybridMultipleImagePickerSpec() { override val memorySize: Long get() = 5 @@ -20,7 +17,15 @@ class MultipleImagePicker: HybridMultipleImagePickerSpec() { pickerModule.openPicker(config, resolved, rejected) } + override fun openCrop( + image: String, + config: NitroCropConfig, + resolved: (result: CropResult) -> Unit, + rejected: (reject: Double) -> Unit + ) { + pickerModule.openCrop(image, config, resolved, rejected) + } } \ No newline at end of file diff --git a/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt b/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt index e9006bb2..fd8d5b83 100644 --- a/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt @@ -1,8 +1,14 @@ package com.margelo.nitro.multipleimagepicker +import android.app.Activity +import android.content.ContentResolver import android.content.Context +import android.content.Intent import android.graphics.Color +import android.net.Uri import androidx.core.content.ContextCompat +import com.facebook.react.bridge.ActivityEventListener +import com.facebook.react.bridge.BaseActivityEventListener import com.facebook.react.bridge.ColorPropConverter import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule @@ -23,8 +29,16 @@ import com.luck.picture.lib.style.PictureSelectorStyle import com.luck.picture.lib.style.PictureWindowAnimationStyle import com.luck.picture.lib.style.SelectMainStyle import com.luck.picture.lib.style.TitleBarStyle +import com.luck.picture.lib.utils.DateUtils import com.luck.picture.lib.utils.DensityUtil +import com.yalantis.ucrop.UCrop import com.yalantis.ucrop.UCrop.Options +import com.yalantis.ucrop.UCrop.REQUEST_CROP +import com.yalantis.ucrop.model.AspectRatio +import java.io.File +import java.net.HttpURLConnection +import java.net.URL + class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : ReactContextBaseJavaModule(reactContext), IApp { @@ -65,7 +79,6 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : else -> SelectMimeType.ofAll() } - val maxSelect = config.maxSelect?.toInt() ?: 20 val maxVideo = config.maxVideo?.toInt() ?: 20 val isPreview = config.isPreview ?: true @@ -82,13 +95,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : val isCrop = config.crop != null - PictureSelector.create(activity) - .openGallery(chooseMode) - .setImageEngine(imageEngine) - .setSelectedData(dataList) - .setSelectorUIStyle(style).apply { + PictureSelector.create(activity).openGallery(chooseMode).setImageEngine(imageEngine) + .setSelectedData(dataList).setSelectorUIStyle(style).apply { if (isCrop) { - setCropOption() + setCropOption(config.crop) // Disabled force crop engine for multiple if (!isMultiple) setCropEngine(CropEngine(cropOption)) else setEditMediaInterceptListener(setEditMediaEvent()) @@ -113,28 +123,18 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : if (videoQuality != null && videoQuality != 1.0) { setVideoQuality(if (videoQuality > 0.5) 1 else 0) } - } - .setImageSpanCount(config.numberOfColumn?.toInt() ?: 3) - .setMaxSelectNum(maxSelect) - .isDirectReturnSingle(true) - .isSelectZoomAnim(true) - .isPageStrategy(true, 50) + }.setImageSpanCount(config.numberOfColumn?.toInt() ?: 3).setMaxSelectNum(maxSelect) + .isDirectReturnSingle(true).isSelectZoomAnim(true).isPageStrategy(true, 50) .isWithSelectVideoImage(true) .setMaxVideoSelectNum(if (maxVideo != 20) maxVideo else maxSelect) - .isMaxSelectEnabledMask(true) - .isAutoVideoPlay(true) - .isFastSlidingSelect(allowSwipeToSelect) - .isPageSyncAlbumCount(true) + .isMaxSelectEnabledMask(true).isAutoVideoPlay(true) + .isFastSlidingSelect(allowSwipeToSelect).isPageSyncAlbumCount(true) // isPreview - .isPreviewImage(isPreview) - .isPreviewVideo(isPreview) + .isPreviewImage(isPreview).isPreviewVideo(isPreview) // - .isDisplayCamera(config.allowedCamera ?: true) - .isDisplayTimeAxis(true) - .setSelectionMode(selectMode) - .isOriginalControl(config.isHiddenOriginalButton == false) - .setLanguage(getLanguage()) - .isPreviewFullScreenMode(true) + .isDisplayCamera(config.allowedCamera ?: true).isDisplayTimeAxis(true) + .setSelectionMode(selectMode).isOriginalControl(config.isHiddenOriginalButton == false) + .setLanguage(getLanguage()).isPreviewFullScreenMode(true) .forResult(object : OnResultCallbackListener { override fun onResult(localMedia: ArrayList?) { var data: Array = arrayOf() @@ -161,6 +161,112 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : }) } + @ReactMethod + fun openCrop( + image: String, + options: NitroCropConfig, + resolved: (result: CropResult) -> Unit, + rejected: (reject: Double) -> Unit + ) { + + + fun isImage(uri: Uri, contentResolver: ContentResolver): Boolean { + val mimeType: String? = contentResolver.getType(uri) + return mimeType?.startsWith("image/") == true + } + + val uri = Uri.parse(image) + val isImageFile = isImage(uri, appContext.contentResolver) + + if (!isImageFile) return rejected(0.0) + + cropOption = Options() + + setCropOption( + PickerCropConfig( + circle = options.circle, + ratio = options.ratio, + defaultRatio = options.defaultRatio, + freeStyle = options.freeStyle + ) + ) + + try { + val uri = when { + // image network + image.startsWith("http://") || image.startsWith("https://") -> { + // Handle remote URL + val url = URL(image) + val connection = url.openConnection() as HttpURLConnection + connection.doInput = true + connection.connect() + + val inputStream = connection.inputStream + // Create a temp file to store the image + val file = File(appContext.cacheDir, "CROP_") + file.outputStream().use { output -> + inputStream.copyTo(output) + } + + Uri.fromFile(file) + } + + + else -> { + Uri.parse(image) + } + } + + + val destinationUri = Uri.fromFile( + File(getSandboxPath(appContext), DateUtils.getCreateFileName("CROP_") + ".jpeg") + ) + + val uCrop = UCrop.of(uri, destinationUri).withOptions(cropOption) + + // set engine + uCrop.setImageEngine(CropImageEngine()) + // start edit + + val cropActivityEventListener = object : BaseActivityEventListener() { + override fun onActivityResult( + activity: Activity, + requestCode: Int, + resultCode: Int, + data: Intent? + ) { + if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CROP) { + val resultUri = UCrop.getOutput(data!!) + val width = UCrop.getOutputImageWidth(data).toDouble() + val height = UCrop.getOutputImageHeight(data).toDouble() + + resultUri?.let { + val result = CropResult( + path = it.toString(), + width, + height, + ) + resolved(result) + } + } else if (resultCode == UCrop.RESULT_ERROR) { + val cropError = UCrop.getError(data!!) + rejected(0.0) + } + + // Remove listener after getting result + reactApplicationContext.removeActivityEventListener(this) + } + } + + // Add listener before starting UCrop + reactApplicationContext.addActivityEventListener(cropActivityEventListener) + + currentActivity?.let { uCrop.start(it, REQUEST_CROP) } + } catch (e: Exception) { + rejected(0.0) + } + } + private fun getLanguage(): Int { return when (config.language) { Language.VI -> LanguageConfig.VIETNAM // -> 🇻🇳 My country. Yeahhh @@ -177,12 +283,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : } } - private fun setCropOption() { -// val mainStyle: SelectMainStyle = style.selectMainStyle - + private fun setCropOption(config: PickerCropConfig?) { cropOption.setShowCropFrame(true) cropOption.setShowCropGrid(true) - cropOption.setCircleDimmedLayer(config.crop?.circle ?: false) + cropOption.setCircleDimmedLayer(config?.circle ?: false) cropOption.setCropOutputPathDir(getSandboxPath(appContext)) cropOption.isCropDragSmoothToCenter(true) cropOption.isForbidSkipMultipleCrop(true) @@ -191,8 +295,48 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : cropOption.setStatusBarColor(Color.WHITE) cropOption.isDarkStatusBarBlack(true) cropOption.isDragCropImages(true) - cropOption.setFreeStyleCropEnabled(true) + cropOption.setFreeStyleCropEnabled(config?.freeStyle ?: true) cropOption.setSkipCropMimeType(*getNotSupportCrop()) + + + val ratioCount = config?.ratio?.size ?: 0 + + if (config?.defaultRatio != null || ratioCount > 0) { + + var ratioList = arrayOf(AspectRatio("Original", 0f, 0f)) + + if (ratioCount > 0) { + config?.ratio?.take(4)?.toTypedArray()?.forEach { item -> + ratioList += AspectRatio( + item.title, item.width.toFloat(), item.height.toFloat() + ) + } + } + + // Add default Aspects + ratioList += arrayOf( + AspectRatio(null, 1f, 1f), + AspectRatio(null, 16f, 9f), + AspectRatio(null, 4f, 3f), + AspectRatio(null, 3f, 2f) + ) + + config?.defaultRatio?.let { + val defaultRatio = AspectRatio(it.title, it.width.toFloat(), it.height.toFloat()) + ratioList = arrayOf(defaultRatio) + ratioList + + } + + cropOption.apply { + + setAspectRatioOptions( + 0, + *ratioList.take(5).toTypedArray() + ) + + } + + } } diff --git a/docs/docs/CONFIG.mdx b/docs/docs/CONFIG.mdx index f02ca667..76248f35 100644 --- a/docs/docs/CONFIG.mdx +++ b/docs/docs/CONFIG.mdx @@ -81,7 +81,7 @@ Maximum number of videos allowed. - **Required**: No - **Platform**: iOS, Android -### `crop` +## Crop 🪚 Configuration for image cropping functionality. @@ -89,8 +89,51 @@ Configuration for image cropping functionality. - **Default**: `undefined` - **Required**: No - **Platform**: iOS, Android + +### `circle` + +Enable circular crop mask. + +- **Type**: boolean +- **Default**: `false` +- **Required**: No +- **Platform**: iOS, Android + +### `ratio` + +Aspect ratios for cropping. +Android: Maximum: 4 items + +- **Type**: `array` +- **Default**: `undefined` +- **Required**: No +- **Platform**: iOS, Android - **Properties**: - - `circle`: boolean - Enable circular crop mask + - `title`: string - Display title for the ratio (e.g., "Square", "16:9") + - `width`: number - Width value for the aspect ratio + - `height`: number - Height value for the aspect ratio + +### `defaultRatio` + +Default ratio to be selected when opening the crop interface. + +- **Type**: `object` +- **Default**: `undefined` +- **Required**: No +- **Platform**: iOS, Android +- **Properties**: + - `title`: string - Display title for the ratio (e.g., "Square", "16:9") + - `width`: number - Width value for the aspect ratio + - `height`: number - Height value for the aspect ratio + +### `freeStyle` + +Enable free style cropping. + +- **Type**: `boolean` +- **Default**: `false` +- **Required**: No +- **Platform**: iOS, Android --- diff --git a/docs/docs/CROP.mdx b/docs/docs/CROP.mdx new file mode 100644 index 00000000..42ba98f6 --- /dev/null +++ b/docs/docs/CROP.mdx @@ -0,0 +1,95 @@ +--- +id: crop +title: Open Crop +sidebar_label: Open Crop +slug: /crop +--- + +## Usage + +```typescript +import { openCropper } from '@baronha/react-native-multiple-image-picker' + +const cropConfig: CropConfig = { + // ... +} + +const open = async () => { + try { + const response = await openCropper('file://path/to/image.jpg', cropConfig) + setImages(response) + } catch (e) { + // catch error for multiple image picker + } +} +``` + +## CropConfig + +### `circle` + +Enable circular crop mask. + +- **Type**: boolean +- **Default**: `false` +- **Required**: No +- **Platform**: iOS, Android + +### `ratio` + +Aspect ratios for cropping. +Android: Maximum: 4 items + +- **Type**: `array` +- **Default**: `undefined` +- **Required**: No +- **Platform**: iOS, Android +- **Properties**: + - `title`: string - Display title for the ratio (e.g., "Square", "16:9") + - `width`: number - Width value for the aspect ratio + - `height`: number - Height value for the aspect ratio + +### `defaultRatio` + +Default ratio to be selected when opening the crop interface. + +- **Type**: `object` +- **Default**: `undefined` +- **Required**: No +- **Platform**: iOS, Android +- **Properties**: + - `title`: string - Display title for the ratio (e.g., "Square", "16:9") + - `width`: number - Width value for the aspect ratio + - `height`: number - Height value for the aspect ratio + +### `freeStyle` + +Enable free style cropping. + +- **Type**: `boolean` +- **Default**: `false` +- **Required**: No +- **Platform**: iOS, Android + +### `language` + +- **Type**: `string` +- **Default**: `false` +- **Required**: No +- **Platform**: iOS + +See [**Language**](/config/#language) + +## Result + +### `path` + +- **Type**: `string` + +### `width` + +- **Type**: `number` + +### `height` + +- **Type**: `number` diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 2df55dd3..9746608a 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -23,6 +23,8 @@ const sidebars: SidebarsConfig = { }, items: ['getting-started', 'usage', 'config', 'result'], }, + + 'crop', ], } diff --git a/example/package.json b/example/package.json index 2ee99933..05210b20 100644 --- a/example/package.json +++ b/example/package.json @@ -14,7 +14,6 @@ }, "dependencies": { "@baronha/react-native-image-grid": "^0.2.7", - "@gorhom/bottom-sheet": "^5.0.6", "@react-native-segmented-control/segmented-control": "2.5.2", "expo": "~51.0.38", "expo-build-properties": "^0.12.5", @@ -23,9 +22,6 @@ "immer": "^10.1.1", "react": "18.2.0", "react-native": "0.75.0", - "react-native-gesture-handler": "~2.16.1", - "react-native-reanimated": "~3.10.1", - "reanimated-color-picker": "^3.0.6", "use-immer": "^0.10.0" }, "devDependencies": { @@ -35,7 +31,7 @@ "@react-native/typescript-config": "0.75.2", "@types/react": "~18.2.45", "react-native-builder-bob": "^0.30.0", - "react-native-nitro-modules": "0.18.1", + "react-native-nitro-modules": "0.18.2", "typescript": "^5.1.3" }, "private": true diff --git a/example/src/components/CodeTag.tsx b/example/src/components/CodeTag.tsx index 719c843f..29cf0445 100644 --- a/example/src/components/CodeTag.tsx +++ b/example/src/components/CodeTag.tsx @@ -20,7 +20,7 @@ export function CodeTag({ children, textProps, ...props }: CodeTagProps) { const style = StyleSheet.create({ text: { - fontFamily: 'monospace', + // fontFamily: 'monospace', fontWeight: 600, fontSize: 16, }, diff --git a/example/src/components/Input.tsx b/example/src/components/Input.tsx index ce019647..5f724ed5 100644 --- a/example/src/components/Input.tsx +++ b/example/src/components/Input.tsx @@ -11,6 +11,7 @@ export function Input({ ...props }: InputProps) { } - -const style = StyleSheet.create({ - segment: { - marginHorizontal: 16, - }, -}) diff --git a/example/src/index.tsx b/example/src/index.tsx index 9221cc54..f4a6360d 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -7,6 +7,7 @@ import { LayoutAnimation, Platform, SafeAreaView, + ScrollView, Switch, TouchableOpacity, useColorScheme, @@ -19,6 +20,7 @@ import { Result, defaultOptions, Config, + openCropper, } from '@baronha/react-native-multiple-image-picker' import { useImmer } from 'use-immer' import { StatusBar } from 'expo-status-bar' @@ -35,10 +37,6 @@ import { } from './components' import useTheme from './hook/useTheme' import assets from './assets' -import Animated, { - useAnimatedScrollHandler, - useSharedValue, -} from 'react-native-reanimated' import { WIDTH } from './theme/size' import { IS_IOS, LOCALIZED_LANGUAGES } from './common/const' import { AppContext } from './hook/context' @@ -66,19 +64,9 @@ export default function App() { const { background, foreground } = useTheme() const [images, setImages] = useState([]) const [options, changeOptions] = useImmer(defaultOptions) - const scrollY = useSharedValue(0) const colorScheme = useColorScheme() - const onScroll = useAnimatedScrollHandler( - { - onScroll: (e) => { - scrollY.value = e.contentOffset.y - }, - }, - [] - ) - const setOptions = (key: keyof Config, value: Config[keyof Config]) => { changeOptions((draft) => { draft[key] = value as any @@ -94,6 +82,13 @@ export default function App() { const response = await openPicker({ ...options, selectedAssets: images, + crop: { + ratio: [ + { title: 'Instagram', width: 1, height: 1 }, + { title: 'Twitter', width: 16, height: 9 }, + { title: 'Facebook', width: 12, height: 11 }, + ], + }, }) setImages(Array.isArray(response) ? response : [response]) @@ -103,6 +98,30 @@ export default function App() { } } + const onCrop = async () => { + try { + console.log('images: ', images) + const response = await openCropper(images[0].path, { + ratio: [ + { title: 'Instagram', width: 1, height: 1 }, + { title: 'Twitter', width: 16, height: 9 }, + { title: 'Facebook', width: 12, height: 11 }, + ], + }) + + setImages((prev) => { + const data = [...prev] + data[0].path = response.path + data[0].width = response.width + data[0].height = response.height + return data + }) + layoutEffect() + } catch (e) { + console.log('e: ', e) + } + } + const onRemovePhoto = (_: Result, index: number) => { const data = [...images].filter((_, idx) => idx !== index) setImages(data) @@ -136,7 +155,7 @@ export default function App() { style={style.keyboardAvoidingView} > - {images.length > 0 ? ( - + <> + + + ) : ( @@ -504,7 +527,7 @@ export default function App() { - + diff --git a/example/yarn.lock b/example/yarn.lock index e16bee41..0000f4b2 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -508,7 +508,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.0.0-0", "@babel/plugin-transform-arrow-functions@^7.25.9": +"@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz" integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== @@ -738,7 +738,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-nullish-coalescing-operator@^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator@^7.24.1", "@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.1", "@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz" integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog== @@ -776,7 +776,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-optional-chaining@^7.0.0-0", "@babel/plugin-transform-optional-chaining@^7.24.5", "@babel/plugin-transform-optional-chaining@^7.25.9": +"@babel/plugin-transform-optional-chaining@^7.24.5", "@babel/plugin-transform-optional-chaining@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz" integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== @@ -897,7 +897,7 @@ babel-plugin-polyfill-regenerator "^0.6.1" semver "^6.3.1" -"@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.0.0-0", "@babel/plugin-transform-shorthand-properties@^7.25.9": +"@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz" integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== @@ -926,7 +926,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-template-literals@^7.0.0-0", "@babel/plugin-transform-template-literals@^7.25.9": +"@babel/plugin-transform-template-literals@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz" integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== @@ -1087,7 +1087,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.25.9" "@babel/plugin-transform-react-pure-annotations" "^7.25.9" -"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7", "@babel/preset-typescript@^7.23.0", "@babel/preset-typescript@^7.24.7": +"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.23.0", "@babel/preset-typescript@^7.24.7": version "7.26.0" resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz" integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== @@ -1151,13 +1151,6 @@ resolved "https://registry.npmjs.org/@baronha/react-native-image-grid/-/react-native-image-grid-0.2.7.tgz" integrity sha512-F5q+hJ1p0+hfWYhOK4uL2EgseG89hpiMu0rMIaiw7lMpEA3sKC1AUOhOTKS58LeX1xE/XoqZ0P7tzyntKN+EoQ== -"@egjs/hammerjs@^2.0.17": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124" - integrity sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A== - dependencies: - "@types/hammerjs" "^2.0.36" - "@expo/bunyan@^4.0.0": version "4.0.1" resolved "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.1.tgz" @@ -1480,21 +1473,6 @@ find-up "^5.0.0" js-yaml "^4.1.0" -"@gorhom/bottom-sheet@^5.0.6": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.0.6.tgz#f20736502399c7bcf8c73ea09e6b571dc07fe0eb" - integrity sha512-SI/AhPvgRfnCWN6/+wbE6TXwRE4X8F2fLyE4L/0bRwgE34Zenq585qLT139uEcfCIyovC2swC3ICqQpkmWEcFA== - dependencies: - "@gorhom/portal" "1.0.14" - invariant "^2.2.4" - -"@gorhom/portal@1.0.14": - version "1.0.14" - resolved "https://registry.yarnpkg.com/@gorhom/portal/-/portal-1.0.14.tgz#1953edb76aaba80fb24021dc774550194a18e111" - integrity sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A== - dependencies: - nanoid "^3.3.1" - "@graphql-typed-document-node/core@^3.1.0": version "3.2.0" resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" @@ -2261,11 +2239,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@types/hammerjs@^2.0.36": - version "2.0.46" - resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.46.tgz#381daaca1360ff8a7c8dff63f32e69745b9fb1e1" - integrity sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw== - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.6" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" @@ -4265,13 +4238,6 @@ hermes-parser@0.23.1: dependencies: hermes-estree "0.23.1" -hoist-non-react-statics@^3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - hosted-git-info@^3.0.2: version "3.0.8" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz" @@ -5574,7 +5540,7 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@^3.3.1, nanoid@^3.3.7: +nanoid@^3.3.7: version "3.3.8" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== @@ -6116,7 +6082,7 @@ prompts@^2.3.2, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6183,7 +6149,7 @@ react-devtools-core@^5.3.1: shell-quote "^1.6.1" ws "^7" -react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.4: +react-is@^16.13.1, react-is@^16.8.4: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6226,35 +6192,10 @@ react-native-builder-bob@^0.30.0: which "^2.0.2" yargs "^17.5.1" -react-native-gesture-handler@~2.16.1: - version "2.16.2" - resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz#032bd2a07334292d7f6cff1dc9d1ec928f72e26d" - integrity sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg== - dependencies: - "@egjs/hammerjs" "^2.0.17" - hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - lodash "^4.17.21" - prop-types "^15.7.2" - -react-native-nitro-modules@0.18.1: - version "0.18.1" - resolved "https://registry.npmjs.org/react-native-nitro-modules/-/react-native-nitro-modules-0.18.1.tgz" - integrity sha512-F1PA92N8Qv/0I3gKnUFU/eP2C16TSSWwuWuUJnVXX4pCrZztP6BHSvRAZj9WpwxytoKICjwgeVk8K//kvZDZAg== - -react-native-reanimated@~3.10.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz#3c37d1100bbba0065df39c96aab0c1ff1b50c0fa" - integrity sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w== - dependencies: - "@babel/plugin-transform-arrow-functions" "^7.0.0-0" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.0.0-0" - "@babel/plugin-transform-optional-chaining" "^7.0.0-0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0-0" - "@babel/plugin-transform-template-literals" "^7.0.0-0" - "@babel/preset-typescript" "^7.16.7" - convert-source-map "^2.0.0" - invariant "^2.2.4" +react-native-nitro-modules@0.18.2: + version "0.18.2" + resolved "https://registry.yarnpkg.com/react-native-nitro-modules/-/react-native-nitro-modules-0.18.2.tgz#f1172f90ecaded0e4b3306f6e7be4b54a24b310e" + integrity sha512-eHsq1cRfm/Bz1Nq7KctTqxAqhzVSNo0WGX281xARZh+vOq8633Qxn1NHRZ5/Rno2Bla6HOXlUW6RoW0wKM/7kg== react-native@0.75.0: version "0.75.0" @@ -6339,11 +6280,6 @@ readline@^1.3.0: resolved "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz" integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== -reanimated-color-picker@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/reanimated-color-picker/-/reanimated-color-picker-3.0.6.tgz#f20074f7c9c2e0e2c15d06a586d8b508d38ce1eb" - integrity sha512-ILmeR57d/DbWDy1vPdgz9vkRJyWHMIeTWEtxUgXPEH/BsiSXXhLju+hFDZY3M+bZUwpoCJWcvaAZl7p9kVwiEQ== - recast@^0.21.0: version "0.21.5" resolved "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz" diff --git a/ios/HybridMultipleImagePicker+Config.swift b/ios/HybridMultipleImagePicker+Config.swift index 3549154b..757140d9 100644 --- a/ios/HybridMultipleImagePicker+Config.swift +++ b/ios/HybridMultipleImagePicker+Config.swift @@ -115,22 +115,7 @@ extension HybridMultipleImagePicker { config.editorOptions = [.photo, .gifPhoto, .livePhoto] if let crop = options.crop { - var editor = config.editor - - let isCircle = crop.circle ?? false - - editor.cropSize.isRoundCrop = isCircle - - if isCircle { - editor.cropSize.aspectRatios = [] - } else { - editor.cropSize.aspectRatios = PickerConfiguration.default.editor.cropSize.aspectRatios - } - - editor.photo.defaultSelectedToolOption = .cropSize - editor.toolsView = .init(toolOptions: [.init(imageType: config.editor.imageResource.editor.tools.cropSize, type: .cropSize)]) - - editor.isFixedCropSizeState = true + let editor = setCropConfig(crop) config.editor = editor @@ -164,10 +149,7 @@ extension HybridMultipleImagePicker { } // LIGHT THEME - if isDark { -// config.appearanceStyle = .dark -// config.photoList.titleView.backgroundColor = UIColor.white.withAlphaComponent(0.5) - } else { + if !isDark { let background = UIColor.white let barStyle = UIBarStyle.default @@ -225,35 +207,37 @@ extension HybridMultipleImagePicker { } } - switch options.language { + config.languageType = setLocale(language: options.language) + } + + func setLocale(language: Language) -> LanguageType { + switch language { case .vi: - config.languageType = .vietnamese // -> 🇻🇳 My country. Yeahhh + return .vietnamese // -> 🇻🇳 My country. Yeahhh case .zhHans: - config.languageType = .simplifiedChinese + return .simplifiedChinese case .zhHant: - config.languageType = .traditionalChinese + return .traditionalChinese case .ja: - config.languageType = .japanese + return .japanese case .ko: - config.languageType = .korean + return .korean case .en: - config.languageType = .english + return .english case .th: - config.languageType = .thai + return .thai case .id: - config.languageType = .indonesia - + return .indonesia case .ru: - config.languageType = .russian + return .russian case .de: - config.languageType = .german + return .german case .fr: - config.languageType = .french + return .french case .ar: - config.languageType = .arabic - + return .arabic default: - config.languageType = .system + return .system } } } diff --git a/ios/HybridMultipleImagePicker+Crop.swift b/ios/HybridMultipleImagePicker+Crop.swift new file mode 100644 index 00000000..4cde2f2d --- /dev/null +++ b/ios/HybridMultipleImagePicker+Crop.swift @@ -0,0 +1,117 @@ +// +// HybridMultipleImagePicker+Crop.swift +// Pods +// +// Created by BAO HA on 9/12/24. +// + +import HXPhotoPicker +import MobileCoreServices + +// class CropConfig: PickerCropConfig { +// // +// } + +extension HybridMultipleImagePicker { + func openCrop(image: String, config: NitroCropConfig, resolved: @escaping ((CropResult) -> Void), rejected: @escaping ((Double) -> Void)) throws { + let asset: EditorAsset + + if !isImage(image) { return rejected(0) } + + if image.hasPrefix("http://") || image.hasPrefix("https://") || image.hasPrefix("file://") { + guard let url = URL(string: image), + let data = try? Data(contentsOf: url) + + else { + rejected(0) + return + } + + asset = .init(type: .imageData(data)) + } else { + asset = .init(type: .photoAsset(.init(localIdentifier: image))) + } + + let cropOption = PickerCropConfig(circle: config.circle, ratio: config.ratio, defaultRatio: config.defaultRatio, freeStyle: config.freeStyle) + + var editConfig = setCropConfig(cropOption) + + editConfig.languageType = setLocale(language: config.language) + + DispatchQueue.main.async { + Photo.edit(asset: asset, config: editConfig) { result, _ in + + if let path = result.result?.url.absoluteString, let size = result.result?.image?.size { + let result = CropResult(path: path, width: size.width, height: size.height) + + resolved(result) + } + } + } + } + + func setCropConfig(_ cropConfig: PickerCropConfig) -> EditorConfiguration { + var config = EditorConfiguration() + + if let defaultRatio = cropConfig.defaultRatio { + config.cropSize.aspectRatio = .init(width: defaultRatio.width, height: defaultRatio.height) + } + + config.photo.defaultSelectedToolOption = .cropSize + + config.isFixedCropSizeState = true + + config.cropSize.defaultSeletedIndex = 0 + + let freeStyle = cropConfig.freeStyle ?? true + + config.cropSize.isFixedRatio = !freeStyle + + config.isWhetherFinishButtonDisabledInUneditedState = true + + config.cropSize.isRoundCrop = cropConfig.circle ?? false + + config.cropSize.isResetToOriginal = true + + config.toolsView = .init(toolOptions: [.init(imageType: PickerConfiguration.default.editor.imageResource.editor.tools.cropSize, type: .cropSize)]) + + config.photo.defaultSelectedToolOption = .cropSize + + if config.cropSize.isRoundCrop { + config.cropSize.aspectRatios = [] + } else { + var aspectRatios: [EditorRatioToolConfig] = PickerConfiguration.default.editor.cropSize.aspectRatios + + let ratio = cropConfig.ratio + // custom ratio + if ratio.count > 0 { + ratio.forEach { ratio in + let width = Int(ratio.width) + let height = Int(ratio.height) + + aspectRatios.insert(.init(title: .custom(ratio.title ?? "\(width)/\(height)"), ratio: .init(width: width, height: height)), at: 3) + } + } + + config.cropSize.aspectRatios = freeStyle ? aspectRatios : aspectRatios.filter { + // check freeStyle crop + if $0.ratio == .zero { return false } + + return true + } + } + + return config + } +} + +private func isImage(_ urlString: String) -> Bool { + guard let url = URL(string: urlString), + let pathExtension = url.pathExtension as CFString?, + let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil)?.takeRetainedValue() + else { + return false + } + + return UTTypeConformsTo(uti, kUTTypeImage) +} diff --git a/ios/TopViewController.swift b/ios/TopViewController.swift new file mode 100644 index 00000000..c96246fa --- /dev/null +++ b/ios/TopViewController.swift @@ -0,0 +1,17 @@ +// +// TopViewController.swift +// Pods +// +// Created by BAO HA on 11/12/24. +// + +import UIKit + +func getTopViewController() -> UIViewController? { + var controller = UIApplication.shared.keyWindow?.rootViewController + while let presentedViewController = controller?.presentedViewController { + controller = presentedViewController + } + + return controller +} diff --git a/nitro.json b/nitro.json index 4319580e..1608f542 100644 --- a/nitro.json +++ b/nitro.json @@ -1,4 +1,5 @@ { + "$schema": "https://nitro.margelo.com/nitro.schema.json", "cxxNamespace": ["multipleimagepicker"], "ios": { "iosModuleName": "MultipleImagePicker" diff --git a/nitrogen/generated/android/MultipleImagePickerOnLoad.cpp b/nitrogen/generated/android/MultipleImagePickerOnLoad.cpp index b0709e89..8564a7ef 100644 --- a/nitrogen/generated/android/MultipleImagePickerOnLoad.cpp +++ b/nitrogen/generated/android/MultipleImagePickerOnLoad.cpp @@ -14,6 +14,7 @@ #include "JHybridMultipleImagePickerSpec.hpp" #include "JFunc_void_std__vector_Result_.hpp" #include "JFunc_void_double.hpp" +#include "JFunc_void_CropResult.hpp" #include #include @@ -29,6 +30,8 @@ int initialize(JavaVM* vm) { margelo::nitro::multipleimagepicker::JHybridMultipleImagePickerSpec::registerNatives(); margelo::nitro::multipleimagepicker::JFunc_void_std__vector_Result_::registerNatives(); margelo::nitro::multipleimagepicker::JFunc_void_double::registerNatives(); + margelo::nitro::multipleimagepicker::JFunc_void_CropResult::registerNatives(); + margelo::nitro::multipleimagepicker::JFunc_void_double::registerNatives(); // Register Nitro Hybrid Objects HybridObjectRegistry::registerHybridObjectConstructor( diff --git a/nitrogen/generated/android/c++/JCropRatio.hpp b/nitrogen/generated/android/c++/JCropRatio.hpp new file mode 100644 index 00000000..562bfd7c --- /dev/null +++ b/nitrogen/generated/android/c++/JCropRatio.hpp @@ -0,0 +1,61 @@ +/// +/// JCropRatio.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "CropRatio.hpp" + +#include +#include + +namespace margelo::nitro::multipleimagepicker { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "CropRatio" and the the Kotlin data class "CropRatio". + */ + struct JCropRatio final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/multipleimagepicker/CropRatio;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct CropRatio by copying all values to C++. + */ + [[maybe_unused]] + CropRatio toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldTitle = clazz->getField("title"); + jni::local_ref title = this->getFieldValue(fieldTitle); + static const auto fieldWidth = clazz->getField("width"); + double width = this->getFieldValue(fieldWidth); + static const auto fieldHeight = clazz->getField("height"); + double height = this->getFieldValue(fieldHeight); + return CropRatio( + title != nullptr ? std::make_optional(title->toStdString()) : std::nullopt, + width, + height + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const CropRatio& value) { + return newInstance( + value.title.has_value() ? jni::make_jstring(value.title.value()) : nullptr, + value.width, + value.height + ); + } + }; + +} // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JCropResult.hpp b/nitrogen/generated/android/c++/JCropResult.hpp new file mode 100644 index 00000000..0ba86dcd --- /dev/null +++ b/nitrogen/generated/android/c++/JCropResult.hpp @@ -0,0 +1,60 @@ +/// +/// JCropResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "CropResult.hpp" + +#include + +namespace margelo::nitro::multipleimagepicker { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "CropResult" and the the Kotlin data class "CropResult". + */ + struct JCropResult final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/multipleimagepicker/CropResult;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct CropResult by copying all values to C++. + */ + [[maybe_unused]] + CropResult toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldPath = clazz->getField("path"); + jni::local_ref path = this->getFieldValue(fieldPath); + static const auto fieldWidth = clazz->getField("width"); + double width = this->getFieldValue(fieldWidth); + static const auto fieldHeight = clazz->getField("height"); + double height = this->getFieldValue(fieldHeight); + return CropResult( + path->toStdString(), + width, + height + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const CropResult& value) { + return newInstance( + jni::make_jstring(value.path), + value.width, + value.height + ); + } + }; + +} // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JFunc_void_CropResult.hpp b/nitrogen/generated/android/c++/JFunc_void_CropResult.hpp new file mode 100644 index 00000000..024e3611 --- /dev/null +++ b/nitrogen/generated/android/c++/JFunc_void_CropResult.hpp @@ -0,0 +1,51 @@ +/// +/// JFunc_void_CropResult.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include +#include "CropResult.hpp" +#include "JCropResult.hpp" +#include + +namespace margelo::nitro::multipleimagepicker { + + using namespace facebook; + + /** + * C++ representation of the callback Func_void_CropResult. + * This is a Kotlin `(result: CropResult) -> Unit`, backed by a `std::function<...>`. + */ + struct JFunc_void_CropResult final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& func) { + return JFunc_void_CropResult::newObjectCxxArgs(func); + } + + public: + void call(jni::alias_ref result) { + return _func(result->toCpp()); + } + + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/multipleimagepicker/Func_void_CropResult;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("call", JFunc_void_CropResult::call)}); + } + + private: + explicit JFunc_void_CropResult(const std::function& func): _func(func) { } + + private: + friend HybridBase; + std::function _func; + }; + +} // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp index 2b9c3167..430b1ee1 100644 --- a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp @@ -21,6 +21,8 @@ namespace margelo::nitro::multipleimagepicker { enum class SelectBoxStyle; } namespace margelo::nitro::multipleimagepicker { enum class SelectMode; } // Forward declaration of `PickerCropConfig` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct PickerCropConfig; } +// Forward declaration of `CropRatio` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropRatio; } // Forward declaration of `Text` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct Text; } // Forward declaration of `Language` to properly resolve imports. @@ -29,6 +31,10 @@ namespace margelo::nitro::multipleimagepicker { enum class Language; } namespace margelo::nitro::multipleimagepicker { enum class Theme; } // Forward declaration of `Presentation` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { enum class Presentation; } +// Forward declaration of `NitroCropConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } +// Forward declaration of `CropResult` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropResult; } #include "NitroConfig.hpp" #include "JNitroConfig.hpp" @@ -47,6 +53,8 @@ namespace margelo::nitro::multipleimagepicker { enum class Presentation; } #include "JSelectMode.hpp" #include "PickerCropConfig.hpp" #include "JPickerCropConfig.hpp" +#include "CropRatio.hpp" +#include "JCropRatio.hpp" #include "Text.hpp" #include "JText.hpp" #include "Language.hpp" @@ -58,6 +66,11 @@ namespace margelo::nitro::multipleimagepicker { enum class Presentation; } #include #include "JFunc_void_std__vector_Result_.hpp" #include "JFunc_void_double.hpp" +#include "NitroCropConfig.hpp" +#include "JNitroCropConfig.hpp" +#include "CropResult.hpp" +#include "JFunc_void_CropResult.hpp" +#include "JCropResult.hpp" namespace margelo::nitro::multipleimagepicker { @@ -84,5 +97,9 @@ namespace margelo::nitro::multipleimagepicker { static const auto method = _javaPart->getClass()->getMethod /* config */, jni::alias_ref /* resolved */, jni::alias_ref /* rejected */)>("openPicker"); method(_javaPart, JNitroConfig::fromCpp(config), JFunc_void_std__vector_Result_::fromCpp(resolved), JFunc_void_double::fromCpp(rejected)); } + void JHybridMultipleImagePickerSpec::openCrop(const std::string& image, const NitroCropConfig& config, const std::function& resolved, const std::function& rejected) { + static const auto method = _javaPart->getClass()->getMethod /* image */, jni::alias_ref /* config */, jni::alias_ref /* resolved */, jni::alias_ref /* rejected */)>("openCrop"); + method(_javaPart, jni::make_jstring(image), JNitroCropConfig::fromCpp(config), JFunc_void_CropResult::fromCpp(resolved), JFunc_void_double::fromCpp(rejected)); + } } // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp index 94bdba84..b5a44f69 100644 --- a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp @@ -52,6 +52,7 @@ namespace margelo::nitro::multipleimagepicker { public: // Methods void openPicker(const NitroConfig& config, const std::function& /* result */)>& resolved, const std::function& rejected) override; + void openCrop(const std::string& image, const NitroCropConfig& config, const std::function& resolved, const std::function& rejected) override; private: friend HybridBase; diff --git a/nitrogen/generated/android/c++/JNitroConfig.hpp b/nitrogen/generated/android/c++/JNitroConfig.hpp index 8ddc2df2..02ad42df 100644 --- a/nitrogen/generated/android/c++/JNitroConfig.hpp +++ b/nitrogen/generated/android/c++/JNitroConfig.hpp @@ -10,6 +10,8 @@ #include #include "NitroConfig.hpp" +#include "CropRatio.hpp" +#include "JCropRatio.hpp" #include "JLanguage.hpp" #include "JMediaType.hpp" #include "JPickerCropConfig.hpp" diff --git a/nitrogen/generated/android/c++/JNitroCropConfig.hpp b/nitrogen/generated/android/c++/JNitroCropConfig.hpp new file mode 100644 index 00000000..604b256f --- /dev/null +++ b/nitrogen/generated/android/c++/JNitroCropConfig.hpp @@ -0,0 +1,97 @@ +/// +/// JNitroCropConfig.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "NitroCropConfig.hpp" + +#include "CropRatio.hpp" +#include "JCropRatio.hpp" +#include "JLanguage.hpp" +#include "JPresentation.hpp" +#include "Language.hpp" +#include "Presentation.hpp" +#include +#include +#include + +namespace margelo::nitro::multipleimagepicker { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "NitroCropConfig" and the the Kotlin data class "NitroCropConfig". + */ + struct JNitroCropConfig final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/multipleimagepicker/NitroCropConfig;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct NitroCropConfig by copying all values to C++. + */ + [[maybe_unused]] + NitroCropConfig toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldLanguage = clazz->getField("language"); + jni::local_ref language = this->getFieldValue(fieldLanguage); + static const auto fieldPresentation = clazz->getField("presentation"); + jni::local_ref presentation = this->getFieldValue(fieldPresentation); + static const auto fieldCircle = clazz->getField("circle"); + jni::local_ref circle = this->getFieldValue(fieldCircle); + static const auto fieldRatio = clazz->getField>("ratio"); + jni::local_ref> ratio = this->getFieldValue(fieldRatio); + static const auto fieldDefaultRatio = clazz->getField("defaultRatio"); + jni::local_ref defaultRatio = this->getFieldValue(fieldDefaultRatio); + static const auto fieldFreeStyle = clazz->getField("freeStyle"); + jni::local_ref freeStyle = this->getFieldValue(fieldFreeStyle); + return NitroCropConfig( + language->toCpp(), + presentation->toCpp(), + circle != nullptr ? std::make_optional(static_cast(circle->value())) : std::nullopt, + [&]() { + size_t __size = ratio->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = ratio->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(), + defaultRatio != nullptr ? std::make_optional(defaultRatio->toCpp()) : std::nullopt, + freeStyle != nullptr ? std::make_optional(static_cast(freeStyle->value())) : std::nullopt + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const NitroCropConfig& value) { + return newInstance( + JLanguage::fromCpp(value.language), + JPresentation::fromCpp(value.presentation), + value.circle.has_value() ? jni::JBoolean::valueOf(value.circle.value()) : nullptr, + [&]() { + size_t __size = value.ratio.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = value.ratio[__i]; + __array->setElement(__i, *JCropRatio::fromCpp(__element)); + } + return __array; + }(), + value.defaultRatio.has_value() ? JCropRatio::fromCpp(value.defaultRatio.value()) : nullptr, + value.freeStyle.has_value() ? jni::JBoolean::valueOf(value.freeStyle.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JPickerCropConfig.hpp b/nitrogen/generated/android/c++/JPickerCropConfig.hpp index 7d6461c0..f03fd736 100644 --- a/nitrogen/generated/android/c++/JPickerCropConfig.hpp +++ b/nitrogen/generated/android/c++/JPickerCropConfig.hpp @@ -10,7 +10,11 @@ #include #include "PickerCropConfig.hpp" +#include "CropRatio.hpp" +#include "JCropRatio.hpp" #include +#include +#include namespace margelo::nitro::multipleimagepicker { @@ -32,8 +36,26 @@ namespace margelo::nitro::multipleimagepicker { static const auto clazz = javaClassStatic(); static const auto fieldCircle = clazz->getField("circle"); jni::local_ref circle = this->getFieldValue(fieldCircle); + static const auto fieldRatio = clazz->getField>("ratio"); + jni::local_ref> ratio = this->getFieldValue(fieldRatio); + static const auto fieldDefaultRatio = clazz->getField("defaultRatio"); + jni::local_ref defaultRatio = this->getFieldValue(fieldDefaultRatio); + static const auto fieldFreeStyle = clazz->getField("freeStyle"); + jni::local_ref freeStyle = this->getFieldValue(fieldFreeStyle); return PickerCropConfig( - circle != nullptr ? std::make_optional(static_cast(circle->value())) : std::nullopt + circle != nullptr ? std::make_optional(static_cast(circle->value())) : std::nullopt, + [&]() { + size_t __size = ratio->size(); + std::vector __vector; + __vector.reserve(__size); + for (size_t __i = 0; __i < __size; __i++) { + auto __element = ratio->getElement(__i); + __vector.push_back(__element->toCpp()); + } + return __vector; + }(), + defaultRatio != nullptr ? std::make_optional(defaultRatio->toCpp()) : std::nullopt, + freeStyle != nullptr ? std::make_optional(static_cast(freeStyle->value())) : std::nullopt ); } @@ -44,7 +66,18 @@ namespace margelo::nitro::multipleimagepicker { [[maybe_unused]] static jni::local_ref fromCpp(const PickerCropConfig& value) { return newInstance( - value.circle.has_value() ? jni::JBoolean::valueOf(value.circle.value()) : nullptr + value.circle.has_value() ? jni::JBoolean::valueOf(value.circle.value()) : nullptr, + [&]() { + size_t __size = value.ratio.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = value.ratio[__i]; + __array->setElement(__i, *JCropRatio::fromCpp(__element)); + } + return __array; + }(), + value.defaultRatio.has_value() ? JCropRatio::fromCpp(value.defaultRatio.value()) : nullptr, + value.freeStyle.has_value() ? jni::JBoolean::valueOf(value.freeStyle.value()) : nullptr ); } }; diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/CropRatio.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/CropRatio.kt new file mode 100644 index 00000000..b54951d9 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/CropRatio.kt @@ -0,0 +1,23 @@ +/// +/// CropRatio.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.multipleimagepicker + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.* + +/** + * Represents the JavaScript object/struct "CropRatio". + */ +@DoNotStrip +@Keep +data class CropRatio( + val title: String?, + val width: Double, + val height: Double +) diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/CropResult.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/CropResult.kt new file mode 100644 index 00000000..6162c117 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/CropResult.kt @@ -0,0 +1,23 @@ +/// +/// CropResult.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.multipleimagepicker + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.* + +/** + * Represents the JavaScript object/struct "CropResult". + */ +@DoNotStrip +@Keep +data class CropResult( + val path: String, + val width: Double, + val height: Double +) diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/Func_void_CropResult.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/Func_void_CropResult.kt new file mode 100644 index 00000000..b4d5c720 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/Func_void_CropResult.kt @@ -0,0 +1,46 @@ +/// +/// Func_void_CropResult.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.multipleimagepicker + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.* +import dalvik.annotation.optimization.FastNative + +/** + * Represents the JavaScript callback `(result: struct) => void`. + * This is implemented in C++, via a `std::function<...>`. + */ +@DoNotStrip +@Keep +@Suppress("RedundantSuppression", "ConvertSecondaryConstructorToPrimary", "RedundantUnitReturnType", "KotlinJniMissingFunction", "ClassName", "unused") +class Func_void_CropResult { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + /** + * Converts this function to a Kotlin Lambda. + * This exists purely as syntactic sugar, and has minimal runtime overhead. + */ + fun toLambda(): (result: CropResult) -> Unit = this::call + + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @FastNative + external fun call(result: CropResult): Unit +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/HybridMultipleImagePickerSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/HybridMultipleImagePickerSpec.kt index 3dd0b3e3..82e8d0c4 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/HybridMultipleImagePickerSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/HybridMultipleImagePickerSpec.kt @@ -51,6 +51,17 @@ abstract class HybridMultipleImagePickerSpec: HybridObject() { val __result = openPicker(config, resolved.toLambda(), rejected.toLambda()) return __result } + + @DoNotStrip + @Keep + abstract fun openCrop(image: String, config: NitroCropConfig, resolved: (result: CropResult) -> Unit, rejected: (reject: Double) -> Unit): Unit + + @DoNotStrip + @Keep + private fun openCrop(image: String, config: NitroCropConfig, resolved: Func_void_CropResult, rejected: Func_void_double): Unit { + val __result = openCrop(image, config, resolved.toLambda(), rejected.toLambda()) + return __result + } private external fun initHybrid(): HybridData diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroCropConfig.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroCropConfig.kt new file mode 100644 index 00000000..8cad51b8 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroCropConfig.kt @@ -0,0 +1,26 @@ +/// +/// NitroCropConfig.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2024 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.multipleimagepicker + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.core.* + +/** + * Represents the JavaScript object/struct "NitroCropConfig". + */ +@DoNotStrip +@Keep +data class NitroCropConfig( + val language: Language, + val presentation: Presentation, + val circle: Boolean?, + val ratio: Array, + val defaultRatio: CropRatio?, + val freeStyle: Boolean? +) diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/PickerCropConfig.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/PickerCropConfig.kt index 0437b61f..15232d12 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/PickerCropConfig.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/PickerCropConfig.kt @@ -17,5 +17,8 @@ import com.margelo.nitro.core.* @DoNotStrip @Keep data class PickerCropConfig( - val circle: Boolean? + val circle: Boolean?, + val ratio: Array, + val defaultRatio: CropRatio?, + val freeStyle: Boolean? ) diff --git a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp index 616bc06e..7cc2cdd8 100644 --- a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp @@ -8,6 +8,10 @@ #pragma once // Forward declarations of C++ defined types +// Forward declaration of `CropRatio` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropRatio; } +// Forward declaration of `CropResult` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropResult; } // Forward declaration of `HybridMultipleImagePickerSpec` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { class HybridMultipleImagePickerSpec; } // Forward declaration of `PickerCropConfig` to properly resolve imports. @@ -24,6 +28,8 @@ namespace margelo::nitro::multipleimagepicker { struct Text; } namespace MultipleImagePicker { class HybridMultipleImagePickerSpecCxx; } // Include C++ defined types +#include "CropRatio.hpp" +#include "CropResult.hpp" #include "HybridMultipleImagePickerSpec.hpp" #include "PickerCropConfig.hpp" #include "Result.hpp" @@ -79,6 +85,26 @@ namespace margelo::nitro::multipleimagepicker::bridge::swift { return vector; } + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_CropRatio_ = std::vector; + inline std::vector create_std__vector_CropRatio_(size_t size) { + std::vector vector; + vector.reserve(size); + return vector; + } + + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_CropRatio_ = std::optional; + inline std::optional create_std__optional_CropRatio_(const CropRatio& value) { + return std::optional(value); + } + // pragma MARK: std::optional /** * Specialized version of `std::optional`. @@ -153,6 +179,34 @@ namespace margelo::nitro::multipleimagepicker::bridge::swift { return std::make_shared(value); } + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_CropResult = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_CropResult_Wrapper final { + public: + explicit Func_void_CropResult_Wrapper(const std::function& func): _function(func) {} + explicit Func_void_CropResult_Wrapper(std::function&& func): _function(std::move(func)) {} + inline void call(CropResult result) const { + _function(result); + } + private: + std::function _function; + }; + inline Func_void_CropResult create_Func_void_CropResult(void* _Nonnull closureHolder, void(* _Nonnull call)(void* _Nonnull /* closureHolder */, CropResult), void(* _Nonnull destroy)(void* _Nonnull)) { + std::shared_ptr sharedClosureHolder(closureHolder, destroy); + return Func_void_CropResult([sharedClosureHolder, call](const CropResult& result) -> void { + call(sharedClosureHolder.get(), result); + }); + } + inline std::shared_ptr share_Func_void_CropResult(const Func_void_CropResult& value) { + return std::make_shared(value); + } + // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. diff --git a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Umbrella.hpp b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Umbrella.hpp index 09fb58ea..76ebc917 100644 --- a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Umbrella.hpp +++ b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Umbrella.hpp @@ -8,6 +8,10 @@ #pragma once // Forward declarations of C++ defined types +// Forward declaration of `CropRatio` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropRatio; } +// Forward declaration of `CropResult` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropResult; } // Forward declaration of `HybridMultipleImagePickerSpec` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { class HybridMultipleImagePickerSpec; } // Forward declaration of `Language` to properly resolve imports. @@ -16,6 +20,8 @@ namespace margelo::nitro::multipleimagepicker { enum class Language; } namespace margelo::nitro::multipleimagepicker { enum class MediaType; } // Forward declaration of `NitroConfig` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct NitroConfig; } +// Forward declaration of `NitroCropConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } // Forward declaration of `PickerCropConfig` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct PickerCropConfig; } // Forward declaration of `Presentation` to properly resolve imports. @@ -34,10 +40,13 @@ namespace margelo::nitro::multipleimagepicker { struct Text; } namespace margelo::nitro::multipleimagepicker { enum class Theme; } // Include C++ defined types +#include "CropRatio.hpp" +#include "CropResult.hpp" #include "HybridMultipleImagePickerSpec.hpp" #include "Language.hpp" #include "MediaType.hpp" #include "NitroConfig.hpp" +#include "NitroCropConfig.hpp" #include "PickerCropConfig.hpp" #include "Presentation.hpp" #include "Result.hpp" diff --git a/nitrogen/generated/ios/c++/HybridMultipleImagePickerSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridMultipleImagePickerSpecSwift.hpp index 70d4bb3e..03fce052 100644 --- a/nitrogen/generated/ios/c++/HybridMultipleImagePickerSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridMultipleImagePickerSpecSwift.hpp @@ -26,6 +26,8 @@ namespace margelo::nitro::multipleimagepicker { enum class SelectBoxStyle; } namespace margelo::nitro::multipleimagepicker { enum class SelectMode; } // Forward declaration of `PickerCropConfig` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct PickerCropConfig; } +// Forward declaration of `CropRatio` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropRatio; } // Forward declaration of `Text` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct Text; } // Forward declaration of `Language` to properly resolve imports. @@ -34,6 +36,10 @@ namespace margelo::nitro::multipleimagepicker { enum class Language; } namespace margelo::nitro::multipleimagepicker { enum class Theme; } // Forward declaration of `Presentation` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { enum class Presentation; } +// Forward declaration of `NitroCropConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } +// Forward declaration of `CropResult` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropResult; } #include "NitroConfig.hpp" #include "MediaType.hpp" @@ -45,11 +51,14 @@ namespace margelo::nitro::multipleimagepicker { enum class Presentation; } #include "SelectBoxStyle.hpp" #include "SelectMode.hpp" #include "PickerCropConfig.hpp" +#include "CropRatio.hpp" #include "Text.hpp" #include "Language.hpp" #include "Theme.hpp" #include "Presentation.hpp" #include +#include "NitroCropConfig.hpp" +#include "CropResult.hpp" #if __has_include() #include @@ -97,6 +106,9 @@ namespace margelo::nitro::multipleimagepicker { inline void openPicker(const NitroConfig& config, const std::function& /* result */)>& resolved, const std::function& rejected) override { _swiftPart.openPicker(config, resolved, rejected); } + inline void openCrop(const std::string& image, const NitroCropConfig& config, const std::function& resolved, const std::function& rejected) override { + _swiftPart.openCrop(image, config, resolved, rejected); + } private: MultipleImagePicker::HybridMultipleImagePickerSpecCxx _swiftPart; diff --git a/nitrogen/generated/ios/swift/CropRatio.swift b/nitrogen/generated/ios/swift/CropRatio.swift new file mode 100644 index 00000000..3c06928c --- /dev/null +++ b/nitrogen/generated/ios/swift/CropRatio.swift @@ -0,0 +1,75 @@ +/// +/// CropRatio.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 `CropRatio`, backed by a C++ struct. + */ +public typealias CropRatio = margelo.nitro.multipleimagepicker.CropRatio + +public extension CropRatio { + private typealias bridge = margelo.nitro.multipleimagepicker.bridge.swift + + /** + * Create a new instance of `CropRatio`. + */ + init(title: String?, width: Double, height: Double) { + self.init({ () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = title { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), width, height) + } + + var title: String? { + @inline(__always) + get { + return { () -> String? in + if let __unwrapped = self.__title.value { + return String(__unwrapped) + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__title = { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }() + } + } + + var width: Double { + @inline(__always) + get { + return self.__width + } + @inline(__always) + set { + self.__width = newValue + } + } + + var height: Double { + @inline(__always) + get { + return self.__height + } + @inline(__always) + set { + self.__height = newValue + } + } +} diff --git a/nitrogen/generated/ios/swift/CropResult.swift b/nitrogen/generated/ios/swift/CropResult.swift new file mode 100644 index 00000000..c06ca480 --- /dev/null +++ b/nitrogen/generated/ios/swift/CropResult.swift @@ -0,0 +1,57 @@ +/// +/// CropResult.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 `CropResult`, backed by a C++ struct. + */ +public typealias CropResult = margelo.nitro.multipleimagepicker.CropResult + +public extension CropResult { + private typealias bridge = margelo.nitro.multipleimagepicker.bridge.swift + + /** + * Create a new instance of `CropResult`. + */ + init(path: String, width: Double, height: Double) { + self.init(std.string(path), width, height) + } + + var path: String { + @inline(__always) + get { + return String(self.__path) + } + @inline(__always) + set { + self.__path = std.string(newValue) + } + } + + var width: Double { + @inline(__always) + get { + return self.__width + } + @inline(__always) + set { + self.__width = newValue + } + } + + var height: Double { + @inline(__always) + get { + return self.__height + } + @inline(__always) + set { + self.__height = newValue + } + } +} diff --git a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift index 355b2fa6..b8d71d6e 100644 --- a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift +++ b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift @@ -33,4 +33,5 @@ public protocol HybridMultipleImagePickerSpec: AnyObject, HybridObjectSpec { // Methods func openPicker(config: NitroConfig, resolved: @escaping ((_ result: [Result]) -> Void), rejected: @escaping ((_ reject: Double) -> Void)) throws -> Void + func openCrop(image: String, config: NitroCropConfig, resolved: @escaping ((_ result: CropResult) -> Void), rejected: @escaping ((_ reject: Double) -> Void)) throws -> Void } diff --git a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift index fc70ae2d..4956d226 100644 --- a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift +++ b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift @@ -124,4 +124,25 @@ public class HybridMultipleImagePickerSpecCxx { fatalError("Swift errors can currently not be propagated to C++! See https://github.com/swiftlang/swift/issues/75290 (Error: \(__message))") } } + + @inline(__always) + public func openCrop(image: std.string, config: NitroCropConfig, resolved: bridge.Func_void_CropResult, rejected: bridge.Func_void_double) -> Void { + do { + try self.__implementation.openCrop(image: String(image), config: config, resolved: { () -> ((CropResult) -> Void) in + let __sharedClosure = bridge.share_Func_void_CropResult(resolved) + return { (__result: CropResult) -> Void in + __sharedClosure.pointee.call(__result) + } + }(), rejected: { () -> ((Double) -> Void) in + let __sharedClosure = bridge.share_Func_void_double(rejected) + return { (__reject: Double) -> Void in + __sharedClosure.pointee.call(__reject) + } + }()) + return + } 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))") + } + } } diff --git a/nitrogen/generated/ios/swift/NitroCropConfig.swift b/nitrogen/generated/ios/swift/NitroCropConfig.swift new file mode 100644 index 00000000..3960b144 --- /dev/null +++ b/nitrogen/generated/ios/swift/NitroCropConfig.swift @@ -0,0 +1,144 @@ +/// +/// NitroCropConfig.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 `NitroCropConfig`, backed by a C++ struct. + */ +public typealias NitroCropConfig = margelo.nitro.multipleimagepicker.NitroCropConfig + +public extension NitroCropConfig { + private typealias bridge = margelo.nitro.multipleimagepicker.bridge.swift + + /** + * Create a new instance of `NitroCropConfig`. + */ + init(language: Language, presentation: Presentation, circle: Bool?, ratio: [CropRatio], defaultRatio: CropRatio?, freeStyle: Bool?) { + self.init(language, presentation, { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = circle { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__vector_CropRatio_ in + var __vector = bridge.create_std__vector_CropRatio_(ratio.count) + for __item in ratio { + __vector.push_back(__item) + } + return __vector + }(), { () -> bridge.std__optional_CropRatio_ in + if let __unwrappedValue = defaultRatio { + return bridge.create_std__optional_CropRatio_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = freeStyle { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }()) + } + + var language: Language { + @inline(__always) + get { + return self.__language + } + @inline(__always) + set { + self.__language = newValue + } + } + + var presentation: Presentation { + @inline(__always) + get { + return self.__presentation + } + @inline(__always) + set { + self.__presentation = newValue + } + } + + var circle: Bool? { + @inline(__always) + get { + return self.__circle.value + } + @inline(__always) + set { + self.__circle = { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }() + } + } + + var ratio: [CropRatio] { + @inline(__always) + get { + return self.__ratio.map({ __item in __item }) + } + @inline(__always) + set { + self.__ratio = { () -> bridge.std__vector_CropRatio_ in + var __vector = bridge.create_std__vector_CropRatio_(newValue.count) + for __item in newValue { + __vector.push_back(__item) + } + return __vector + }() + } + } + + var defaultRatio: CropRatio? { + @inline(__always) + get { + return { () -> CropRatio? in + if let __unwrapped = self.__defaultRatio.value { + return __unwrapped + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__defaultRatio = { () -> bridge.std__optional_CropRatio_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_CropRatio_(__unwrappedValue) + } else { + return .init() + } + }() + } + } + + var freeStyle: Bool? { + @inline(__always) + get { + return self.__freeStyle.value + } + @inline(__always) + set { + self.__freeStyle = { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }() + } + } +} diff --git a/nitrogen/generated/ios/swift/PickerCropConfig.swift b/nitrogen/generated/ios/swift/PickerCropConfig.swift index 86fd7386..c6944da9 100644 --- a/nitrogen/generated/ios/swift/PickerCropConfig.swift +++ b/nitrogen/generated/ios/swift/PickerCropConfig.swift @@ -18,13 +18,31 @@ public extension PickerCropConfig { /** * Create a new instance of `PickerCropConfig`. */ - init(circle: Bool?) { + init(circle: Bool?, ratio: [CropRatio], defaultRatio: CropRatio?, freeStyle: Bool?) { self.init({ () -> bridge.std__optional_bool_ in if let __unwrappedValue = circle { return bridge.create_std__optional_bool_(__unwrappedValue) } else { return .init() } + }(), { () -> bridge.std__vector_CropRatio_ in + var __vector = bridge.create_std__vector_CropRatio_(ratio.count) + for __item in ratio { + __vector.push_back(__item) + } + return __vector + }(), { () -> bridge.std__optional_CropRatio_ in + if let __unwrappedValue = defaultRatio { + return bridge.create_std__optional_CropRatio_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = freeStyle { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } }()) } @@ -44,4 +62,61 @@ public extension PickerCropConfig { }() } } + + var ratio: [CropRatio] { + @inline(__always) + get { + return self.__ratio.map({ __item in __item }) + } + @inline(__always) + set { + self.__ratio = { () -> bridge.std__vector_CropRatio_ in + var __vector = bridge.create_std__vector_CropRatio_(newValue.count) + for __item in newValue { + __vector.push_back(__item) + } + return __vector + }() + } + } + + var defaultRatio: CropRatio? { + @inline(__always) + get { + return { () -> CropRatio? in + if let __unwrapped = self.__defaultRatio.value { + return __unwrapped + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__defaultRatio = { () -> bridge.std__optional_CropRatio_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_CropRatio_(__unwrappedValue) + } else { + return .init() + } + }() + } + } + + var freeStyle: Bool? { + @inline(__always) + get { + return self.__freeStyle.value + } + @inline(__always) + set { + self.__freeStyle = { () -> bridge.std__optional_bool_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_bool_(__unwrappedValue) + } else { + return .init() + } + }() + } + } } diff --git a/nitrogen/generated/shared/c++/CropRatio.hpp b/nitrogen/generated/shared/c++/CropRatio.hpp new file mode 100644 index 00000000..6d185566 --- /dev/null +++ b/nitrogen/generated/shared/c++/CropRatio.hpp @@ -0,0 +1,77 @@ +/// +/// CropRatio.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 + +namespace margelo::nitro::multipleimagepicker { + + /** + * A struct which can be represented as a JavaScript object (CropRatio). + */ + struct CropRatio { + public: + std::optional title SWIFT_PRIVATE; + double width SWIFT_PRIVATE; + double height SWIFT_PRIVATE; + + public: + explicit CropRatio(std::optional title, double width, double height): title(title), width(width), height(height) {} + }; + +} // namespace margelo::nitro::multipleimagepicker + +namespace margelo::nitro { + + using namespace margelo::nitro::multipleimagepicker; + + // C++ CropRatio <> JS CropRatio (object) + template <> + struct JSIConverter { + static inline CropRatio fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return CropRatio( + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "title")), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "width")), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "height")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const CropRatio& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "title", JSIConverter>::toJSI(runtime, arg.title)); + obj.setProperty(runtime, "width", JSIConverter::toJSI(runtime, arg.width)); + obj.setProperty(runtime, "height", JSIConverter::toJSI(runtime, arg.height)); + 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, "title"))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "width"))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "height"))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/CropResult.hpp b/nitrogen/generated/shared/c++/CropResult.hpp new file mode 100644 index 00000000..128373ad --- /dev/null +++ b/nitrogen/generated/shared/c++/CropResult.hpp @@ -0,0 +1,76 @@ +/// +/// CropResult.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 + +namespace margelo::nitro::multipleimagepicker { + + /** + * A struct which can be represented as a JavaScript object (CropResult). + */ + struct CropResult { + public: + std::string path SWIFT_PRIVATE; + double width SWIFT_PRIVATE; + double height SWIFT_PRIVATE; + + public: + explicit CropResult(std::string path, double width, double height): path(path), width(width), height(height) {} + }; + +} // namespace margelo::nitro::multipleimagepicker + +namespace margelo::nitro { + + using namespace margelo::nitro::multipleimagepicker; + + // C++ CropResult <> JS CropResult (object) + template <> + struct JSIConverter { + static inline CropResult fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return CropResult( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "path")), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "width")), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "height")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const CropResult& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "path", JSIConverter::toJSI(runtime, arg.path)); + obj.setProperty(runtime, "width", JSIConverter::toJSI(runtime, arg.width)); + obj.setProperty(runtime, "height", JSIConverter::toJSI(runtime, arg.height)); + 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, "path"))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "width"))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "height"))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp index 81e210e0..cf4ed3a1 100644 --- a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp @@ -15,6 +15,7 @@ namespace margelo::nitro::multipleimagepicker { // load custom methods/properties registerHybrids(this, [](Prototype& prototype) { prototype.registerHybridMethod("openPicker", &HybridMultipleImagePickerSpec::openPicker); + prototype.registerHybridMethod("openCrop", &HybridMultipleImagePickerSpec::openCrop); }); } diff --git a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp index 42e5e8d2..436dabfe 100644 --- a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp @@ -17,11 +17,18 @@ namespace margelo::nitro::multipleimagepicker { struct NitroConfig; } // Forward declaration of `Result` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct Result; } +// Forward declaration of `NitroCropConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } +// Forward declaration of `CropResult` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropResult; } #include "NitroConfig.hpp" #include #include #include "Result.hpp" +#include +#include "NitroCropConfig.hpp" +#include "CropResult.hpp" namespace margelo::nitro::multipleimagepicker { @@ -55,6 +62,7 @@ namespace margelo::nitro::multipleimagepicker { public: // Methods virtual void openPicker(const NitroConfig& config, const std::function& /* result */)>& resolved, const std::function& rejected) = 0; + virtual void openCrop(const std::string& image, const NitroCropConfig& config, const std::function& resolved, const std::function& rejected) = 0; protected: // Hybrid Setup diff --git a/nitrogen/generated/shared/c++/NitroCropConfig.hpp b/nitrogen/generated/shared/c++/NitroCropConfig.hpp new file mode 100644 index 00000000..24f9ef1d --- /dev/null +++ b/nitrogen/generated/shared/c++/NitroCropConfig.hpp @@ -0,0 +1,97 @@ +/// +/// NitroCropConfig.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 + +// Forward declaration of `Language` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { enum class Language; } +// Forward declaration of `Presentation` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { enum class Presentation; } +// Forward declaration of `CropRatio` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropRatio; } + +#include "Language.hpp" +#include "Presentation.hpp" +#include +#include +#include "CropRatio.hpp" + +namespace margelo::nitro::multipleimagepicker { + + /** + * A struct which can be represented as a JavaScript object (NitroCropConfig). + */ + struct NitroCropConfig { + public: + Language language SWIFT_PRIVATE; + Presentation presentation SWIFT_PRIVATE; + std::optional circle SWIFT_PRIVATE; + std::vector ratio SWIFT_PRIVATE; + std::optional defaultRatio SWIFT_PRIVATE; + std::optional freeStyle SWIFT_PRIVATE; + + public: + explicit NitroCropConfig(Language language, Presentation presentation, std::optional circle, std::vector ratio, std::optional defaultRatio, std::optional freeStyle): language(language), presentation(presentation), circle(circle), ratio(ratio), defaultRatio(defaultRatio), freeStyle(freeStyle) {} + }; + +} // namespace margelo::nitro::multipleimagepicker + +namespace margelo::nitro { + + using namespace margelo::nitro::multipleimagepicker; + + // C++ NitroCropConfig <> JS NitroCropConfig (object) + template <> + struct JSIConverter { + static inline NitroCropConfig fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return NitroCropConfig( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "language")), + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "presentation")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "circle")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "ratio")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "defaultRatio")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "freeStyle")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const NitroCropConfig& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "language", JSIConverter::toJSI(runtime, arg.language)); + obj.setProperty(runtime, "presentation", JSIConverter::toJSI(runtime, arg.presentation)); + obj.setProperty(runtime, "circle", JSIConverter>::toJSI(runtime, arg.circle)); + obj.setProperty(runtime, "ratio", JSIConverter>::toJSI(runtime, arg.ratio)); + obj.setProperty(runtime, "defaultRatio", JSIConverter>::toJSI(runtime, arg.defaultRatio)); + obj.setProperty(runtime, "freeStyle", JSIConverter>::toJSI(runtime, arg.freeStyle)); + 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, "language"))) return false; + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "presentation"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "circle"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "ratio"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "defaultRatio"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "freeStyle"))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/PickerCropConfig.hpp b/nitrogen/generated/shared/c++/PickerCropConfig.hpp index 57db38b0..101f14c8 100644 --- a/nitrogen/generated/shared/c++/PickerCropConfig.hpp +++ b/nitrogen/generated/shared/c++/PickerCropConfig.hpp @@ -18,9 +18,12 @@ #error NitroModules cannot be found! Are you sure you installed NitroModules properly? #endif - +// Forward declaration of `CropRatio` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct CropRatio; } #include +#include +#include "CropRatio.hpp" namespace margelo::nitro::multipleimagepicker { @@ -30,9 +33,12 @@ namespace margelo::nitro::multipleimagepicker { struct PickerCropConfig { public: std::optional circle SWIFT_PRIVATE; + std::vector ratio SWIFT_PRIVATE; + std::optional defaultRatio SWIFT_PRIVATE; + std::optional freeStyle SWIFT_PRIVATE; public: - explicit PickerCropConfig(std::optional circle): circle(circle) {} + explicit PickerCropConfig(std::optional circle, std::vector ratio, std::optional defaultRatio, std::optional freeStyle): circle(circle), ratio(ratio), defaultRatio(defaultRatio), freeStyle(freeStyle) {} }; } // namespace margelo::nitro::multipleimagepicker @@ -47,12 +53,18 @@ namespace margelo::nitro { static inline PickerCropConfig fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { jsi::Object obj = arg.asObject(runtime); return PickerCropConfig( - JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "circle")) + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "circle")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "ratio")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "defaultRatio")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "freeStyle")) ); } static inline jsi::Value toJSI(jsi::Runtime& runtime, const PickerCropConfig& arg) { jsi::Object obj(runtime); obj.setProperty(runtime, "circle", JSIConverter>::toJSI(runtime, arg.circle)); + obj.setProperty(runtime, "ratio", JSIConverter>::toJSI(runtime, arg.ratio)); + obj.setProperty(runtime, "defaultRatio", JSIConverter>::toJSI(runtime, arg.defaultRatio)); + obj.setProperty(runtime, "freeStyle", JSIConverter>::toJSI(runtime, arg.freeStyle)); return obj; } static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { @@ -61,6 +73,9 @@ namespace margelo::nitro { } jsi::Object obj = value.getObject(runtime); if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "circle"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "ratio"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "defaultRatio"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "freeStyle"))) return false; return true; } }; diff --git a/package.json b/package.json index dc87fe1a..22b766eb 100644 --- a/package.json +++ b/package.json @@ -62,12 +62,12 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", - "nitro-codegen": "0.18.1", + "nitro-codegen": "0.18.2", "prettier": "^3.3.3", "react": "^18.3.1", "react-native": "^0.75.2", "react-native-builder-bob": "^0.30.0", - "react-native-nitro-modules": "0.18.1", + "react-native-nitro-modules": "0.18.2", "typescript": "^5.5.4" }, "peerDependencies": { diff --git a/src/index.ts b/src/index.ts index 913d6a50..bd3acf50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,16 @@ import { type MultipleImagePicker } from './specs/MultipleImagePicker.nitro' import { processColor, Appearance } from 'react-native' -import { Result, Config, NitroConfig } from './types' +import { + Result, + Config, + NitroConfig, + CropResult, + CropConfig, + NitroCropConfig, + CropRatio, +} from './types' +import { CropError } from './types/error' const Picker = NitroModules.createHybridObject( 'MultipleImagePicker' @@ -34,6 +43,8 @@ export async function openPicker( config.language = 'system' } + if (config.crop) config.crop.ratio = config.crop.ratio ?? [] + return Picker.openPicker( config, (result: Result[]) => { @@ -46,8 +57,35 @@ export async function openPicker( }) } +export async function openCropper( + image: string, + config?: CropConfig +): Promise { + return new Promise((resolved, rejected) => { + const cropConfig = { + presentation: 'fullScreenModal', + language: 'system', + ratio: [], + ...config, + } as NitroCropConfig + + Picker.openCrop( + image, + cropConfig, + (result: CropResult) => { + resolved(result) + }, + (error: CropError) => { + rejected(error) + } + ) + }) +} + const DEFAULT_COUNT = 20 +export const DEFAULT_RATIO: CropRatio[] = [] + export const defaultOptions: Config = { maxSelect: DEFAULT_COUNT, maxVideo: DEFAULT_COUNT, diff --git a/src/specs/MultipleImagePicker.nitro.ts b/src/specs/MultipleImagePicker.nitro.ts index 1e40fcdd..267e7123 100644 --- a/src/specs/MultipleImagePicker.nitro.ts +++ b/src/specs/MultipleImagePicker.nitro.ts @@ -1,5 +1,5 @@ import { type HybridObject } from 'react-native-nitro-modules' -import { NitroConfig, Result } from '../types' +import { CropResult, NitroConfig, NitroCropConfig, Result } from '../types' export interface MultipleImagePicker extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { @@ -8,4 +8,11 @@ export interface MultipleImagePicker resolved: (result: Result[]) => void, rejected: (reject: number) => void ): void + + openCrop( + image: string, + config: NitroCropConfig, + resolved: (result: CropResult) => void, + rejected: (reject: number) => void + ): void } diff --git a/src/types/config.ts b/src/types/config.ts index 2716e5b4..94d54791 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -26,6 +26,8 @@ export type SelectMode = 'single' | 'multiple' export type MediaType = 'video' | 'image' | 'all' +export type CropRatio = { title?: string; width: number; height: number } + /** * Configuration for image cropping * @interface PickerCropConfig @@ -33,6 +35,24 @@ export type MediaType = 'video' | 'image' | 'all' export type PickerCropConfig = { /** Enable circular crop mask */ circle?: boolean + + ratio: CropRatio[] + + /** + * Default ratio to be selected when opening the crop interface. + * If not specified, the first ratio in the list will be selected. + * + * @platform ios, android + * + * @example + * ```ts + * // Custom ratio without title + * defaultRatio: { width: 4, height: 3 } + */ + defaultRatio?: CropRatio + + /** Enable free style cropping */ + freeStyle?: boolean } /** @@ -313,6 +333,7 @@ export interface Config | 'language' | 'theme' | 'backgroundDark' + | 'crop' > { /** * Type of media to be displayed @@ -421,4 +442,89 @@ export interface Config * ``` */ backgroundDark?: ColorValue + + /** + * Configuration for image cropping + * @interface PickerCropConfig + */ + crop?: Omit & { + /** + * Array of aspect ratios for image cropping. The ratios will be inserted after the default ratios (Original and Square). + * Android: Maximum: 4 items + * + * @platform ios, Android + * + * @property {Array} ratio - Array of custom aspect ratios + * @property {string} [ratio[].title] - Optional display title for the ratio (e.g., "16:9"). If not provided, will use "width/height" + * @property {number} ratio[].width - Width value for aspect ratio + * @property {number} ratio[].height - Height value for aspect ratio + * + * @example + * ```ts + * ratio: [ + * { title: "Instagram", width: 1, height: 1 }, + * { title: "Twitter", width: 16, height: 9 }, + * { width: 12, height: 11 } + * ] + * ``` + */ + ratio?: CropRatio[] + } +} + +export interface NitroCropConfig extends PickerCropConfig { + /** + * Interface language + * @type {Language} + */ + language: Language + + presentation: Presentation +} + +export interface CropConfig + extends Omit { + /** + * Language options for the picker. + * + * @platform ios + * + * @description + * - 'system': 🌐 System default + * - 'zh-Hans': 🇨🇳 Simplified Chinese + * - 'zh-Hant': 🇹🇼 Traditional Chinese + * - 'ja': 🇯🇵 Japanese + * - 'ko': 🇰🇷 Korean + * - 'en': 🇬🇧 English + * - 'th': 🇹🇭 Thai + * - 'id': 🇮🇩 Indonesian + * - 'vi': 🇻🇳 Vietnamese (My Country) + * - 'ru': 🇷🇺 Russian + * - 'de': 🇩🇪 German + * - 'fr': 🇫🇷 French + * - 'ar': 🇸🇦 Arabic + */ + language?: Language + + /** + * Array of aspect ratios for image cropping. The ratios will be inserted after the default ratios (Original and Square). + * Android: Maximum: 4 items + * + * @platform ios, android + * + * @property {Array} ratio - Array of custom aspect ratios + * @property {string} [ratio[].title] - Optional display title for the ratio (e.g., "16:9"). If not provided, will use "width/height" + * @property {number} ratio[].width - Width value for aspect ratio + * @property {number} ratio[].height - Height value for aspect ratio + * + * @example + * ```ts + * ratio: [ + * { title: "Instagram", width: 1, height: 1 }, + * { title: "Twitter", width: 16, height: 9 }, + * { width: 12, height: 11 } + * ] + * ``` + */ + ratio?: CropRatio[] } diff --git a/src/types/error.ts b/src/types/error.ts index b5d9b2d1..02e570c3 100644 --- a/src/types/error.ts +++ b/src/types/error.ts @@ -1,3 +1,7 @@ export enum MultipleImagePickerError { CANCELLED = 0, } + +export enum CropError { + INVALID_IMAGE = 0, +} diff --git a/src/types/result.ts b/src/types/result.ts index d96e32fc..d8aa8bcd 100644 --- a/src/types/result.ts +++ b/src/types/result.ts @@ -17,3 +17,9 @@ export interface Result { thumbnail?: string crop?: boolean } + +export interface CropResult { + path: string + width: number + height: number +} diff --git a/yarn.lock b/yarn.lock index 118fbed5..57245af8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4557,13 +4557,13 @@ neo-async@^2.5.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nitro-codegen@0.18.1: - version "0.18.1" - resolved "https://registry.yarnpkg.com/nitro-codegen/-/nitro-codegen-0.18.1.tgz#5f04829fda54028b5832f35abc432e577530b7c1" - integrity sha512-gDOHIIFFY89Ibo/Q8Dlzx4Rk9fCaGnby4Er5Dh1xV4J5hMqTfqo2VjG+RxScdUTYy/SKOc0UsB2faQybs5+GDw== +nitro-codegen@0.18.2: + version "0.18.2" + resolved "https://registry.yarnpkg.com/nitro-codegen/-/nitro-codegen-0.18.2.tgz#cf2087729260e910e2ef582de6dc3c6ac172ab70" + integrity sha512-SMPf7dkb9/kZpXvIRdQojgqqmIVy7sa6DmxhC7kqJyDoXWteTM3b6NMJ4z+Ir4ON5MxbUWLqjRvfjQpQC1tPIw== dependencies: chalk "^5.3.0" - react-native-nitro-modules "^0.18.1" + react-native-nitro-modules "^0.18.2" ts-morph "^24.0.0" yargs "^17.7.2" zod "^3.23.8" @@ -5074,10 +5074,10 @@ react-native-builder-bob@^0.30.0: which "^2.0.2" yargs "^17.5.1" -react-native-nitro-modules@0.18.1, react-native-nitro-modules@^0.18.1: - version "0.18.1" - resolved "https://registry.yarnpkg.com/react-native-nitro-modules/-/react-native-nitro-modules-0.18.1.tgz#2d0f695b5a5a6f1be87da158868c3ea984be5989" - integrity sha512-F1PA92N8Qv/0I3gKnUFU/eP2C16TSSWwuWuUJnVXX4pCrZztP6BHSvRAZj9WpwxytoKICjwgeVk8K//kvZDZAg== +react-native-nitro-modules@0.18.2, react-native-nitro-modules@^0.18.2: + version "0.18.2" + resolved "https://registry.yarnpkg.com/react-native-nitro-modules/-/react-native-nitro-modules-0.18.2.tgz#f1172f90ecaded0e4b3306f6e7be4b54a24b310e" + integrity sha512-eHsq1cRfm/Bz1Nq7KctTqxAqhzVSNo0WGX281xARZh+vOq8633Qxn1NHRZ5/Rno2Bla6HOXlUW6RoW0wKM/7kg== react-native@^0.75.2: version "0.75.4"